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