blob: 71cfaec6e9dcabe9a303b33bcb9a40cb04fe512f [file] [log] [blame]
Alex Kiernanf73a7df2018-05-29 15:30:53 +00001// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright (C) 2016 The Android Open Source Project
4 */
5
6#include <common.h>
Simon Glass288b29e2019-11-14 12:57:43 -07007#include <command.h>
Simon Glassc7694dd2019-08-01 09:46:46 -06008#include <env.h>
Alex Kiernanf73a7df2018-05-29 15:30:53 +00009#include <fastboot.h>
10#include <fastboot-internal.h>
11#include <fb_mmc.h>
12#include <fb_nand.h>
13#include <part.h>
14#include <stdlib.h>
15
16/**
17 * image_size - final fastboot image size
18 */
19static u32 image_size;
20
21/**
22 * fastboot_bytes_received - number of bytes received in the current download
23 */
24static u32 fastboot_bytes_received;
25
26/**
27 * fastboot_bytes_expected - number of bytes expected in the current download
28 */
29static u32 fastboot_bytes_expected;
30
31static void okay(char *, char *);
32static void getvar(char *, char *);
33static void download(char *, char *);
Alex Kiernanf73a7df2018-05-29 15:30:53 +000034static void flash(char *, char *);
35static void erase(char *, char *);
Alex Kiernanf73a7df2018-05-29 15:30:53 +000036static void reboot_bootloader(char *, char *);
Roman Kovalivskyi2b2a7712020-07-28 23:35:33 +030037static void reboot_fastbootd(char *, char *);
38static void reboot_recovery(char *, char *);
Alex Kiernan3845b902018-05-29 15:30:54 +000039static void oem_format(char *, char *);
Patrick Delaunayb2f6b972021-01-27 14:46:48 +010040static void oem_partconf(char *, char *);
Patrick Delaunay0c0394b2021-01-27 14:46:49 +010041static void oem_bootbus(char *, char *);
Heiko Schocherbc820d52021-02-10 09:29:03 +010042static void run_ucmd(char *, char *);
43static void run_acmd(char *, char *);
Heiko Schocherbc820d52021-02-10 09:29:03 +010044
Alex Kiernanf73a7df2018-05-29 15:30:53 +000045static const struct {
46 const char *command;
47 void (*dispatch)(char *cmd_parameter, char *response);
48} commands[FASTBOOT_COMMAND_COUNT] = {
49 [FASTBOOT_COMMAND_GETVAR] = {
50 .command = "getvar",
51 .dispatch = getvar
52 },
53 [FASTBOOT_COMMAND_DOWNLOAD] = {
54 .command = "download",
55 .dispatch = download
56 },
Alex Kiernanf73a7df2018-05-29 15:30:53 +000057 [FASTBOOT_COMMAND_FLASH] = {
58 .command = "flash",
Patrick Delaunayd0379902022-12-15 10:15:50 +010059 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_FLASH, (flash), (NULL))
Alex Kiernanf73a7df2018-05-29 15:30:53 +000060 },
61 [FASTBOOT_COMMAND_ERASE] = {
62 .command = "erase",
Patrick Delaunayd0379902022-12-15 10:15:50 +010063 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_FLASH, (erase), (NULL))
Alex Kiernanf73a7df2018-05-29 15:30:53 +000064 },
Alex Kiernanf73a7df2018-05-29 15:30:53 +000065 [FASTBOOT_COMMAND_BOOT] = {
66 .command = "boot",
67 .dispatch = okay
68 },
69 [FASTBOOT_COMMAND_CONTINUE] = {
70 .command = "continue",
71 .dispatch = okay
72 },
73 [FASTBOOT_COMMAND_REBOOT] = {
74 .command = "reboot",
75 .dispatch = okay
76 },
77 [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] = {
78 .command = "reboot-bootloader",
79 .dispatch = reboot_bootloader
80 },
Roman Kovalivskyi2b2a7712020-07-28 23:35:33 +030081 [FASTBOOT_COMMAND_REBOOT_FASTBOOTD] = {
82 .command = "reboot-fastboot",
83 .dispatch = reboot_fastbootd
84 },
85 [FASTBOOT_COMMAND_REBOOT_RECOVERY] = {
86 .command = "reboot-recovery",
87 .dispatch = reboot_recovery
88 },
Alex Kiernanf73a7df2018-05-29 15:30:53 +000089 [FASTBOOT_COMMAND_SET_ACTIVE] = {
90 .command = "set_active",
91 .dispatch = okay
92 },
Alex Kiernan3845b902018-05-29 15:30:54 +000093 [FASTBOOT_COMMAND_OEM_FORMAT] = {
94 .command = "oem format",
Patrick Delaunayd0379902022-12-15 10:15:50 +010095 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT, (oem_format), (NULL))
Alex Kiernan3845b902018-05-29 15:30:54 +000096 },
Patrick Delaunayb2f6b972021-01-27 14:46:48 +010097 [FASTBOOT_COMMAND_OEM_PARTCONF] = {
98 .command = "oem partconf",
Patrick Delaunayd0379902022-12-15 10:15:50 +010099 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_PARTCONF, (oem_partconf), (NULL))
Patrick Delaunayb2f6b972021-01-27 14:46:48 +0100100 },
Patrick Delaunay0c0394b2021-01-27 14:46:49 +0100101 [FASTBOOT_COMMAND_OEM_BOOTBUS] = {
102 .command = "oem bootbus",
Patrick Delaunayd0379902022-12-15 10:15:50 +0100103 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_BOOTBUS, (oem_bootbus), (NULL))
Patrick Delaunay0c0394b2021-01-27 14:46:49 +0100104 },
Sean Andersonf3d914c2022-12-16 13:20:16 -0500105 [FASTBOOT_COMMAND_OEM_RUN] = {
106 .command = "oem run",
107 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL))
108 },
Heiko Schocherbc820d52021-02-10 09:29:03 +0100109 [FASTBOOT_COMMAND_UCMD] = {
110 .command = "UCmd",
Patrick Delaunayd0379902022-12-15 10:15:50 +0100111 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
Heiko Schocherbc820d52021-02-10 09:29:03 +0100112 },
113 [FASTBOOT_COMMAND_ACMD] = {
114 .command = "ACmd",
Patrick Delaunayd0379902022-12-15 10:15:50 +0100115 .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_acmd), (NULL))
Heiko Schocherbc820d52021-02-10 09:29:03 +0100116 },
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000117};
118
119/**
120 * fastboot_handle_command - Handle fastboot command
121 *
122 * @cmd_string: Pointer to command string
123 * @response: Pointer to fastboot response buffer
124 *
125 * Return: Executed command, or -1 if not recognized
126 */
127int fastboot_handle_command(char *cmd_string, char *response)
128{
129 int i;
130 char *cmd_parameter;
131
132 cmd_parameter = cmd_string;
133 strsep(&cmd_parameter, ":");
134
135 for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) {
136 if (!strcmp(commands[i].command, cmd_string)) {
137 if (commands[i].dispatch) {
138 commands[i].dispatch(cmd_parameter,
139 response);
140 return i;
141 } else {
Patrick Delaunayd0379902022-12-15 10:15:50 +0100142 pr_err("command %s not supported.\n", cmd_string);
143 fastboot_fail("Unsupported command", response);
144 return -1;
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000145 }
146 }
147 }
148
149 pr_err("command %s not recognized.\n", cmd_string);
150 fastboot_fail("unrecognized command", response);
151 return -1;
152}
153
154/**
155 * okay() - Send bare OKAY response
156 *
157 * @cmd_parameter: Pointer to command parameter
158 * @response: Pointer to fastboot response buffer
159 *
160 * Send a bare OKAY fastboot response. This is used where the command is
161 * valid, but all the work is done after the response has been sent (e.g.
162 * boot, reboot etc.)
163 */
164static void okay(char *cmd_parameter, char *response)
165{
166 fastboot_okay(NULL, response);
167}
168
169/**
170 * getvar() - Read a config/version variable
171 *
172 * @cmd_parameter: Pointer to command parameter
173 * @response: Pointer to fastboot response buffer
174 */
175static void getvar(char *cmd_parameter, char *response)
176{
177 fastboot_getvar(cmd_parameter, response);
178}
179
180/**
181 * fastboot_download() - Start a download transfer from the client
182 *
183 * @cmd_parameter: Pointer to command parameter
184 * @response: Pointer to fastboot response buffer
185 */
186static void download(char *cmd_parameter, char *response)
187{
188 char *tmp;
189
190 if (!cmd_parameter) {
191 fastboot_fail("Expected command parameter", response);
192 return;
193 }
194 fastboot_bytes_received = 0;
Simon Glass7e5f4602021-07-24 09:03:29 -0600195 fastboot_bytes_expected = hextoul(cmd_parameter, &tmp);
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000196 if (fastboot_bytes_expected == 0) {
197 fastboot_fail("Expected nonzero image size", response);
198 return;
199 }
200 /*
201 * Nothing to download yet. Response is of the form:
202 * [DATA|FAIL]$cmd_parameter
203 *
204 * where cmd_parameter is an 8 digit hexadecimal number
205 */
206 if (fastboot_bytes_expected > fastboot_buf_size) {
207 fastboot_fail(cmd_parameter, response);
208 } else {
209 printf("Starting download of %d bytes\n",
210 fastboot_bytes_expected);
211 fastboot_response("DATA", response, "%s", cmd_parameter);
212 }
213}
214
215/**
216 * fastboot_data_remaining() - return bytes remaining in current transfer
217 *
218 * Return: Number of bytes left in the current download
219 */
220u32 fastboot_data_remaining(void)
221{
222 return fastboot_bytes_expected - fastboot_bytes_received;
223}
224
225/**
226 * fastboot_data_download() - Copy image data to fastboot_buf_addr.
227 *
228 * @fastboot_data: Pointer to received fastboot data
229 * @fastboot_data_len: Length of received fastboot data
230 * @response: Pointer to fastboot response buffer
231 *
232 * Copies image data from fastboot_data to fastboot_buf_addr. Writes to
233 * response. fastboot_bytes_received is updated to indicate the number
234 * of bytes that have been transferred.
235 *
236 * On completion sets image_size and ${filesize} to the total size of the
237 * downloaded image.
238 */
239void fastboot_data_download(const void *fastboot_data,
240 unsigned int fastboot_data_len,
241 char *response)
242{
243#define BYTES_PER_DOT 0x20000
244 u32 pre_dot_num, now_dot_num;
245
246 if (fastboot_data_len == 0 ||
247 (fastboot_bytes_received + fastboot_data_len) >
248 fastboot_bytes_expected) {
249 fastboot_fail("Received invalid data length",
250 response);
251 return;
252 }
253 /* Download data to fastboot_buf_addr */
254 memcpy(fastboot_buf_addr + fastboot_bytes_received,
255 fastboot_data, fastboot_data_len);
256
257 pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
258 fastboot_bytes_received += fastboot_data_len;
259 now_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
260
261 if (pre_dot_num != now_dot_num) {
262 putc('.');
263 if (!(now_dot_num % 74))
264 putc('\n');
265 }
266 *response = '\0';
267}
268
269/**
270 * fastboot_data_complete() - Mark current transfer complete
271 *
272 * @response: Pointer to fastboot response buffer
273 *
274 * Set image_size and ${filesize} to the total size of the downloaded image.
275 */
276void fastboot_data_complete(char *response)
277{
278 /* Download complete. Respond with "OKAY" */
279 fastboot_okay(NULL, response);
280 printf("\ndownloading of %d bytes finished\n", fastboot_bytes_received);
281 image_size = fastboot_bytes_received;
282 env_set_hex("filesize", image_size);
283 fastboot_bytes_expected = 0;
284 fastboot_bytes_received = 0;
285}
286
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000287/**
288 * flash() - write the downloaded image to the indicated partition.
289 *
290 * @cmd_parameter: Pointer to partition name
291 * @response: Pointer to fastboot response buffer
292 *
293 * Writes the previously downloaded image to the partition indicated by
294 * cmd_parameter. Writes to response.
295 */
Patrick Delaunayd0379902022-12-15 10:15:50 +0100296static void __maybe_unused flash(char *cmd_parameter, char *response)
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000297{
Simon Glass7cb10e52023-02-05 17:54:12 -0700298 if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC))
Patrick Delaunayd0379902022-12-15 10:15:50 +0100299 fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr,
300 image_size, response);
301
Simon Glassc6228ed2023-02-05 17:54:13 -0700302 if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND))
Patrick Delaunayd0379902022-12-15 10:15:50 +0100303 fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr,
304 image_size, response);
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000305}
306
307/**
308 * erase() - erase the indicated partition.
309 *
310 * @cmd_parameter: Pointer to partition name
311 * @response: Pointer to fastboot response buffer
312 *
313 * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
314 * to response.
315 */
Patrick Delaunayd0379902022-12-15 10:15:50 +0100316static void __maybe_unused erase(char *cmd_parameter, char *response)
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000317{
Simon Glass7cb10e52023-02-05 17:54:12 -0700318 if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC))
Patrick Delaunayd0379902022-12-15 10:15:50 +0100319 fastboot_mmc_erase(cmd_parameter, response);
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000320
Simon Glassc6228ed2023-02-05 17:54:13 -0700321 if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND))
Patrick Delaunayd0379902022-12-15 10:15:50 +0100322 fastboot_nand_erase(cmd_parameter, response);
323}
324
Heiko Schocherbc820d52021-02-10 09:29:03 +0100325/**
326 * run_ucmd() - Execute the UCmd command
327 *
328 * @cmd_parameter: Pointer to command parameter
329 * @response: Pointer to fastboot response buffer
330 */
Patrick Delaunayd0379902022-12-15 10:15:50 +0100331static void __maybe_unused run_ucmd(char *cmd_parameter, char *response)
Heiko Schocherbc820d52021-02-10 09:29:03 +0100332{
333 if (!cmd_parameter) {
334 pr_err("missing slot suffix\n");
335 fastboot_fail("missing command", response);
336 return;
337 }
338
339 if (run_command(cmd_parameter, 0))
340 fastboot_fail("", response);
341 else
342 fastboot_okay(NULL, response);
343}
344
345static char g_a_cmd_buff[64];
346
347void fastboot_acmd_complete(void)
348{
349 run_command(g_a_cmd_buff, 0);
350}
351
352/**
353 * run_acmd() - Execute the ACmd command
354 *
355 * @cmd_parameter: Pointer to command parameter
356 * @response: Pointer to fastboot response buffer
357 */
Patrick Delaunayd0379902022-12-15 10:15:50 +0100358static void __maybe_unused run_acmd(char *cmd_parameter, char *response)
Heiko Schocherbc820d52021-02-10 09:29:03 +0100359{
360 if (!cmd_parameter) {
361 pr_err("missing slot suffix\n");
362 fastboot_fail("missing command", response);
363 return;
364 }
365
366 if (strlen(cmd_parameter) > sizeof(g_a_cmd_buff)) {
367 pr_err("too long command\n");
368 fastboot_fail("too long command", response);
369 return;
370 }
371
372 strcpy(g_a_cmd_buff, cmd_parameter);
373 fastboot_okay(NULL, response);
374}
Heiko Schocherbc820d52021-02-10 09:29:03 +0100375
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000376/**
377 * reboot_bootloader() - Sets reboot bootloader flag.
378 *
379 * @cmd_parameter: Pointer to command parameter
380 * @response: Pointer to fastboot response buffer
381 */
382static void reboot_bootloader(char *cmd_parameter, char *response)
383{
Roman Kovalivskyi851737a2020-07-28 23:35:32 +0300384 if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_BOOTLOADER))
Alex Kiernanf73a7df2018-05-29 15:30:53 +0000385 fastboot_fail("Cannot set reboot flag", response);
386 else
387 fastboot_okay(NULL, response);
388}
Alex Kiernan3845b902018-05-29 15:30:54 +0000389
Roman Kovalivskyi2b2a7712020-07-28 23:35:33 +0300390/**
391 * reboot_fastbootd() - Sets reboot fastboot flag.
392 *
393 * @cmd_parameter: Pointer to command parameter
394 * @response: Pointer to fastboot response buffer
395 */
396static void reboot_fastbootd(char *cmd_parameter, char *response)
397{
398 if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_FASTBOOTD))
399 fastboot_fail("Cannot set fastboot flag", response);
400 else
401 fastboot_okay(NULL, response);
402}
403
404/**
405 * reboot_recovery() - Sets reboot recovery flag.
406 *
407 * @cmd_parameter: Pointer to command parameter
408 * @response: Pointer to fastboot response buffer
409 */
410static void reboot_recovery(char *cmd_parameter, char *response)
411{
412 if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_RECOVERY))
413 fastboot_fail("Cannot set recovery flag", response);
414 else
415 fastboot_okay(NULL, response);
416}
417
Alex Kiernan3845b902018-05-29 15:30:54 +0000418/**
419 * oem_format() - Execute the OEM format command
420 *
421 * @cmd_parameter: Pointer to command parameter
422 * @response: Pointer to fastboot response buffer
423 */
Patrick Delaunayd0379902022-12-15 10:15:50 +0100424static void __maybe_unused oem_format(char *cmd_parameter, char *response)
Alex Kiernan3845b902018-05-29 15:30:54 +0000425{
426 char cmdbuf[32];
Patrick Delaunayd0379902022-12-15 10:15:50 +0100427 const int mmc_dev = config_opt_enabled(CONFIG_FASTBOOT_FLASH_MMC,
428 CONFIG_FASTBOOT_FLASH_MMC_DEV, -1);
Alex Kiernan3845b902018-05-29 15:30:54 +0000429
430 if (!env_get("partitions")) {
431 fastboot_fail("partitions not set", response);
432 } else {
Patrick Delaunayd0379902022-12-15 10:15:50 +0100433 sprintf(cmdbuf, "gpt write mmc %x $partitions", mmc_dev);
Alex Kiernan3845b902018-05-29 15:30:54 +0000434 if (run_command(cmdbuf, 0))
435 fastboot_fail("", response);
436 else
437 fastboot_okay(NULL, response);
438 }
439}
Patrick Delaunayb2f6b972021-01-27 14:46:48 +0100440
Patrick Delaunayb2f6b972021-01-27 14:46:48 +0100441/**
442 * oem_partconf() - Execute the OEM partconf command
443 *
444 * @cmd_parameter: Pointer to command parameter
445 * @response: Pointer to fastboot response buffer
446 */
Patrick Delaunayd0379902022-12-15 10:15:50 +0100447static void __maybe_unused oem_partconf(char *cmd_parameter, char *response)
Patrick Delaunayb2f6b972021-01-27 14:46:48 +0100448{
449 char cmdbuf[32];
Patrick Delaunayd0379902022-12-15 10:15:50 +0100450 const int mmc_dev = config_opt_enabled(CONFIG_FASTBOOT_FLASH_MMC,
451 CONFIG_FASTBOOT_FLASH_MMC_DEV, -1);
Patrick Delaunayb2f6b972021-01-27 14:46:48 +0100452
453 if (!cmd_parameter) {
454 fastboot_fail("Expected command parameter", response);
455 return;
456 }
457
458 /* execute 'mmc partconfg' command with cmd_parameter arguments*/
Patrick Delaunayd0379902022-12-15 10:15:50 +0100459 snprintf(cmdbuf, sizeof(cmdbuf), "mmc partconf %x %s 0", mmc_dev, cmd_parameter);
Patrick Delaunayb2f6b972021-01-27 14:46:48 +0100460 printf("Execute: %s\n", cmdbuf);
461 if (run_command(cmdbuf, 0))
462 fastboot_fail("Cannot set oem partconf", response);
463 else
464 fastboot_okay(NULL, response);
465}
Patrick Delaunay0c0394b2021-01-27 14:46:49 +0100466
Patrick Delaunay0c0394b2021-01-27 14:46:49 +0100467/**
468 * oem_bootbus() - Execute the OEM bootbus command
469 *
470 * @cmd_parameter: Pointer to command parameter
471 * @response: Pointer to fastboot response buffer
472 */
Patrick Delaunayd0379902022-12-15 10:15:50 +0100473static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response)
Patrick Delaunay0c0394b2021-01-27 14:46:49 +0100474{
475 char cmdbuf[32];
Patrick Delaunayd0379902022-12-15 10:15:50 +0100476 const int mmc_dev = config_opt_enabled(CONFIG_FASTBOOT_FLASH_MMC,
477 CONFIG_FASTBOOT_FLASH_MMC_DEV, -1);
Patrick Delaunay0c0394b2021-01-27 14:46:49 +0100478
479 if (!cmd_parameter) {
480 fastboot_fail("Expected command parameter", response);
481 return;
482 }
483
484 /* execute 'mmc bootbus' command with cmd_parameter arguments*/
Patrick Delaunayd0379902022-12-15 10:15:50 +0100485 snprintf(cmdbuf, sizeof(cmdbuf), "mmc bootbus %x %s", mmc_dev, cmd_parameter);
Patrick Delaunay0c0394b2021-01-27 14:46:49 +0100486 printf("Execute: %s\n", cmdbuf);
487 if (run_command(cmdbuf, 0))
488 fastboot_fail("Cannot set oem bootbus", response);
489 else
490 fastboot_okay(NULL, response);
491}