xref: /rk3399_rockchip-uboot/net/fastboot.c (revision aba554cc34bdfc92eb13ef852a73d6bd82329f37)
1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6 
7 #include <common.h>
8 #include <fastboot.h>
9 #include <fb_mmc.h>
10 #include <net.h>
11 #include <net/fastboot.h>
12 #include <stdlib.h>
13 #include <version.h>
14 
15 /* Fastboot port # defined in spec */
16 #define WELL_KNOWN_PORT 5554
17 
18 enum {
19 	FASTBOOT_ERROR = 0,
20 	FASTBOOT_QUERY = 1,
21 	FASTBOOT_INIT = 2,
22 	FASTBOOT_FASTBOOT = 3,
23 };
24 
25 struct __attribute__((packed)) fastboot_header {
26 	uchar id;
27 	uchar flags;
28 	unsigned short seq;
29 };
30 
31 #define PACKET_SIZE 1024
32 #define FASTBOOT_HEADER_SIZE sizeof(struct fastboot_header)
33 #define DATA_SIZE (PACKET_SIZE - FASTBOOT_HEADER_SIZE)
34 #define FASTBOOT_VERSION "0.4"
35 
36 /* Sequence number sent for every packet */
37 static unsigned short fb_sequence_number = 1;
38 static const unsigned short fb_packet_size = PACKET_SIZE;
39 static const unsigned short fb_udp_version = 1;
40 
41 /* Keep track of last packet for resubmission */
42 static uchar last_packet[PACKET_SIZE];
43 static unsigned int last_packet_len = 0;
44 
45 /* Parsed from first fastboot command packet */
46 static char *cmd_string = NULL;
47 static char *cmd_parameter = NULL;
48 
49 /* Fastboot download parameters */
50 static unsigned int bytes_received = 0;
51 static unsigned int bytes_expected = 0;
52 static unsigned int image_size = 0;
53 
54 static struct in_addr fastboot_remote_ip;
55 /* The UDP port at their end */
56 static int fastboot_remote_port;
57 /* The UDP port at our end */
58 static int fastboot_our_port;
59 
60 static void fb_getvar(char*);
61 static void fb_download(char*, unsigned int, char*);
62 static void fb_flash(char*);
63 static void fb_erase(char*);
64 static void fb_continue(char*);
65 static void fb_reboot(char*);
66 static void boot_downloaded_image(void);
67 static void cleanup_command_data(void);
68 static void write_fb_response(const char*, const char*, char*);
69 
70 void fastboot_send_info(const char *msg)
71 {
72 	uchar *packet;
73 	uchar *packet_base;
74 	int len = 0;
75 	char response[FASTBOOT_RESPONSE_LEN] = {0};
76 
77 	struct fastboot_header fb_response_header =
78 	{
79 		.id = FASTBOOT_FASTBOOT,
80 		.flags = 0,
81 		.seq = htons(fb_sequence_number)
82 	};
83 	++fb_sequence_number;
84 	packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
85 	packet_base = packet;
86 
87 	/* Write headers */
88 	memcpy(packet, &fb_response_header, sizeof(fb_response_header));
89 	packet += sizeof(fb_response_header);
90 	/* Write response */
91 	write_fb_response("INFO", msg, response);
92 	memcpy(packet, response, strlen(response));
93 	packet += strlen(response);
94 
95 	len = packet-packet_base;
96 
97 	/* Save packet for retransmitting */
98 	last_packet_len = len;
99 	memcpy(last_packet, packet_base, last_packet_len);
100 
101 	net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
102 			    fastboot_remote_port, fastboot_our_port, len);
103 }
104 
105 /**
106  * Constructs and sends a packet in response to received fastboot packet
107  *
108  * @param fb_header            Header for response packet
109  * @param fastboot_data        Pointer to received fastboot data
110  * @param fastboot_data_len    Length of received fastboot data
111  * @param retransmit           Nonzero if sending last sent packet
112  */
113 static void fastboot_send(struct fastboot_header fb_header, char *fastboot_data,
114 		unsigned int fastboot_data_len, uchar retransmit)
115 {
116 	uchar *packet;
117 	uchar *packet_base;
118 	int len = 0;
119 	const char *error_msg = "An error occurred.";
120 	short tmp;
121 	struct fastboot_header fb_response_header = fb_header;
122 	char response[FASTBOOT_RESPONSE_LEN] = {0};
123 	/*
124 	 *	We will always be sending some sort of packet, so
125 	 *	cobble together the packet headers now.
126 	 */
127 	packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
128 	packet_base = packet;
129 
130 	/* Resend last packet */
131 	if (retransmit) {
132 		memcpy(packet, last_packet, last_packet_len);
133 		net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
134 				    fastboot_remote_port, fastboot_our_port, last_packet_len);
135 		return;
136 	}
137 
138 	fb_response_header.seq = htons(fb_response_header.seq);
139 	memcpy(packet, &fb_response_header, sizeof(fb_response_header));
140 	packet += sizeof(fb_response_header);
141 
142 	switch (fb_header.id) {
143 	case FASTBOOT_QUERY:
144 		tmp = htons(fb_sequence_number);
145 		memcpy(packet, &tmp, sizeof(tmp));
146 		packet += sizeof(tmp);
147 		break;
148 	case FASTBOOT_INIT:
149 		tmp = htons(fb_udp_version);
150 		memcpy(packet, &tmp, sizeof(tmp));
151 		packet += sizeof(tmp);
152 		tmp = htons(fb_packet_size);
153 		memcpy(packet, &tmp, sizeof(tmp));
154 		packet += sizeof(tmp);
155 		break;
156 	case FASTBOOT_ERROR:
157 		memcpy(packet, error_msg, strlen(error_msg));
158 		packet += strlen(error_msg);
159 		break;
160 	case FASTBOOT_FASTBOOT:
161 		if (cmd_string == NULL) {
162 			/* Parse command and send ack */
163 			cmd_parameter = fastboot_data;
164 			cmd_string = strsep(&cmd_parameter, ":");
165 			cmd_string = strdup(cmd_string);
166 			if (cmd_parameter) {
167 				cmd_parameter = strdup(cmd_parameter);
168 			}
169 		} else if (!strcmp("getvar", cmd_string)) {
170 			fb_getvar(response);
171 		} else if (!strcmp("download", cmd_string)) {
172 			fb_download(fastboot_data, fastboot_data_len, response);
173 		} else if (!strcmp("flash", cmd_string)) {
174 			fb_flash(response);
175 		} else if (!strcmp("erase", cmd_string)) {
176 			fb_erase(response);
177 		} else if (!strcmp("boot", cmd_string)) {
178 			write_fb_response("OKAY", "", response);
179 		} else if (!strcmp("continue", cmd_string)) {
180 			fb_continue(response);
181 		} else if (!strncmp("reboot", cmd_string, 6)) {
182 			fb_reboot(response);
183 		} else {
184 			error("command %s not implemented.\n", cmd_string);
185 			write_fb_response("FAIL", "unrecognized command", response);
186 		}
187 		/* Sent some INFO packets, need to update sequence number in header */
188 		if (fb_header.seq != fb_sequence_number) {
189 			fb_response_header.seq = htons(fb_sequence_number);
190 			memcpy(packet_base, &fb_response_header, sizeof(fb_response_header));
191 		}
192 		/* Write response to packet */
193 		memcpy(packet, response, strlen(response));
194 		packet += strlen(response);
195 		break;
196 	default:
197 		error("ID %d not implemented.\n", fb_header.id);
198 		return;
199 	}
200 
201 	len = packet-packet_base;
202 
203 	/* Save packet for retransmitting */
204 	last_packet_len = len;
205 	memcpy(last_packet, packet_base, last_packet_len);
206 
207 	net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
208 			    fastboot_remote_port, fastboot_our_port, len);
209 
210 	/* Continue boot process after sending response */
211 	if (!strncmp("OKAY", response, 4)) {
212 		if (!strcmp("boot", cmd_string)) {
213 			boot_downloaded_image();
214 		} else if (!strcmp("continue", cmd_string)) {
215 			run_command(getenv("bootcmd"), CMD_FLAG_ENV);
216 		} else if (!strncmp("reboot", cmd_string, 6)) {
217 			/* Matches reboot or reboot-bootloader */
218 			do_reset(NULL, 0, 0, NULL);
219 		}
220 	}
221 
222 	/* OKAY and FAIL indicate command is complete */
223 	if (!strncmp("OKAY", response, 4) ||
224 			!strncmp("FAIL", response, 4)) {
225 		cleanup_command_data();
226 	}
227 }
228 
229 /**
230  * Writes ascii string specified by cmd_parameter to response.
231  *
232  * @param repsonse    Pointer to fastboot response buffer
233  */
234 static void fb_getvar(char *response)
235 {
236 
237 	if (cmd_parameter == NULL) {
238 		write_fb_response("FAIL", "missing var", response);
239 	} else if (!strcmp("version", cmd_parameter)) {
240 		write_fb_response("OKAY", FASTBOOT_VERSION, response);
241 	} else if (!strcmp("bootloader-version", cmd_parameter)) {
242 		write_fb_response("OKAY", U_BOOT_VERSION, response);
243 	} else if (!strcmp("downloadsize", cmd_parameter) ||
244 		!strcmp("max-download-size", cmd_parameter)) {
245 		char buf_size_str[12];
246 		sprintf(buf_size_str, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE);
247 		write_fb_response("OKAY", buf_size_str, response);
248 	} else if (!strcmp("serialno", cmd_parameter)) {
249 		const char *tmp = getenv("serial#");
250 		if (tmp) {
251 			write_fb_response("OKAY", tmp, response);
252 		} else {
253 			write_fb_response("FAIL", "Value not set", response);
254 		}
255 	} else {
256 		printf("WARNING: unknown variable: %s\n", cmd_parameter);
257 		write_fb_response("FAIL", "Variable not implemented", response);
258 	}
259 }
260 
261 /**
262  * Copies image data from fastboot_data to CONFIG_FASTBOOT_BUF_ADDR.
263  * Writes to response.
264  *
265  * @param fastboot_data        Pointer to received fastboot data
266  * @param fastboot_data_len    Length of received fastboot data
267  * @param repsonse             Pointer to fastboot response buffer
268  */
269 static void fb_download(char *fastboot_data, unsigned int fastboot_data_len,
270 		char *response)
271 {
272 	char *tmp;
273 
274 	if (bytes_expected == 0) {
275 		if (cmd_parameter == NULL) {
276 			write_fb_response("FAIL", "Expected command parameter", response);
277 			return;
278 		}
279 		bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16);
280 		if (bytes_expected == 0) {
281 			write_fb_response("FAIL", "Expected nonzero image size", response);
282 			return;
283 		}
284 	}
285 	if (fastboot_data_len == 0 && bytes_received == 0) {
286 		/* Nothing to download yet. Response is of the form:
287 		 * [DATA|FAIL]$cmd_parameter
288 		 *
289 		 * where cmd_parameter is an 8 digit hexadecimal number
290 		 */
291 		if (bytes_expected > CONFIG_FASTBOOT_BUF_SIZE) {
292 			write_fb_response("FAIL", cmd_parameter, response);
293 		} else {
294 			write_fb_response("DATA", cmd_parameter, response);
295 		}
296 	} else if (fastboot_data_len == 0 && (bytes_received >= bytes_expected)) {
297 		/* Download complete. Respond with "OKAY" */
298 		write_fb_response("OKAY", "", response);
299 		image_size = bytes_received;
300 		bytes_expected = bytes_received = 0;
301 	} else {
302 		if (fastboot_data_len == 0 ||
303 				(bytes_received + fastboot_data_len) > bytes_expected) {
304 			write_fb_response("FAIL", "Received invalid data length", response);
305 			return;
306 		}
307 		/* Download data to CONFIG_FASTBOOT_BUF_ADDR */
308 		memcpy((void*)CONFIG_FASTBOOT_BUF_ADDR + bytes_received, fastboot_data,
309 				fastboot_data_len);
310 		bytes_received += fastboot_data_len;
311 	}
312 }
313 
314 /**
315  * Writes the previously downloaded image to the partition indicated by
316  * cmd_parameter. Writes to response.
317  *
318  * @param repsonse    Pointer to fastboot response buffer
319  */
320 static void fb_flash(char *response)
321 {
322 	fb_mmc_flash_write(cmd_parameter, (void*)CONFIG_FASTBOOT_BUF_ADDR,
323 			image_size, response);
324 }
325 
326 /**
327  * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
328  * to response.
329  *
330  * @param repsonse    Pointer to fastboot response buffer
331  */
332 static void fb_erase(char *response)
333 {
334 	fb_mmc_erase(cmd_parameter, response);
335 }
336 
337 /**
338  * Continues normal boot process by running "bootcmd". Writes
339  * to response.
340  *
341  * @param repsonse    Pointer to fastboot response buffer
342  */
343 static void fb_continue(char *response)
344 {
345 	char *bootcmd;
346 	bootcmd = getenv("bootcmd");
347 	if (bootcmd) {
348 		write_fb_response("OKAY", "", response);
349 	} else {
350 		write_fb_response("FAIL", "bootcmd not set", response);
351 	}
352 }
353 
354 /**
355  * Sets reboot bootloader flag if requested. Writes to response.
356  *
357  * @param repsonse    Pointer to fastboot response buffer
358  */
359 static void fb_reboot(char *response)
360 {
361 	write_fb_response("OKAY", "", response);
362 	if (!strcmp("reboot-bootloader", cmd_string)) {
363 		strcpy((char*)CONFIG_FASTBOOT_BUF_ADDR, "reboot-bootloader");
364 	}
365 }
366 
367 /**
368  * Boots into downloaded image.
369  */
370 static void boot_downloaded_image(void)
371 {
372 	char kernel_addr[12];
373 	char *fdt_addr = getenv("fdt_addr_r");
374 	char *bootm_args[] = { "bootm", kernel_addr, "-", fdt_addr, NULL };
375 
376 	sprintf(kernel_addr, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR);
377 
378 	printf("\nBooting kernel at %s with fdt at %s...\n\n\n",
379 			kernel_addr, fdt_addr);
380 	do_bootm(NULL, 0, 4, bootm_args);
381 
382 	/* This only happens if image is faulty so we start over. */
383 	do_reset(NULL, 0, 0, NULL);
384 }
385 
386 /**
387  * Writes a response to response buffer of the form "$tag$reason".
388  *
389  * @param tag         The first part of the response
390  * @param reason      The second part of the response
391  * @param repsonse    Pointer to fastboot response buffer
392  */
393 static void write_fb_response(const char* tag, const char *reason,
394 		char *response)
395 {
396 	strncpy(response, tag, strlen(tag));
397 	strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(tag) - 1);
398 }
399 
400 /**
401  * Frees any resources allocated during current fastboot command.
402  */
403 static void cleanup_command_data(void)
404 {
405 	/* cmd_parameter and cmd_string potentially point to memory allocated by
406 	 * strdup
407 	 */
408 	if (cmd_parameter) {
409 		free(cmd_parameter);
410 	}
411 	if (cmd_string) {
412 		free(cmd_string);
413 	}
414 	cmd_parameter = cmd_string = NULL;
415 }
416 
417 /**
418  * Incoming UDP packet handler.
419  *
420  * @param packet  Pointer to incoming UDP packet
421  * @param dport   Destination UDP port
422  * @param sip     Source IP address
423  * @param sport   Source UDP port
424  * @param len     Packet length
425  */
426 static void fastboot_handler(uchar *packet, unsigned dport, struct in_addr sip,
427 		unsigned sport, unsigned len)
428 {
429 	struct fastboot_header fb_header;
430 	char fastboot_data[DATA_SIZE] = {0};
431 	unsigned int fastboot_data_len = 0;
432 
433 	if (dport != fastboot_our_port) {
434 		return;
435 	}
436 
437 	fastboot_remote_ip = sip;
438 	fastboot_remote_port = sport;
439 
440 	if (len < FASTBOOT_HEADER_SIZE || len > PACKET_SIZE) {
441 		return;
442 	}
443 	memcpy(&fb_header, packet, sizeof(fb_header));
444 	fb_header.flags = 0;
445 	fb_header.seq = ntohs(fb_header.seq);
446 	packet += sizeof(fb_header);
447 	len -= sizeof(fb_header);
448 
449 	switch (fb_header.id) {
450 	case FASTBOOT_QUERY:
451 		fastboot_send(fb_header, fastboot_data, 0, 0);
452 		break;
453 	case FASTBOOT_INIT:
454 	case FASTBOOT_FASTBOOT:
455 		fastboot_data_len = len;
456 		if (len > 0) {
457 			memcpy(fastboot_data, packet, len);
458 		}
459 		if (fb_header.seq == fb_sequence_number) {
460 			fastboot_send(fb_header, fastboot_data, fastboot_data_len, 0);
461 			fb_sequence_number++;
462 		} else if (fb_header.seq == fb_sequence_number - 1) {
463 			/* Retransmit last sent packet */
464 			fastboot_send(fb_header, fastboot_data, fastboot_data_len, 1);
465 		}
466 		break;
467 	default:
468 		error("ID %d not implemented.\n", fb_header.id);
469 		fb_header.id = FASTBOOT_ERROR;
470 		fastboot_send(fb_header, fastboot_data, 0, 0);
471 		break;
472 	}
473 }
474 
475 void fastboot_start_server(void)
476 {
477 	printf("Using %s device\n", eth_get_name());
478 	printf("Listening for fastboot command on %pI4\n", &net_ip);
479 
480 	fastboot_our_port = WELL_KNOWN_PORT;
481 
482 	net_set_udp_handler(fastboot_handler);
483 
484 	/* zero out server ether in case the server ip has changed */
485 	memset(net_server_ethaddr, 0, 6);
486 }
487