xref: /openbmc/u-boot/drivers/fastboot/fb_command.c (revision caa2a2e5ab44d87faf51fafc780ecc985e0c05d6)
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