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