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