]>
Commit | Line | Data |
---|---|---|
f73a7df9 AK |
1 | // SPDX-License-Identifier: BSD-2-Clause |
2 | /* | |
3 | * Copyright (C) 2016 The Android Open Source Project | |
4 | */ | |
5 | ||
6 | #include <common.h> | |
288b29e4 | 7 | #include <command.h> |
85fcd69d | 8 | #include <console.h> |
c7694dd4 | 9 | #include <env.h> |
f73a7df9 AK |
10 | #include <fastboot.h> |
11 | #include <fastboot-internal.h> | |
12 | #include <fb_mmc.h> | |
13 | #include <fb_nand.h> | |
14 | #include <part.h> | |
15 | #include <stdlib.h> | |
1e94b46f | 16 | #include <linux/printk.h> |
f73a7df9 AK |
17 | |
18 | /** | |
19 | * image_size - final fastboot image size | |
20 | */ | |
21 | static u32 image_size; | |
22 | ||
23 | /** | |
24 | * fastboot_bytes_received - number of bytes received in the current download | |
25 | */ | |
26 | static u32 fastboot_bytes_received; | |
27 | ||
28 | /** | |
29 | * fastboot_bytes_expected - number of bytes expected in the current download | |
30 | */ | |
31 | static u32 fastboot_bytes_expected; | |
32 | ||
33 | static void okay(char *, char *); | |
34 | static void getvar(char *, char *); | |
35 | static void download(char *, char *); | |
f73a7df9 AK |
36 | static void flash(char *, char *); |
37 | static void erase(char *, char *); | |
f73a7df9 | 38 | static void reboot_bootloader(char *, char *); |
2b2a771b RK |
39 | static void reboot_fastbootd(char *, char *); |
40 | static void reboot_recovery(char *, char *); | |
3845b906 | 41 | static void oem_format(char *, char *); |
b2f6b97b | 42 | static void oem_partconf(char *, char *); |
0c0394b5 | 43 | static void oem_bootbus(char *, char *); |
bc820d5b HS |
44 | static void run_ucmd(char *, char *); |
45 | static void run_acmd(char *, char *); | |
bc820d5b | 46 | |
f73a7df9 AK |
47 | static const struct { |
48 | const char *command; | |
49 | void (*dispatch)(char *cmd_parameter, char *response); | |
50 | } commands[FASTBOOT_COMMAND_COUNT] = { | |
51 | [FASTBOOT_COMMAND_GETVAR] = { | |
52 | .command = "getvar", | |
53 | .dispatch = getvar | |
54 | }, | |
55 | [FASTBOOT_COMMAND_DOWNLOAD] = { | |
56 | .command = "download", | |
57 | .dispatch = download | |
58 | }, | |
f73a7df9 AK |
59 | [FASTBOOT_COMMAND_FLASH] = { |
60 | .command = "flash", | |
d0379900 | 61 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_FLASH, (flash), (NULL)) |
f73a7df9 AK |
62 | }, |
63 | [FASTBOOT_COMMAND_ERASE] = { | |
64 | .command = "erase", | |
d0379900 | 65 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_FLASH, (erase), (NULL)) |
f73a7df9 | 66 | }, |
f73a7df9 AK |
67 | [FASTBOOT_COMMAND_BOOT] = { |
68 | .command = "boot", | |
69 | .dispatch = okay | |
70 | }, | |
71 | [FASTBOOT_COMMAND_CONTINUE] = { | |
72 | .command = "continue", | |
73 | .dispatch = okay | |
74 | }, | |
75 | [FASTBOOT_COMMAND_REBOOT] = { | |
76 | .command = "reboot", | |
77 | .dispatch = okay | |
78 | }, | |
79 | [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] = { | |
80 | .command = "reboot-bootloader", | |
81 | .dispatch = reboot_bootloader | |
82 | }, | |
2b2a771b RK |
83 | [FASTBOOT_COMMAND_REBOOT_FASTBOOTD] = { |
84 | .command = "reboot-fastboot", | |
85 | .dispatch = reboot_fastbootd | |
86 | }, | |
87 | [FASTBOOT_COMMAND_REBOOT_RECOVERY] = { | |
88 | .command = "reboot-recovery", | |
89 | .dispatch = reboot_recovery | |
90 | }, | |
f73a7df9 AK |
91 | [FASTBOOT_COMMAND_SET_ACTIVE] = { |
92 | .command = "set_active", | |
93 | .dispatch = okay | |
94 | }, | |
3845b906 AK |
95 | [FASTBOOT_COMMAND_OEM_FORMAT] = { |
96 | .command = "oem format", | |
d0379900 | 97 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT, (oem_format), (NULL)) |
3845b906 | 98 | }, |
b2f6b97b PD |
99 | [FASTBOOT_COMMAND_OEM_PARTCONF] = { |
100 | .command = "oem partconf", | |
d0379900 | 101 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_PARTCONF, (oem_partconf), (NULL)) |
b2f6b97b | 102 | }, |
0c0394b5 PD |
103 | [FASTBOOT_COMMAND_OEM_BOOTBUS] = { |
104 | .command = "oem bootbus", | |
d0379900 | 105 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_BOOTBUS, (oem_bootbus), (NULL)) |
0c0394b5 | 106 | }, |
f3d914cf SA |
107 | [FASTBOOT_COMMAND_OEM_RUN] = { |
108 | .command = "oem run", | |
109 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) | |
110 | }, | |
bc820d5b HS |
111 | [FASTBOOT_COMMAND_UCMD] = { |
112 | .command = "UCmd", | |
d0379900 | 113 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL)) |
bc820d5b HS |
114 | }, |
115 | [FASTBOOT_COMMAND_ACMD] = { | |
116 | .command = "ACmd", | |
d0379900 | 117 | .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_acmd), (NULL)) |
bc820d5b | 118 | }, |
f73a7df9 AK |
119 | }; |
120 | ||
121 | /** | |
122 | * fastboot_handle_command - Handle fastboot command | |
123 | * | |
124 | * @cmd_string: Pointer to command string | |
125 | * @response: Pointer to fastboot response buffer | |
126 | * | |
127 | * Return: Executed command, or -1 if not recognized | |
128 | */ | |
129 | int fastboot_handle_command(char *cmd_string, char *response) | |
130 | { | |
131 | int i; | |
132 | char *cmd_parameter; | |
133 | ||
134 | cmd_parameter = cmd_string; | |
135 | strsep(&cmd_parameter, ":"); | |
136 | ||
137 | for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) { | |
138 | if (!strcmp(commands[i].command, cmd_string)) { | |
139 | if (commands[i].dispatch) { | |
140 | commands[i].dispatch(cmd_parameter, | |
141 | response); | |
142 | return i; | |
143 | } else { | |
d0379900 PD |
144 | pr_err("command %s not supported.\n", cmd_string); |
145 | fastboot_fail("Unsupported command", response); | |
146 | return -1; | |
f73a7df9 AK |
147 | } |
148 | } | |
149 | } | |
150 | ||
151 | pr_err("command %s not recognized.\n", cmd_string); | |
152 | fastboot_fail("unrecognized command", response); | |
153 | return -1; | |
154 | } | |
155 | ||
85fcd69d IA |
156 | void fastboot_multiresponse(int cmd, char *response) |
157 | { | |
158 | switch (cmd) { | |
159 | default: | |
160 | fastboot_fail("Unknown multiresponse command", response); | |
161 | break; | |
162 | } | |
163 | } | |
164 | ||
f73a7df9 AK |
165 | /** |
166 | * okay() - Send bare OKAY response | |
167 | * | |
168 | * @cmd_parameter: Pointer to command parameter | |
169 | * @response: Pointer to fastboot response buffer | |
170 | * | |
171 | * Send a bare OKAY fastboot response. This is used where the command is | |
172 | * valid, but all the work is done after the response has been sent (e.g. | |
173 | * boot, reboot etc.) | |
174 | */ | |
175 | static void okay(char *cmd_parameter, char *response) | |
176 | { | |
177 | fastboot_okay(NULL, response); | |
178 | } | |
179 | ||
180 | /** | |
181 | * getvar() - Read a config/version variable | |
182 | * | |
183 | * @cmd_parameter: Pointer to command parameter | |
184 | * @response: Pointer to fastboot response buffer | |
185 | */ | |
186 | static void getvar(char *cmd_parameter, char *response) | |
187 | { | |
188 | fastboot_getvar(cmd_parameter, response); | |
189 | } | |
190 | ||
191 | /** | |
192 | * fastboot_download() - Start a download transfer from the client | |
193 | * | |
194 | * @cmd_parameter: Pointer to command parameter | |
195 | * @response: Pointer to fastboot response buffer | |
196 | */ | |
197 | static void download(char *cmd_parameter, char *response) | |
198 | { | |
199 | char *tmp; | |
200 | ||
201 | if (!cmd_parameter) { | |
202 | fastboot_fail("Expected command parameter", response); | |
203 | return; | |
204 | } | |
205 | fastboot_bytes_received = 0; | |
7e5f460e | 206 | fastboot_bytes_expected = hextoul(cmd_parameter, &tmp); |
f73a7df9 AK |
207 | if (fastboot_bytes_expected == 0) { |
208 | fastboot_fail("Expected nonzero image size", response); | |
209 | return; | |
210 | } | |
211 | /* | |
212 | * Nothing to download yet. Response is of the form: | |
213 | * [DATA|FAIL]$cmd_parameter | |
214 | * | |
215 | * where cmd_parameter is an 8 digit hexadecimal number | |
216 | */ | |
217 | if (fastboot_bytes_expected > fastboot_buf_size) { | |
218 | fastboot_fail(cmd_parameter, response); | |
219 | } else { | |
220 | printf("Starting download of %d bytes\n", | |
221 | fastboot_bytes_expected); | |
222 | fastboot_response("DATA", response, "%s", cmd_parameter); | |
223 | } | |
224 | } | |
225 | ||
226 | /** | |
227 | * fastboot_data_remaining() - return bytes remaining in current transfer | |
228 | * | |
229 | * Return: Number of bytes left in the current download | |
230 | */ | |
231 | u32 fastboot_data_remaining(void) | |
232 | { | |
233 | return fastboot_bytes_expected - fastboot_bytes_received; | |
234 | } | |
235 | ||
236 | /** | |
237 | * fastboot_data_download() - Copy image data to fastboot_buf_addr. | |
238 | * | |
239 | * @fastboot_data: Pointer to received fastboot data | |
240 | * @fastboot_data_len: Length of received fastboot data | |
241 | * @response: Pointer to fastboot response buffer | |
242 | * | |
243 | * Copies image data from fastboot_data to fastboot_buf_addr. Writes to | |
244 | * response. fastboot_bytes_received is updated to indicate the number | |
245 | * of bytes that have been transferred. | |
246 | * | |
247 | * On completion sets image_size and ${filesize} to the total size of the | |
248 | * downloaded image. | |
249 | */ | |
250 | void fastboot_data_download(const void *fastboot_data, | |
251 | unsigned int fastboot_data_len, | |
252 | char *response) | |
253 | { | |
254 | #define BYTES_PER_DOT 0x20000 | |
255 | u32 pre_dot_num, now_dot_num; | |
256 | ||
257 | if (fastboot_data_len == 0 || | |
258 | (fastboot_bytes_received + fastboot_data_len) > | |
259 | fastboot_bytes_expected) { | |
260 | fastboot_fail("Received invalid data length", | |
261 | response); | |
262 | return; | |
263 | } | |
264 | /* Download data to fastboot_buf_addr */ | |
265 | memcpy(fastboot_buf_addr + fastboot_bytes_received, | |
266 | fastboot_data, fastboot_data_len); | |
267 | ||
268 | pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT; | |
269 | fastboot_bytes_received += fastboot_data_len; | |
270 | now_dot_num = fastboot_bytes_received / BYTES_PER_DOT; | |
271 | ||
272 | if (pre_dot_num != now_dot_num) { | |
273 | putc('.'); | |
274 | if (!(now_dot_num % 74)) | |
275 | putc('\n'); | |
276 | } | |
277 | *response = '\0'; | |
278 | } | |
279 | ||
280 | /** | |
281 | * fastboot_data_complete() - Mark current transfer complete | |
282 | * | |
283 | * @response: Pointer to fastboot response buffer | |
284 | * | |
285 | * Set image_size and ${filesize} to the total size of the downloaded image. | |
286 | */ | |
287 | void fastboot_data_complete(char *response) | |
288 | { | |
289 | /* Download complete. Respond with "OKAY" */ | |
290 | fastboot_okay(NULL, response); | |
291 | printf("\ndownloading of %d bytes finished\n", fastboot_bytes_received); | |
292 | image_size = fastboot_bytes_received; | |
293 | env_set_hex("filesize", image_size); | |
294 | fastboot_bytes_expected = 0; | |
295 | fastboot_bytes_received = 0; | |
296 | } | |
297 | ||
f73a7df9 AK |
298 | /** |
299 | * flash() - write the downloaded image to the indicated partition. | |
300 | * | |
301 | * @cmd_parameter: Pointer to partition name | |
302 | * @response: Pointer to fastboot response buffer | |
303 | * | |
304 | * Writes the previously downloaded image to the partition indicated by | |
305 | * cmd_parameter. Writes to response. | |
306 | */ | |
d0379900 | 307 | static void __maybe_unused flash(char *cmd_parameter, char *response) |
f73a7df9 | 308 | { |
7cb10e51 | 309 | if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)) |
d0379900 PD |
310 | fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, |
311 | image_size, response); | |
312 | ||
c6228edf | 313 | if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND)) |
d0379900 PD |
314 | fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, |
315 | image_size, response); | |
f73a7df9 AK |
316 | } |
317 | ||
318 | /** | |
319 | * erase() - erase the indicated partition. | |
320 | * | |
321 | * @cmd_parameter: Pointer to partition name | |
322 | * @response: Pointer to fastboot response buffer | |
323 | * | |
324 | * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes | |
325 | * to response. | |
326 | */ | |
d0379900 | 327 | static void __maybe_unused erase(char *cmd_parameter, char *response) |
f73a7df9 | 328 | { |
7cb10e51 | 329 | if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)) |
d0379900 PD |
330 | fastboot_mmc_erase(cmd_parameter, response); |
331 | ||
c6228edf | 332 | if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND)) |
d0379900 | 333 | fastboot_nand_erase(cmd_parameter, response); |
f73a7df9 | 334 | } |
f73a7df9 | 335 | |
bc820d5b HS |
336 | /** |
337 | * run_ucmd() - Execute the UCmd command | |
338 | * | |
339 | * @cmd_parameter: Pointer to command parameter | |
340 | * @response: Pointer to fastboot response buffer | |
341 | */ | |
d0379900 | 342 | static void __maybe_unused run_ucmd(char *cmd_parameter, char *response) |
bc820d5b HS |
343 | { |
344 | if (!cmd_parameter) { | |
345 | pr_err("missing slot suffix\n"); | |
346 | fastboot_fail("missing command", response); | |
347 | return; | |
348 | } | |
349 | ||
350 | if (run_command(cmd_parameter, 0)) | |
351 | fastboot_fail("", response); | |
352 | else | |
353 | fastboot_okay(NULL, response); | |
354 | } | |
355 | ||
356 | static char g_a_cmd_buff[64]; | |
357 | ||
358 | void fastboot_acmd_complete(void) | |
359 | { | |
360 | run_command(g_a_cmd_buff, 0); | |
361 | } | |
362 | ||
363 | /** | |
364 | * run_acmd() - Execute the ACmd command | |
365 | * | |
366 | * @cmd_parameter: Pointer to command parameter | |
367 | * @response: Pointer to fastboot response buffer | |
368 | */ | |
d0379900 | 369 | static void __maybe_unused run_acmd(char *cmd_parameter, char *response) |
bc820d5b HS |
370 | { |
371 | if (!cmd_parameter) { | |
372 | pr_err("missing slot suffix\n"); | |
373 | fastboot_fail("missing command", response); | |
374 | return; | |
375 | } | |
376 | ||
377 | if (strlen(cmd_parameter) > sizeof(g_a_cmd_buff)) { | |
378 | pr_err("too long command\n"); | |
379 | fastboot_fail("too long command", response); | |
380 | return; | |
381 | } | |
382 | ||
383 | strcpy(g_a_cmd_buff, cmd_parameter); | |
384 | fastboot_okay(NULL, response); | |
385 | } | |
bc820d5b | 386 | |
f73a7df9 AK |
387 | /** |
388 | * reboot_bootloader() - Sets reboot bootloader flag. | |
389 | * | |
390 | * @cmd_parameter: Pointer to command parameter | |
391 | * @response: Pointer to fastboot response buffer | |
392 | */ | |
393 | static void reboot_bootloader(char *cmd_parameter, char *response) | |
394 | { | |
851737ab | 395 | if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_BOOTLOADER)) |
f73a7df9 AK |
396 | fastboot_fail("Cannot set reboot flag", response); |
397 | else | |
398 | fastboot_okay(NULL, response); | |
399 | } | |
3845b906 | 400 | |
2b2a771b RK |
401 | /** |
402 | * reboot_fastbootd() - Sets reboot fastboot flag. | |
403 | * | |
404 | * @cmd_parameter: Pointer to command parameter | |
405 | * @response: Pointer to fastboot response buffer | |
406 | */ | |
407 | static void reboot_fastbootd(char *cmd_parameter, char *response) | |
408 | { | |
409 | if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_FASTBOOTD)) | |
410 | fastboot_fail("Cannot set fastboot flag", response); | |
411 | else | |
412 | fastboot_okay(NULL, response); | |
413 | } | |
414 | ||
415 | /** | |
416 | * reboot_recovery() - Sets reboot recovery flag. | |
417 | * | |
418 | * @cmd_parameter: Pointer to command parameter | |
419 | * @response: Pointer to fastboot response buffer | |
420 | */ | |
421 | static void reboot_recovery(char *cmd_parameter, char *response) | |
422 | { | |
423 | if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_RECOVERY)) | |
424 | fastboot_fail("Cannot set recovery flag", response); | |
425 | else | |
426 | fastboot_okay(NULL, response); | |
427 | } | |
428 | ||
3845b906 AK |
429 | /** |
430 | * oem_format() - Execute the OEM format command | |
431 | * | |
432 | * @cmd_parameter: Pointer to command parameter | |
433 | * @response: Pointer to fastboot response buffer | |
434 | */ | |
d0379900 | 435 | static void __maybe_unused oem_format(char *cmd_parameter, char *response) |
3845b906 AK |
436 | { |
437 | char cmdbuf[32]; | |
d0379900 PD |
438 | const int mmc_dev = config_opt_enabled(CONFIG_FASTBOOT_FLASH_MMC, |
439 | CONFIG_FASTBOOT_FLASH_MMC_DEV, -1); | |
3845b906 AK |
440 | |
441 | if (!env_get("partitions")) { | |
442 | fastboot_fail("partitions not set", response); | |
443 | } else { | |
d0379900 | 444 | sprintf(cmdbuf, "gpt write mmc %x $partitions", mmc_dev); |
3845b906 AK |
445 | if (run_command(cmdbuf, 0)) |
446 | fastboot_fail("", response); | |
447 | else | |
448 | fastboot_okay(NULL, response); | |
449 | } | |
450 | } | |
b2f6b97b | 451 | |
b2f6b97b PD |
452 | /** |
453 | * oem_partconf() - Execute the OEM partconf command | |
454 | * | |
455 | * @cmd_parameter: Pointer to command parameter | |
456 | * @response: Pointer to fastboot response buffer | |
457 | */ | |
d0379900 | 458 | static void __maybe_unused oem_partconf(char *cmd_parameter, char *response) |
b2f6b97b PD |
459 | { |
460 | char cmdbuf[32]; | |
d0379900 PD |
461 | const int mmc_dev = config_opt_enabled(CONFIG_FASTBOOT_FLASH_MMC, |
462 | CONFIG_FASTBOOT_FLASH_MMC_DEV, -1); | |
b2f6b97b PD |
463 | |
464 | if (!cmd_parameter) { | |
465 | fastboot_fail("Expected command parameter", response); | |
466 | return; | |
467 | } | |
468 | ||
469 | /* execute 'mmc partconfg' command with cmd_parameter arguments*/ | |
d0379900 | 470 | snprintf(cmdbuf, sizeof(cmdbuf), "mmc partconf %x %s 0", mmc_dev, cmd_parameter); |
b2f6b97b PD |
471 | printf("Execute: %s\n", cmdbuf); |
472 | if (run_command(cmdbuf, 0)) | |
473 | fastboot_fail("Cannot set oem partconf", response); | |
474 | else | |
475 | fastboot_okay(NULL, response); | |
476 | } | |
0c0394b5 | 477 | |
0c0394b5 PD |
478 | /** |
479 | * oem_bootbus() - Execute the OEM bootbus command | |
480 | * | |
481 | * @cmd_parameter: Pointer to command parameter | |
482 | * @response: Pointer to fastboot response buffer | |
483 | */ | |
d0379900 | 484 | static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response) |
0c0394b5 PD |
485 | { |
486 | char cmdbuf[32]; | |
d0379900 PD |
487 | const int mmc_dev = config_opt_enabled(CONFIG_FASTBOOT_FLASH_MMC, |
488 | CONFIG_FASTBOOT_FLASH_MMC_DEV, -1); | |
0c0394b5 PD |
489 | |
490 | if (!cmd_parameter) { | |
491 | fastboot_fail("Expected command parameter", response); | |
492 | return; | |
493 | } | |
494 | ||
495 | /* execute 'mmc bootbus' command with cmd_parameter arguments*/ | |
d0379900 | 496 | snprintf(cmdbuf, sizeof(cmdbuf), "mmc bootbus %x %s", mmc_dev, cmd_parameter); |
0c0394b5 PD |
497 | printf("Execute: %s\n", cmdbuf); |
498 | if (run_command(cmdbuf, 0)) | |
499 | fastboot_fail("Cannot set oem bootbus", response); | |
500 | else | |
501 | fastboot_okay(NULL, response); | |
502 | } |