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