1f73a7df9SAlex Kiernan // SPDX-License-Identifier: BSD-2-Clause
2f73a7df9SAlex Kiernan /*
3f73a7df9SAlex Kiernan * Copyright (C) 2016 The Android Open Source Project
4f73a7df9SAlex Kiernan */
5f73a7df9SAlex Kiernan
6f73a7df9SAlex Kiernan #include <common.h>
7f73a7df9SAlex Kiernan #include <fastboot.h>
8f73a7df9SAlex Kiernan #include <fastboot-internal.h>
9f73a7df9SAlex Kiernan #include <fb_mmc.h>
10f73a7df9SAlex Kiernan #include <fb_nand.h>
11f73a7df9SAlex Kiernan #include <part.h>
12f73a7df9SAlex Kiernan #include <stdlib.h>
13f73a7df9SAlex Kiernan
14f73a7df9SAlex Kiernan /**
15f73a7df9SAlex Kiernan * image_size - final fastboot image size
16f73a7df9SAlex Kiernan */
17f73a7df9SAlex Kiernan static u32 image_size;
18f73a7df9SAlex Kiernan
19f73a7df9SAlex Kiernan /**
20f73a7df9SAlex Kiernan * fastboot_bytes_received - number of bytes received in the current download
21f73a7df9SAlex Kiernan */
22f73a7df9SAlex Kiernan static u32 fastboot_bytes_received;
23f73a7df9SAlex Kiernan
24f73a7df9SAlex Kiernan /**
25f73a7df9SAlex Kiernan * fastboot_bytes_expected - number of bytes expected in the current download
26f73a7df9SAlex Kiernan */
27f73a7df9SAlex Kiernan static u32 fastboot_bytes_expected;
28f73a7df9SAlex Kiernan
29f73a7df9SAlex Kiernan static void okay(char *, char *);
30f73a7df9SAlex Kiernan static void getvar(char *, char *);
31f73a7df9SAlex Kiernan static void download(char *, char *);
32f73a7df9SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
33f73a7df9SAlex Kiernan static void flash(char *, char *);
34f73a7df9SAlex Kiernan static void erase(char *, char *);
35f73a7df9SAlex Kiernan #endif
36f73a7df9SAlex Kiernan static void reboot_bootloader(char *, char *);
37*3845b906SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
38*3845b906SAlex Kiernan static void oem_format(char *, char *);
39*3845b906SAlex Kiernan #endif
40f73a7df9SAlex Kiernan
41f73a7df9SAlex Kiernan static const struct {
42f73a7df9SAlex Kiernan const char *command;
43f73a7df9SAlex Kiernan void (*dispatch)(char *cmd_parameter, char *response);
44f73a7df9SAlex Kiernan } commands[FASTBOOT_COMMAND_COUNT] = {
45f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_GETVAR] = {
46f73a7df9SAlex Kiernan .command = "getvar",
47f73a7df9SAlex Kiernan .dispatch = getvar
48f73a7df9SAlex Kiernan },
49f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_DOWNLOAD] = {
50f73a7df9SAlex Kiernan .command = "download",
51f73a7df9SAlex Kiernan .dispatch = download
52f73a7df9SAlex Kiernan },
53f73a7df9SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
54f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_FLASH] = {
55f73a7df9SAlex Kiernan .command = "flash",
56f73a7df9SAlex Kiernan .dispatch = flash
57f73a7df9SAlex Kiernan },
58f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_ERASE] = {
59f73a7df9SAlex Kiernan .command = "erase",
60f73a7df9SAlex Kiernan .dispatch = erase
61f73a7df9SAlex Kiernan },
62f73a7df9SAlex Kiernan #endif
63f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_BOOT] = {
64f73a7df9SAlex Kiernan .command = "boot",
65f73a7df9SAlex Kiernan .dispatch = okay
66f73a7df9SAlex Kiernan },
67f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_CONTINUE] = {
68f73a7df9SAlex Kiernan .command = "continue",
69f73a7df9SAlex Kiernan .dispatch = okay
70f73a7df9SAlex Kiernan },
71f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_REBOOT] = {
72f73a7df9SAlex Kiernan .command = "reboot",
73f73a7df9SAlex Kiernan .dispatch = okay
74f73a7df9SAlex Kiernan },
75f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] = {
76f73a7df9SAlex Kiernan .command = "reboot-bootloader",
77f73a7df9SAlex Kiernan .dispatch = reboot_bootloader
78f73a7df9SAlex Kiernan },
79f73a7df9SAlex Kiernan [FASTBOOT_COMMAND_SET_ACTIVE] = {
80f73a7df9SAlex Kiernan .command = "set_active",
81f73a7df9SAlex Kiernan .dispatch = okay
82f73a7df9SAlex Kiernan },
83*3845b906SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
84*3845b906SAlex Kiernan [FASTBOOT_COMMAND_OEM_FORMAT] = {
85*3845b906SAlex Kiernan .command = "oem format",
86*3845b906SAlex Kiernan .dispatch = oem_format,
87*3845b906SAlex Kiernan },
88*3845b906SAlex Kiernan #endif
89f73a7df9SAlex Kiernan };
90f73a7df9SAlex Kiernan
91f73a7df9SAlex Kiernan /**
92f73a7df9SAlex Kiernan * fastboot_handle_command - Handle fastboot command
93f73a7df9SAlex Kiernan *
94f73a7df9SAlex Kiernan * @cmd_string: Pointer to command string
95f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
96f73a7df9SAlex Kiernan *
97f73a7df9SAlex Kiernan * Return: Executed command, or -1 if not recognized
98f73a7df9SAlex Kiernan */
fastboot_handle_command(char * cmd_string,char * response)99f73a7df9SAlex Kiernan int fastboot_handle_command(char *cmd_string, char *response)
100f73a7df9SAlex Kiernan {
101f73a7df9SAlex Kiernan int i;
102f73a7df9SAlex Kiernan char *cmd_parameter;
103f73a7df9SAlex Kiernan
104f73a7df9SAlex Kiernan cmd_parameter = cmd_string;
105f73a7df9SAlex Kiernan strsep(&cmd_parameter, ":");
106f73a7df9SAlex Kiernan
107f73a7df9SAlex Kiernan for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) {
108f73a7df9SAlex Kiernan if (!strcmp(commands[i].command, cmd_string)) {
109f73a7df9SAlex Kiernan if (commands[i].dispatch) {
110f73a7df9SAlex Kiernan commands[i].dispatch(cmd_parameter,
111f73a7df9SAlex Kiernan response);
112f73a7df9SAlex Kiernan return i;
113f73a7df9SAlex Kiernan } else {
114f73a7df9SAlex Kiernan break;
115f73a7df9SAlex Kiernan }
116f73a7df9SAlex Kiernan }
117f73a7df9SAlex Kiernan }
118f73a7df9SAlex Kiernan
119f73a7df9SAlex Kiernan pr_err("command %s not recognized.\n", cmd_string);
120f73a7df9SAlex Kiernan fastboot_fail("unrecognized command", response);
121f73a7df9SAlex Kiernan return -1;
122f73a7df9SAlex Kiernan }
123f73a7df9SAlex Kiernan
124f73a7df9SAlex Kiernan /**
125f73a7df9SAlex Kiernan * okay() - Send bare OKAY response
126f73a7df9SAlex Kiernan *
127f73a7df9SAlex Kiernan * @cmd_parameter: Pointer to command parameter
128f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
129f73a7df9SAlex Kiernan *
130f73a7df9SAlex Kiernan * Send a bare OKAY fastboot response. This is used where the command is
131f73a7df9SAlex Kiernan * valid, but all the work is done after the response has been sent (e.g.
132f73a7df9SAlex Kiernan * boot, reboot etc.)
133f73a7df9SAlex Kiernan */
okay(char * cmd_parameter,char * response)134f73a7df9SAlex Kiernan static void okay(char *cmd_parameter, char *response)
135f73a7df9SAlex Kiernan {
136f73a7df9SAlex Kiernan fastboot_okay(NULL, response);
137f73a7df9SAlex Kiernan }
138f73a7df9SAlex Kiernan
139f73a7df9SAlex Kiernan /**
140f73a7df9SAlex Kiernan * getvar() - Read a config/version variable
141f73a7df9SAlex Kiernan *
142f73a7df9SAlex Kiernan * @cmd_parameter: Pointer to command parameter
143f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
144f73a7df9SAlex Kiernan */
getvar(char * cmd_parameter,char * response)145f73a7df9SAlex Kiernan static void getvar(char *cmd_parameter, char *response)
146f73a7df9SAlex Kiernan {
147f73a7df9SAlex Kiernan fastboot_getvar(cmd_parameter, response);
148f73a7df9SAlex Kiernan }
149f73a7df9SAlex Kiernan
150f73a7df9SAlex Kiernan /**
151f73a7df9SAlex Kiernan * fastboot_download() - Start a download transfer from the client
152f73a7df9SAlex Kiernan *
153f73a7df9SAlex Kiernan * @cmd_parameter: Pointer to command parameter
154f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
155f73a7df9SAlex Kiernan */
download(char * cmd_parameter,char * response)156f73a7df9SAlex Kiernan static void download(char *cmd_parameter, char *response)
157f73a7df9SAlex Kiernan {
158f73a7df9SAlex Kiernan char *tmp;
159f73a7df9SAlex Kiernan
160f73a7df9SAlex Kiernan if (!cmd_parameter) {
161f73a7df9SAlex Kiernan fastboot_fail("Expected command parameter", response);
162f73a7df9SAlex Kiernan return;
163f73a7df9SAlex Kiernan }
164f73a7df9SAlex Kiernan fastboot_bytes_received = 0;
165f73a7df9SAlex Kiernan fastboot_bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16);
166f73a7df9SAlex Kiernan if (fastboot_bytes_expected == 0) {
167f73a7df9SAlex Kiernan fastboot_fail("Expected nonzero image size", response);
168f73a7df9SAlex Kiernan return;
169f73a7df9SAlex Kiernan }
170f73a7df9SAlex Kiernan /*
171f73a7df9SAlex Kiernan * Nothing to download yet. Response is of the form:
172f73a7df9SAlex Kiernan * [DATA|FAIL]$cmd_parameter
173f73a7df9SAlex Kiernan *
174f73a7df9SAlex Kiernan * where cmd_parameter is an 8 digit hexadecimal number
175f73a7df9SAlex Kiernan */
176f73a7df9SAlex Kiernan if (fastboot_bytes_expected > fastboot_buf_size) {
177f73a7df9SAlex Kiernan fastboot_fail(cmd_parameter, response);
178f73a7df9SAlex Kiernan } else {
179f73a7df9SAlex Kiernan printf("Starting download of %d bytes\n",
180f73a7df9SAlex Kiernan fastboot_bytes_expected);
181f73a7df9SAlex Kiernan fastboot_response("DATA", response, "%s", cmd_parameter);
182f73a7df9SAlex Kiernan }
183f73a7df9SAlex Kiernan }
184f73a7df9SAlex Kiernan
185f73a7df9SAlex Kiernan /**
186f73a7df9SAlex Kiernan * fastboot_data_remaining() - return bytes remaining in current transfer
187f73a7df9SAlex Kiernan *
188f73a7df9SAlex Kiernan * Return: Number of bytes left in the current download
189f73a7df9SAlex Kiernan */
fastboot_data_remaining(void)190f73a7df9SAlex Kiernan u32 fastboot_data_remaining(void)
191f73a7df9SAlex Kiernan {
192f73a7df9SAlex Kiernan return fastboot_bytes_expected - fastboot_bytes_received;
193f73a7df9SAlex Kiernan }
194f73a7df9SAlex Kiernan
195f73a7df9SAlex Kiernan /**
196f73a7df9SAlex Kiernan * fastboot_data_download() - Copy image data to fastboot_buf_addr.
197f73a7df9SAlex Kiernan *
198f73a7df9SAlex Kiernan * @fastboot_data: Pointer to received fastboot data
199f73a7df9SAlex Kiernan * @fastboot_data_len: Length of received fastboot data
200f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
201f73a7df9SAlex Kiernan *
202f73a7df9SAlex Kiernan * Copies image data from fastboot_data to fastboot_buf_addr. Writes to
203f73a7df9SAlex Kiernan * response. fastboot_bytes_received is updated to indicate the number
204f73a7df9SAlex Kiernan * of bytes that have been transferred.
205f73a7df9SAlex Kiernan *
206f73a7df9SAlex Kiernan * On completion sets image_size and ${filesize} to the total size of the
207f73a7df9SAlex Kiernan * downloaded image.
208f73a7df9SAlex Kiernan */
fastboot_data_download(const void * fastboot_data,unsigned int fastboot_data_len,char * response)209f73a7df9SAlex Kiernan void fastboot_data_download(const void *fastboot_data,
210f73a7df9SAlex Kiernan unsigned int fastboot_data_len,
211f73a7df9SAlex Kiernan char *response)
212f73a7df9SAlex Kiernan {
213f73a7df9SAlex Kiernan #define BYTES_PER_DOT 0x20000
214f73a7df9SAlex Kiernan u32 pre_dot_num, now_dot_num;
215f73a7df9SAlex Kiernan
216f73a7df9SAlex Kiernan if (fastboot_data_len == 0 ||
217f73a7df9SAlex Kiernan (fastboot_bytes_received + fastboot_data_len) >
218f73a7df9SAlex Kiernan fastboot_bytes_expected) {
219f73a7df9SAlex Kiernan fastboot_fail("Received invalid data length",
220f73a7df9SAlex Kiernan response);
221f73a7df9SAlex Kiernan return;
222f73a7df9SAlex Kiernan }
223f73a7df9SAlex Kiernan /* Download data to fastboot_buf_addr */
224f73a7df9SAlex Kiernan memcpy(fastboot_buf_addr + fastboot_bytes_received,
225f73a7df9SAlex Kiernan fastboot_data, fastboot_data_len);
226f73a7df9SAlex Kiernan
227f73a7df9SAlex Kiernan pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
228f73a7df9SAlex Kiernan fastboot_bytes_received += fastboot_data_len;
229f73a7df9SAlex Kiernan now_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
230f73a7df9SAlex Kiernan
231f73a7df9SAlex Kiernan if (pre_dot_num != now_dot_num) {
232f73a7df9SAlex Kiernan putc('.');
233f73a7df9SAlex Kiernan if (!(now_dot_num % 74))
234f73a7df9SAlex Kiernan putc('\n');
235f73a7df9SAlex Kiernan }
236f73a7df9SAlex Kiernan *response = '\0';
237f73a7df9SAlex Kiernan }
238f73a7df9SAlex Kiernan
239f73a7df9SAlex Kiernan /**
240f73a7df9SAlex Kiernan * fastboot_data_complete() - Mark current transfer complete
241f73a7df9SAlex Kiernan *
242f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
243f73a7df9SAlex Kiernan *
244f73a7df9SAlex Kiernan * Set image_size and ${filesize} to the total size of the downloaded image.
245f73a7df9SAlex Kiernan */
fastboot_data_complete(char * response)246f73a7df9SAlex Kiernan void fastboot_data_complete(char *response)
247f73a7df9SAlex Kiernan {
248f73a7df9SAlex Kiernan /* Download complete. Respond with "OKAY" */
249f73a7df9SAlex Kiernan fastboot_okay(NULL, response);
250f73a7df9SAlex Kiernan printf("\ndownloading of %d bytes finished\n", fastboot_bytes_received);
251f73a7df9SAlex Kiernan image_size = fastboot_bytes_received;
252f73a7df9SAlex Kiernan env_set_hex("filesize", image_size);
253f73a7df9SAlex Kiernan fastboot_bytes_expected = 0;
254f73a7df9SAlex Kiernan fastboot_bytes_received = 0;
255f73a7df9SAlex Kiernan }
256f73a7df9SAlex Kiernan
257f73a7df9SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
258f73a7df9SAlex Kiernan /**
259f73a7df9SAlex Kiernan * flash() - write the downloaded image to the indicated partition.
260f73a7df9SAlex Kiernan *
261f73a7df9SAlex Kiernan * @cmd_parameter: Pointer to partition name
262f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
263f73a7df9SAlex Kiernan *
264f73a7df9SAlex Kiernan * Writes the previously downloaded image to the partition indicated by
265f73a7df9SAlex Kiernan * cmd_parameter. Writes to response.
266f73a7df9SAlex Kiernan */
flash(char * cmd_parameter,char * response)267f73a7df9SAlex Kiernan static void flash(char *cmd_parameter, char *response)
268f73a7df9SAlex Kiernan {
269f73a7df9SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
270f73a7df9SAlex Kiernan fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
271f73a7df9SAlex Kiernan response);
272f73a7df9SAlex Kiernan #endif
273f73a7df9SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
274f73a7df9SAlex Kiernan fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
275f73a7df9SAlex Kiernan response);
276f73a7df9SAlex Kiernan #endif
277f73a7df9SAlex Kiernan }
278f73a7df9SAlex Kiernan
279f73a7df9SAlex Kiernan /**
280f73a7df9SAlex Kiernan * erase() - erase the indicated partition.
281f73a7df9SAlex Kiernan *
282f73a7df9SAlex Kiernan * @cmd_parameter: Pointer to partition name
283f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
284f73a7df9SAlex Kiernan *
285f73a7df9SAlex Kiernan * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
286f73a7df9SAlex Kiernan * to response.
287f73a7df9SAlex Kiernan */
erase(char * cmd_parameter,char * response)288f73a7df9SAlex Kiernan static void erase(char *cmd_parameter, char *response)
289f73a7df9SAlex Kiernan {
290f73a7df9SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
291f73a7df9SAlex Kiernan fastboot_mmc_erase(cmd_parameter, response);
292f73a7df9SAlex Kiernan #endif
293f73a7df9SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
294f73a7df9SAlex Kiernan fastboot_nand_erase(cmd_parameter, response);
295f73a7df9SAlex Kiernan #endif
296f73a7df9SAlex Kiernan }
297f73a7df9SAlex Kiernan #endif
298f73a7df9SAlex Kiernan
299f73a7df9SAlex Kiernan /**
300f73a7df9SAlex Kiernan * reboot_bootloader() - Sets reboot bootloader flag.
301f73a7df9SAlex Kiernan *
302f73a7df9SAlex Kiernan * @cmd_parameter: Pointer to command parameter
303f73a7df9SAlex Kiernan * @response: Pointer to fastboot response buffer
304f73a7df9SAlex Kiernan */
reboot_bootloader(char * cmd_parameter,char * response)305f73a7df9SAlex Kiernan static void reboot_bootloader(char *cmd_parameter, char *response)
306f73a7df9SAlex Kiernan {
307f73a7df9SAlex Kiernan if (fastboot_set_reboot_flag())
308f73a7df9SAlex Kiernan fastboot_fail("Cannot set reboot flag", response);
309f73a7df9SAlex Kiernan else
310f73a7df9SAlex Kiernan fastboot_okay(NULL, response);
311f73a7df9SAlex Kiernan }
312*3845b906SAlex Kiernan
313*3845b906SAlex Kiernan #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
314*3845b906SAlex Kiernan /**
315*3845b906SAlex Kiernan * oem_format() - Execute the OEM format command
316*3845b906SAlex Kiernan *
317*3845b906SAlex Kiernan * @cmd_parameter: Pointer to command parameter
318*3845b906SAlex Kiernan * @response: Pointer to fastboot response buffer
319*3845b906SAlex Kiernan */
oem_format(char * cmd_parameter,char * response)320*3845b906SAlex Kiernan static void oem_format(char *cmd_parameter, char *response)
321*3845b906SAlex Kiernan {
322*3845b906SAlex Kiernan char cmdbuf[32];
323*3845b906SAlex Kiernan
324*3845b906SAlex Kiernan if (!env_get("partitions")) {
325*3845b906SAlex Kiernan fastboot_fail("partitions not set", response);
326*3845b906SAlex Kiernan } else {
327*3845b906SAlex Kiernan sprintf(cmdbuf, "gpt write mmc %x $partitions",
328*3845b906SAlex Kiernan CONFIG_FASTBOOT_FLASH_MMC_DEV);
329*3845b906SAlex Kiernan if (run_command(cmdbuf, 0))
330*3845b906SAlex Kiernan fastboot_fail("", response);
331*3845b906SAlex Kiernan else
332*3845b906SAlex Kiernan fastboot_okay(NULL, response);
333*3845b906SAlex Kiernan }
334*3845b906SAlex Kiernan }
335*3845b906SAlex Kiernan #endif
336