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