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 static struct in_addr fastboot_remote_ip; 49 /* The UDP port at their end */ 50 static int fastboot_remote_port; 51 /* The UDP port at our end */ 52 static int fastboot_our_port; 53 54 static void fb_getvar(char*); 55 static void cleanup_command_data(void); 56 static void write_fb_response(const char*, const char*, char*); 57 58 /** 59 * Constructs and sends a packet in response to received fastboot packet 60 * 61 * @param fb_header Header for response packet 62 * @param fastboot_data Pointer to received fastboot data 63 * @param fastboot_data_len Length of received fastboot data 64 * @param retransmit Nonzero if sending last sent packet 65 */ 66 static void fastboot_send(struct fastboot_header fb_header, char *fastboot_data, 67 unsigned int fastboot_data_len, uchar retransmit) 68 { 69 uchar *packet; 70 uchar *packet_base; 71 int len = 0; 72 const char *error_msg = "An error occurred."; 73 short tmp; 74 struct fastboot_header fb_response_header = fb_header; 75 char response[FASTBOOT_RESPONSE_LEN] = {0}; 76 /* 77 * We will always be sending some sort of packet, so 78 * cobble together the packet headers now. 79 */ 80 packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; 81 packet_base = packet; 82 83 /* Resend last packet */ 84 if (retransmit) { 85 memcpy(packet, last_packet, last_packet_len); 86 net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, 87 fastboot_remote_port, fastboot_our_port, last_packet_len); 88 return; 89 } 90 91 fb_response_header.seq = htons(fb_response_header.seq); 92 memcpy(packet, &fb_response_header, sizeof(fb_response_header)); 93 packet += sizeof(fb_response_header); 94 95 switch (fb_header.id) { 96 case FASTBOOT_QUERY: 97 tmp = htons(fb_sequence_number); 98 memcpy(packet, &tmp, sizeof(tmp)); 99 packet += sizeof(tmp); 100 break; 101 case FASTBOOT_INIT: 102 tmp = htons(fb_udp_version); 103 memcpy(packet, &tmp, sizeof(tmp)); 104 packet += sizeof(tmp); 105 tmp = htons(fb_packet_size); 106 memcpy(packet, &tmp, sizeof(tmp)); 107 packet += sizeof(tmp); 108 break; 109 case FASTBOOT_ERROR: 110 memcpy(packet, error_msg, strlen(error_msg)); 111 packet += strlen(error_msg); 112 break; 113 case FASTBOOT_FASTBOOT: 114 if (cmd_string == NULL) { 115 /* Parse command and send ack */ 116 cmd_parameter = fastboot_data; 117 cmd_string = strsep(&cmd_parameter, ":"); 118 cmd_string = strdup(cmd_string); 119 if (cmd_parameter) { 120 cmd_parameter = strdup(cmd_parameter); 121 } 122 } else if (!strcmp("getvar", cmd_string)) { 123 fb_getvar(response); 124 } else { 125 error("command %s not implemented.\n", cmd_string); 126 write_fb_response("FAIL", "unrecognized command", response); 127 } 128 /* Write response to packet */ 129 memcpy(packet, response, strlen(response)); 130 packet += strlen(response); 131 break; 132 default: 133 error("ID %d not implemented.\n", fb_header.id); 134 return; 135 } 136 137 len = packet-packet_base; 138 139 /* Save packet for retransmitting */ 140 last_packet_len = len; 141 memcpy(last_packet, packet_base, last_packet_len); 142 143 net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, 144 fastboot_remote_port, fastboot_our_port, len); 145 146 /* OKAY and FAIL indicate command is complete */ 147 if (!strncmp("OKAY", response, 4) || 148 !strncmp("FAIL", response, 4)) { 149 cleanup_command_data(); 150 } 151 } 152 153 /** 154 * Writes ascii string specified by cmd_parameter to response. 155 * 156 * @param repsonse Pointer to fastboot response buffer 157 */ 158 static void fb_getvar(char *response) 159 { 160 161 if (cmd_parameter == NULL) { 162 write_fb_response("FAIL", "missing var", response); 163 } else if (!strcmp("version", cmd_parameter)) { 164 write_fb_response("OKAY", FASTBOOT_VERSION, response); 165 } else if (!strcmp("bootloader-version", cmd_parameter)) { 166 write_fb_response("OKAY", U_BOOT_VERSION, response); 167 } else if (!strcmp("downloadsize", cmd_parameter) || 168 !strcmp("max-download-size", cmd_parameter)) { 169 char buf_size_str[12]; 170 sprintf(buf_size_str, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE); 171 write_fb_response("OKAY", buf_size_str, response); 172 } else if (!strcmp("serialno", cmd_parameter)) { 173 const char *tmp = getenv("serial#"); 174 if (tmp) { 175 write_fb_response("OKAY", tmp, response); 176 } else { 177 write_fb_response("FAIL", "Value not set", response); 178 } 179 } else { 180 printf("WARNING: unknown variable: %s\n", cmd_parameter); 181 write_fb_response("FAIL", "Variable not implemented", response); 182 } 183 } 184 185 /** 186 * Writes a response to response buffer of the form "$tag$reason". 187 * 188 * @param tag The first part of the response 189 * @param reason The second part of the response 190 * @param repsonse Pointer to fastboot response buffer 191 */ 192 static void write_fb_response(const char* tag, const char *reason, 193 char *response) 194 { 195 strncpy(response, tag, strlen(tag)); 196 strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(tag) - 1); 197 } 198 199 /** 200 * Frees any resources allocated during current fastboot command. 201 */ 202 static void cleanup_command_data(void) 203 { 204 /* cmd_parameter and cmd_string potentially point to memory allocated by 205 * strdup 206 */ 207 if (cmd_parameter) { 208 free(cmd_parameter); 209 } 210 if (cmd_string) { 211 free(cmd_string); 212 } 213 cmd_parameter = cmd_string = NULL; 214 } 215 216 /** 217 * Incoming UDP packet handler. 218 * 219 * @param packet Pointer to incoming UDP packet 220 * @param dport Destination UDP port 221 * @param sip Source IP address 222 * @param sport Source UDP port 223 * @param len Packet length 224 */ 225 static void fastboot_handler(uchar *packet, unsigned dport, struct in_addr sip, 226 unsigned sport, unsigned len) 227 { 228 struct fastboot_header fb_header; 229 char fastboot_data[DATA_SIZE] = {0}; 230 unsigned int fastboot_data_len = 0; 231 232 if (dport != fastboot_our_port) { 233 return; 234 } 235 236 fastboot_remote_ip = sip; 237 fastboot_remote_port = sport; 238 239 if (len < FASTBOOT_HEADER_SIZE || len > PACKET_SIZE) { 240 return; 241 } 242 memcpy(&fb_header, packet, sizeof(fb_header)); 243 fb_header.flags = 0; 244 fb_header.seq = ntohs(fb_header.seq); 245 packet += sizeof(fb_header); 246 len -= sizeof(fb_header); 247 248 switch (fb_header.id) { 249 case FASTBOOT_QUERY: 250 fastboot_send(fb_header, fastboot_data, 0, 0); 251 break; 252 case FASTBOOT_INIT: 253 case FASTBOOT_FASTBOOT: 254 fastboot_data_len = len; 255 if (len > 0) { 256 memcpy(fastboot_data, packet, len); 257 } 258 if (fb_header.seq == fb_sequence_number) { 259 fastboot_send(fb_header, fastboot_data, fastboot_data_len, 0); 260 fb_sequence_number++; 261 } else if (fb_header.seq == fb_sequence_number - 1) { 262 /* Retransmit last sent packet */ 263 fastboot_send(fb_header, fastboot_data, fastboot_data_len, 1); 264 } 265 break; 266 default: 267 error("ID %d not implemented.\n", fb_header.id); 268 fb_header.id = FASTBOOT_ERROR; 269 fastboot_send(fb_header, fastboot_data, 0, 0); 270 break; 271 } 272 } 273 274 void fastboot_start_server(void) 275 { 276 printf("Using %s device\n", eth_get_name()); 277 printf("Listening for fastboot command on %pI4\n", &net_ip); 278 279 fastboot_our_port = WELL_KNOWN_PORT; 280 281 net_set_udp_handler(fastboot_handler); 282 283 /* zero out server ether in case the server ip has changed */ 284 memset(net_server_ethaddr, 0, 6); 285 } 286