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