uboot/net/fastboot.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-2-Clause
   2/*
   3 * Copyright (C) 2016 The Android Open Source Project
   4 */
   5
   6#include <common.h>
   7#include <command.h>
   8#include <fastboot.h>
   9#include <net.h>
  10#include <net/fastboot.h>
  11
  12/* Fastboot port # defined in spec */
  13#define WELL_KNOWN_PORT 5554
  14
  15enum {
  16        FASTBOOT_ERROR = 0,
  17        FASTBOOT_QUERY = 1,
  18        FASTBOOT_INIT = 2,
  19        FASTBOOT_FASTBOOT = 3,
  20};
  21
  22struct __packed fastboot_header {
  23        uchar id;
  24        uchar flags;
  25        unsigned short seq;
  26};
  27
  28#define PACKET_SIZE 1024
  29#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header))
  30
  31/* Sequence number sent for every packet */
  32static unsigned short sequence_number = 1;
  33static const unsigned short packet_size = PACKET_SIZE;
  34static const unsigned short udp_version = 1;
  35
  36/* Keep track of last packet for resubmission */
  37static uchar last_packet[PACKET_SIZE];
  38static unsigned int last_packet_len;
  39
  40static struct in_addr fastboot_remote_ip;
  41/* The UDP port at their end */
  42static int fastboot_remote_port;
  43/* The UDP port at our end */
  44static int fastboot_our_port;
  45
  46static void boot_downloaded_image(void);
  47
  48#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
  49/**
  50 * fastboot_udp_send_info() - Send an INFO packet during long commands.
  51 *
  52 * @msg: String describing the reason for waiting
  53 */
  54static void fastboot_udp_send_info(const char *msg)
  55{
  56        uchar *packet;
  57        uchar *packet_base;
  58        int len = 0;
  59        char response[FASTBOOT_RESPONSE_LEN] = {0};
  60
  61        struct fastboot_header response_header = {
  62                .id = FASTBOOT_FASTBOOT,
  63                .flags = 0,
  64                .seq = htons(sequence_number)
  65        };
  66        ++sequence_number;
  67        packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
  68        packet_base = packet;
  69
  70        /* Write headers */
  71        memcpy(packet, &response_header, sizeof(response_header));
  72        packet += sizeof(response_header);
  73        /* Write response */
  74        fastboot_response("INFO", response, "%s", msg);
  75        memcpy(packet, response, strlen(response));
  76        packet += strlen(response);
  77
  78        len = packet - packet_base;
  79
  80        /* Save packet for retransmitting */
  81        last_packet_len = len;
  82        memcpy(last_packet, packet_base, last_packet_len);
  83
  84        net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
  85                            fastboot_remote_port, fastboot_our_port, len);
  86}
  87
  88/**
  89 * fastboot_timed_send_info() - Send INFO packet every 30 seconds
  90 *
  91 * @msg: String describing the reason for waiting
  92 *
  93 * Send an INFO packet during long commands based on timer. An INFO packet
  94 * is sent if the time is 30 seconds after start. Else, noop.
  95 */
  96static void fastboot_timed_send_info(const char *msg)
  97{
  98        static ulong start;
  99
 100        /* Initialize timer */
 101        if (start == 0)
 102                start = get_timer(0);
 103        ulong time = get_timer(start);
 104        /* Send INFO packet to host every 30 seconds */
 105        if (time >= 30000) {
 106                start = get_timer(0);
 107                fastboot_udp_send_info(msg);
 108        }
 109}
 110#endif
 111
 112/**
 113 * fastboot_send() - Sends a packet in response to received fastboot packet
 114 *
 115 * @header: Header for response packet
 116 * @fastboot_data: Pointer to received fastboot data
 117 * @fastboot_data_len: Length of received fastboot data
 118 * @retransmit: Nonzero if sending last sent packet
 119 */
 120static void fastboot_send(struct fastboot_header header, char *fastboot_data,
 121                          unsigned int fastboot_data_len, uchar retransmit)
 122{
 123        uchar *packet;
 124        uchar *packet_base;
 125        int len = 0;
 126        const char *error_msg = "An error occurred.";
 127        short tmp;
 128        struct fastboot_header response_header = header;
 129        static char command[FASTBOOT_COMMAND_LEN];
 130        static int cmd = -1;
 131        static bool pending_command;
 132        char response[FASTBOOT_RESPONSE_LEN] = {0};
 133
 134        /*
 135         * We will always be sending some sort of packet, so
 136         * cobble together the packet headers now.
 137         */
 138        packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
 139        packet_base = packet;
 140
 141        /* Resend last packet */
 142        if (retransmit) {
 143                memcpy(packet, last_packet, last_packet_len);
 144                net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
 145                                    fastboot_remote_port, fastboot_our_port,
 146                                    last_packet_len);
 147                return;
 148        }
 149
 150        response_header.seq = htons(response_header.seq);
 151        memcpy(packet, &response_header, sizeof(response_header));
 152        packet += sizeof(response_header);
 153
 154        switch (header.id) {
 155        case FASTBOOT_QUERY:
 156                tmp = htons(sequence_number);
 157                memcpy(packet, &tmp, sizeof(tmp));
 158                packet += sizeof(tmp);
 159                break;
 160        case FASTBOOT_INIT:
 161                tmp = htons(udp_version);
 162                memcpy(packet, &tmp, sizeof(tmp));
 163                packet += sizeof(tmp);
 164                tmp = htons(packet_size);
 165                memcpy(packet, &tmp, sizeof(tmp));
 166                packet += sizeof(tmp);
 167                break;
 168        case FASTBOOT_ERROR:
 169                memcpy(packet, error_msg, strlen(error_msg));
 170                packet += strlen(error_msg);
 171                break;
 172        case FASTBOOT_FASTBOOT:
 173                if (cmd == FASTBOOT_COMMAND_DOWNLOAD) {
 174                        if (!fastboot_data_len && !fastboot_data_remaining()) {
 175                                fastboot_data_complete(response);
 176                        } else {
 177                                fastboot_data_download(fastboot_data,
 178                                                       fastboot_data_len,
 179                                                       response);
 180                        }
 181                } else if (!pending_command) {
 182                        strlcpy(command, fastboot_data,
 183                                min((size_t)fastboot_data_len + 1,
 184                                    sizeof(command)));
 185                        pending_command = true;
 186                } else {
 187                        cmd = fastboot_handle_command(command, response);
 188                        pending_command = false;
 189                }
 190                /*
 191                 * Sent some INFO packets, need to update sequence number in
 192                 * header
 193                 */
 194                if (header.seq != sequence_number) {
 195                        response_header.seq = htons(sequence_number);
 196                        memcpy(packet_base, &response_header,
 197                               sizeof(response_header));
 198                }
 199                /* Write response to packet */
 200                memcpy(packet, response, strlen(response));
 201                packet += strlen(response);
 202                break;
 203        default:
 204                pr_err("ID %d not implemented.\n", header.id);
 205                return;
 206        }
 207
 208        len = packet - packet_base;
 209
 210        /* Save packet for retransmitting */
 211        last_packet_len = len;
 212        memcpy(last_packet, packet_base, last_packet_len);
 213
 214        net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
 215                            fastboot_remote_port, fastboot_our_port, len);
 216
 217        /* Continue boot process after sending response */
 218        if (!strncmp("OKAY", response, 4)) {
 219                switch (cmd) {
 220                case FASTBOOT_COMMAND_BOOT:
 221                        boot_downloaded_image();
 222                        break;
 223
 224                case FASTBOOT_COMMAND_CONTINUE:
 225                        net_set_state(NETLOOP_SUCCESS);
 226                        break;
 227
 228                case FASTBOOT_COMMAND_REBOOT:
 229                case FASTBOOT_COMMAND_REBOOT_BOOTLOADER:
 230                case FASTBOOT_COMMAND_REBOOT_FASTBOOTD:
 231                case FASTBOOT_COMMAND_REBOOT_RECOVERY:
 232                        do_reset(NULL, 0, 0, NULL);
 233                        break;
 234                }
 235        }
 236
 237        if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4))
 238                cmd = -1;
 239}
 240
 241/**
 242 * boot_downloaded_image() - Boots into downloaded image.
 243 */
 244static void boot_downloaded_image(void)
 245{
 246        fastboot_boot();
 247        net_set_state(NETLOOP_SUCCESS);
 248}
 249
 250/**
 251 * fastboot_handler() - Incoming UDP packet handler.
 252 *
 253 * @packet: Pointer to incoming UDP packet
 254 * @dport: Destination UDP port
 255 * @sip: Source IP address
 256 * @sport: Source UDP port
 257 * @len: Packet length
 258 */
 259static void fastboot_handler(uchar *packet, unsigned int dport,
 260                             struct in_addr sip, unsigned int sport,
 261                             unsigned int len)
 262{
 263        struct fastboot_header header;
 264        char fastboot_data[DATA_SIZE] = {0};
 265        unsigned int fastboot_data_len = 0;
 266
 267        if (dport != fastboot_our_port)
 268                return;
 269
 270        fastboot_remote_ip = sip;
 271        fastboot_remote_port = sport;
 272
 273        if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE)
 274                return;
 275        memcpy(&header, packet, sizeof(header));
 276        header.flags = 0;
 277        header.seq = ntohs(header.seq);
 278        packet += sizeof(header);
 279        len -= sizeof(header);
 280
 281        switch (header.id) {
 282        case FASTBOOT_QUERY:
 283                fastboot_send(header, fastboot_data, 0, 0);
 284                break;
 285        case FASTBOOT_INIT:
 286        case FASTBOOT_FASTBOOT:
 287                fastboot_data_len = len;
 288                if (len > 0)
 289                        memcpy(fastboot_data, packet, len);
 290                if (header.seq == sequence_number) {
 291                        fastboot_send(header, fastboot_data,
 292                                      fastboot_data_len, 0);
 293                        sequence_number++;
 294                } else if (header.seq == sequence_number - 1) {
 295                        /* Retransmit last sent packet */
 296                        fastboot_send(header, fastboot_data,
 297                                      fastboot_data_len, 1);
 298                }
 299                break;
 300        default:
 301                pr_err("ID %d not implemented.\n", header.id);
 302                header.id = FASTBOOT_ERROR;
 303                fastboot_send(header, fastboot_data, 0, 0);
 304                break;
 305        }
 306}
 307
 308void fastboot_start_server(void)
 309{
 310        printf("Using %s device\n", eth_get_name());
 311        printf("Listening for fastboot command on %pI4\n", &net_ip);
 312
 313        fastboot_our_port = WELL_KNOWN_PORT;
 314
 315#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
 316        fastboot_set_progress_callback(fastboot_timed_send_info);
 317#endif
 318        net_set_udp_handler(fastboot_handler);
 319
 320        /* zero out server ether in case the server ip has changed */
 321        memset(net_server_ethaddr, 0, 6);
 322}
 323