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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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