From patchwork Tue Apr 24 09:37:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Kiernan X-Patchwork-Id: 903343 X-Patchwork-Delegate: lukma@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="ZIYBQ5s+"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 40Vddl6jY9z9rvt for ; Tue, 24 Apr 2018 19:42:03 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 94CC8C21E12; Tue, 24 Apr 2018 09:40:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=FREEMAIL_FROM, RCVD_IN_MSPIKE_H2, T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 560FEC21EAE; Tue, 24 Apr 2018 09:38:55 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 8AFC7C21DE8; Tue, 24 Apr 2018 09:38:35 +0000 (UTC) Received: from mail-wr0-f181.google.com (mail-wr0-f181.google.com [209.85.128.181]) by lists.denx.de (Postfix) with ESMTPS id AE342C21DA1 for ; Tue, 24 Apr 2018 09:38:30 +0000 (UTC) Received: by mail-wr0-f181.google.com with SMTP id p5-v6so21056373wre.12 for ; Tue, 24 Apr 2018 02:38:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=HlRSCEYQcsn8AIg9OlEk2yaDpxok+Ot4y6JUVFzAYNc=; b=ZIYBQ5s+/N+s5f58aJYHyAaAi6Kqyg2gEgMSNz+513EWNAo1Wkax+/02BCE/1bjfcM i3M1daNW2o0mDQMo3R8owKOgSuAF6QAMlVewfABVfAUcGnnaZhCMdW7ODgywhOjfoiqB FwB6Miyiz7r7q7luGW2YUopM3AyLjPUad2ErqURYzYjetfShfr8I1bzTu9zpLHIpiV+q 5GI9mB40/LJJ/Ivd9WYy/cGqJ4gyMM75BAJJf/ih6lELiapxopaX90/3iICFo21wNILV vY+U8Ijr2pnZ3WF8omeV9/UpregpeTwTbz/4aFagxvZvlqec4NxuLn2t3TFHpIYw5+iE Q93A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=HlRSCEYQcsn8AIg9OlEk2yaDpxok+Ot4y6JUVFzAYNc=; b=BLzFD34JtJjAnH0wmxD+Z8Fj8AFsvx+rli7TpEfpluUpaSlL416GqpRqgp2iMxz188 EaTbwZBqmppspChq32gQ7zK8PmtrT6CPeJLGhg3ot0LynNbcCEpDpDR113hDLxgU5jdH /9CzrRPZe/VjIesSlOuOsurgu925eBTPuy+SBDHXoykNaqPrZCGTNiDzt2xHC20cnAoo NIH61oTMfmLuMJpySfLv2LWajSXy7TVoZHSvQ2Y32BCKD6oOkReUWpmfuem4Q/Pgxk6D pRf8xpwVYPO+EKnNy1T0o1nTEkItcTTxesl3+Bgviu9yjJ3xXJ1dfaotTF6rPQ7n4kHA sZmg== X-Gm-Message-State: ALQs6tCqJL9paCGHZIg9u5yUTu6hcnzl0sULey4bvxAJbRDsK1KMzlae 9nXvXojSoHXPXc1YMwCsEuOO/LKo X-Google-Smtp-Source: AIpwx49yH53xO10FxYvAOjJC/QU8UethEN0mwqHS5T0bYVt4Ut/xZq0HBo/8Af1m+eOI8X1U5HHKUQ== X-Received: by 2002:adf:87e1:: with SMTP id c30-v6mr18825814wrc.246.1524562709749; Tue, 24 Apr 2018 02:38:29 -0700 (PDT) Received: from localhost.localdomain ([195.89.75.181]) by smtp.gmail.com with ESMTPSA id h124sm2237052wma.35.2018.04.24.02.38.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 24 Apr 2018 02:38:29 -0700 (PDT) From: Alex Kiernan To: u-boot@lists.denx.de Date: Tue, 24 Apr 2018 09:37:05 +0000 Message-Id: <1524562627-5794-4-git-send-email-alex.kiernan@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1524562627-5794-1-git-send-email-alex.kiernan@gmail.com> References: <1524562627-5794-1-git-send-email-alex.kiernan@gmail.com> Cc: Joe Hershberger Subject: [U-Boot] [RFC PATCH v1 3/5] net: dfu: Merge AOSP UDP fastboot X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Merge UDP fastboot support from AOSP: https://android.googlesource.com/platform/external/u-boot/+/android-o-mr1-iot-preview-8 Signed-off-by: Alex Kiernan Co-authored-by: Alex Deymo Co-authored-by: Jocelyn Bohr Co-authored-by: Neal Ostrem --- cmd/fastboot.c | 32 ++- cmd/fastboot/Kconfig | 18 +- cmd/net.c | 6 + common/fb_common.c | 18 ++ common/fb_mmc.c | 34 +++- include/fastboot.h | 13 ++ include/net.h | 6 +- include/net/fastboot.h | 27 +++ net/Makefile | 1 + net/fastboot.c | 542 +++++++++++++++++++++++++++++++++++++++++++++++++ net/net.c | 9 + 11 files changed, 692 insertions(+), 14 deletions(-) create mode 100644 include/net/fastboot.h create mode 100644 net/fastboot.c diff --git a/cmd/fastboot.c b/cmd/fastboot.c index 8adcca5..ce5ac1e 100644 --- a/cmd/fastboot.c +++ b/cmd/fastboot.c @@ -11,18 +11,37 @@ #include #include #include +#include #include static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { +#ifdef CONFIG_USB_FUNCTION_FASTBOOT int controller_index; char *usb_controller; int ret; +#endif if (argc < 2) return CMD_RET_USAGE; - usb_controller = argv[1]; + if (!strcmp(argv[1], "udp")) { +#ifndef CONFIG_UDP_FUNCTION_FASTBOOT + pr_err("Fastboot UDP not enabled\n"); + return -1; +#else + return do_fastboot_udp(cmdtp, flag, argc, argv); +#endif + } + + if (strcmp(argv[1], "usb") || argc < 3) + return CMD_RET_USAGE; + +#ifndef CONFIG_USB_FUNCTION_FASTBOOT + pr_err("Fastboot USB not enabled\n"); + return -1; +#else + usb_controller = argv[2]; controller_index = simple_strtoul(usb_controller, NULL, 0); ret = board_usb_init(controller_index, USB_INIT_DEVICE); @@ -59,11 +78,14 @@ exit: board_usb_cleanup(controller_index, USB_INIT_DEVICE); return ret; +#endif } U_BOOT_CMD( - fastboot, 2, 1, do_fastboot, - "use USB Fastboot protocol", - "\n" - " - run as a fastboot usb device" + fastboot, 3, 1, do_fastboot, + "use USB or UDP Fastboot protocol", + "[usb,udp] \n" + " - run as a fastboot usb or udp device\n" + " usb: specify \n" + " udp: requires ip_addr set and ethernet initialized\n" ); diff --git a/cmd/fastboot/Kconfig b/cmd/fastboot/Kconfig index 0d2c2f1..0c57616 100644 --- a/cmd/fastboot/Kconfig +++ b/cmd/fastboot/Kconfig @@ -16,17 +16,25 @@ config USB_FUNCTION_FASTBOOT help This enables the USB part of the fastboot gadget. +config UDP_FUNCTION_FASTBOOT + select NET + bool "Enable fastboot protocol over UDP" + help + This enables the fastboot protocol over UDP. + config CMD_FASTBOOT bool "Enable FASTBOOT command" + depends on USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT help This enables the command "fastboot" which enables the Android - fastboot mode for the platform's USB device. Fastboot is a USB - protocol for downloading images, flashing and device control - used on Android devices. + fastboot mode for the platform. Fastboot is a protocol for + downloading images, flashing and device control used on + Android devices. Fastboot requires either network stack + enabled or support for acting as a USB device. See doc/README.android-fastboot for more information. -if USB_FUNCTION_FASTBOOT +if USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT config FASTBOOT_BUF_ADDR hex "Define FASTBOOT buffer address" @@ -129,6 +137,6 @@ config FASTBOOT_MBR_NAME specified on the "fastboot flash" command line matches the value defined here. The default target name for updating MBR is "mbr". -endif # USB_FUNCTION_FASTBOOT +endif # USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT endif # FASTBOOT diff --git a/cmd/net.c b/cmd/net.c index 67888d4..668f344 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -74,6 +74,12 @@ U_BOOT_CMD( ); #endif +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT +int do_fastboot_udp(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + return netboot_common(FASTBOOT, cmdtp, argc, argv); +} +#endif #ifdef CONFIG_CMD_RARP int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) diff --git a/common/fb_common.c b/common/fb_common.c index 53cffe3..cd79a82 100644 --- a/common/fb_common.c +++ b/common/fb_common.c @@ -12,6 +12,9 @@ #include #include +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT +#include +#endif void fastboot_fail(const char *reason, char *response) { @@ -24,3 +27,18 @@ void fastboot_okay(const char *reason, char *response) strncpy(response, "OKAY\0", 5); strncat(response, reason, FASTBOOT_RESPONSE_LEN - 4 - 1); } + +void timed_send_info(ulong *start, const char *msg) +{ +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT + /* Initialize timer */ + if (*start == 0) + *start = get_timer(0); + ulong time = get_timer(*start); + /* Send INFO packet to host every 30 seconds */ + if (time >= 30000) { + *start = get_timer(0); + fastboot_send_info(msg); + } +#endif +} diff --git a/common/fb_mmc.c b/common/fb_mmc.c index 02864aa..304bda1 100644 --- a/common/fb_mmc.c +++ b/common/fb_mmc.c @@ -29,6 +29,9 @@ #define CONFIG_FASTBOOT_MBR_NAME "mbr" #endif +#define FASTBOOT_MAX_BLK_WRITE 16384 +static ulong timer; + #define BOOT_PARTITION_NAME "boot" struct fb_mmc_sparse { @@ -57,13 +60,38 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc, return ret; } +static lbaint_t fb_mmc_blk_write(struct blk_desc *block_dev, lbaint_t start, + lbaint_t blkcnt, const void *buffer) +{ + lbaint_t blk = start; + lbaint_t blks_written; + lbaint_t cur_blkcnt; + lbaint_t blks = 0; + int i; + + for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) { + cur_blkcnt = min((int)blkcnt - i, FASTBOOT_MAX_BLK_WRITE); + if (!buffer) { + timed_send_info(&timer, "writing"); + blks_written = blk_dwrite(block_dev, blk, cur_blkcnt, + buffer + (i * block_dev->blksz)); + } else { + timed_send_info(&timer, "erasing"); + blks_written = blk_derase(block_dev, blk, cur_blkcnt); + } + blk += blks_written; + blks += blks_written; + } + return blks; +} + static lbaint_t fb_mmc_sparse_write(struct sparse_storage *info, lbaint_t blk, lbaint_t blkcnt, const void *buffer) { struct fb_mmc_sparse *sparse = info->priv; struct blk_desc *dev_desc = sparse->dev_desc; - return blk_dwrite(dev_desc, blk, blkcnt, buffer); + return fb_mmc_blk_write(dev_desc, blk, blkcnt, buffer); } static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info, @@ -91,7 +119,7 @@ static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info, puts("Flashing Raw Image\n"); - blks = blk_dwrite(dev_desc, info->start, blkcnt, buffer); + blks = fb_mmc_blk_write(dev_desc, info->start, blkcnt, buffer); if (blks != blkcnt) { pr_err("failed writing to device %d\n", dev_desc->devnum); fastboot_fail("failed writing to device", response); @@ -399,7 +427,7 @@ void fb_mmc_erase(const char *cmd, char *response) printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n", blks_start, blks_start + blks_size); - blks = blk_derase(dev_desc, blks_start, blks_size); + blks = fb_mmc_blk_write(dev_desc, blks_start, blks_size, NULL); if (blks != blks_size) { pr_err("failed erasing from device %d", dev_desc->devnum); fastboot_fail("failed erasing from device", response); diff --git a/include/fastboot.h b/include/fastboot.h index f22080a..f93a03b 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -19,4 +19,17 @@ void fastboot_fail(const char *reason, char *response); void fastboot_okay(const char *reason, char *response); +/** + * Send an INFO packet during long commands based on timer. If + * CONFIG_UDP_FUNCTION_FASTBOOT is defined, an INFO packet is sent + * if the time is 30 seconds after start. Else, noop. + * + * TODO: Handle the situation where both UDP and USB fastboot are + * enabled. + * + * @param start: Time since last INFO packet was sent. + * @param msg: String describing the reason for waiting + */ +void timed_send_info(ulong *start, const char *msg); + #endif /* _FASTBOOT_H_ */ diff --git a/include/net.h b/include/net.h index 3469811..890ae27 100644 --- a/include/net.h +++ b/include/net.h @@ -535,7 +535,7 @@ extern int net_restart_wrap; /* Tried all network devices */ enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP, - TFTPSRV, TFTPPUT, LINKLOCAL + TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT }; extern char net_boot_file_name[1024];/* Boot File name */ @@ -549,6 +549,10 @@ extern char *net_dns_resolve; /* The host to resolve */ extern char *net_dns_env_var; /* the env var to put the ip into */ #endif +#if defined(CONFIG_UDP_FUNCTION_FASTBOOT) +int do_fastboot_udp(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); +#endif + #if defined(CONFIG_CMD_PING) extern struct in_addr net_ping_ip; /* the ip address to ping */ #endif diff --git a/include/net/fastboot.h b/include/net/fastboot.h new file mode 100644 index 0000000..c0dd033 --- /dev/null +++ b/include/net/fastboot.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2016 The Android Open Source Project + */ + +#ifndef __NET_FASTBOOT_H__ +#define __NET_FASTBOOT_H__ + +/**********************************************************************/ +/* + * Global functions and variables. + */ + +/** + * Wait for incoming fastboot comands. + */ +void fastboot_start_server(void); +/** + * Send an INFO packet during long commands + * + * @param msg: String describing the reason for waiting + */ +void fastboot_send_info(const char *msg); + +/**********************************************************************/ + +#endif /* __NET_FASTBOOT_H__ */ diff --git a/net/Makefile b/net/Makefile index ce6e5ad..3489ce5 100644 --- a/net/Makefile +++ b/net/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot.o # Disable this warning as it is triggered by: # sprintf(buf, index ? "foo%d" : "foo", index) diff --git a/net/fastboot.c b/net/fastboot.c new file mode 100644 index 0000000..32cb581 --- /dev/null +++ b/net/fastboot.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2016 The Android Open Source Project + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Fastboot port # defined in spec */ +#define WELL_KNOWN_PORT 5554 + +enum { + FASTBOOT_ERROR = 0, + FASTBOOT_QUERY = 1, + FASTBOOT_INIT = 2, + FASTBOOT_FASTBOOT = 3, +}; + +struct __packed fastboot_header { + uchar id; + uchar flags; + unsigned short seq; +}; + +#define PACKET_SIZE 1024 +#define FASTBOOT_HEADER_SIZE sizeof(struct fastboot_header) +#define DATA_SIZE (PACKET_SIZE - FASTBOOT_HEADER_SIZE) +#define FASTBOOT_VERSION "0.4" + +/* Sequence number sent for every packet */ +static unsigned short fb_sequence_number = 1; +static const unsigned short fb_packet_size = PACKET_SIZE; +static const unsigned short fb_udp_version = 1; + +/* Keep track of last packet for resubmission */ +static uchar last_packet[PACKET_SIZE]; +static unsigned int last_packet_len; + +/* Parsed from first fastboot command packet */ +static char *cmd_string; +static char *cmd_parameter; + +/* Fastboot download parameters */ +static unsigned int bytes_received; +static unsigned int bytes_expected; +static unsigned int image_size; + +static struct in_addr fastboot_remote_ip; +/* The UDP port at their end */ +static int fastboot_remote_port; +/* The UDP port at our end */ +static int fastboot_our_port; + +static void fb_getvar(char *); +static void fb_download(char *, unsigned int, char *); +static void fb_flash(char *); +static void fb_erase(char *); +static void fb_continue(char *); +static void fb_reboot(char *); +static void boot_downloaded_image(void); +static void cleanup_command_data(void); +static void write_fb_response(const char *, const char *, char *); + +void fastboot_send_info(const char *msg) +{ + uchar *packet; + uchar *packet_base; + int len = 0; + char response[FASTBOOT_RESPONSE_LEN] = {0}; + + struct fastboot_header fb_response_header = { + .id = FASTBOOT_FASTBOOT, + .flags = 0, + .seq = htons(fb_sequence_number) + }; + ++fb_sequence_number; + packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; + packet_base = packet; + + /* Write headers */ + memcpy(packet, &fb_response_header, sizeof(fb_response_header)); + packet += sizeof(fb_response_header); + /* Write response */ + write_fb_response("INFO", msg, response); + memcpy(packet, response, strlen(response)); + packet += strlen(response); + + len = packet - packet_base; + + /* Save packet for retransmitting */ + last_packet_len = len; + memcpy(last_packet, packet_base, last_packet_len); + + net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, + fastboot_remote_port, fastboot_our_port, len); +} + +/** + * Constructs and sends a packet in response to received fastboot packet + * + * @param fb_header Header for response packet + * @param fastboot_data Pointer to received fastboot data + * @param fastboot_data_len Length of received fastboot data + * @param retransmit Nonzero if sending last sent packet + */ +static void fastboot_send(struct fastboot_header fb_header, char *fastboot_data, + unsigned int fastboot_data_len, uchar retransmit) +{ + uchar *packet; + uchar *packet_base; + int len = 0; + const char *error_msg = "An error occurred."; + short tmp; + struct fastboot_header fb_response_header = fb_header; + char response[FASTBOOT_RESPONSE_LEN] = {0}; + /* + * We will always be sending some sort of packet, so + * cobble together the packet headers now. + */ + packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; + packet_base = packet; + + /* Resend last packet */ + if (retransmit) { + memcpy(packet, last_packet, last_packet_len); + net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, + fastboot_remote_port, fastboot_our_port, + last_packet_len); + return; + } + + fb_response_header.seq = htons(fb_response_header.seq); + memcpy(packet, &fb_response_header, sizeof(fb_response_header)); + packet += sizeof(fb_response_header); + + switch (fb_header.id) { + case FASTBOOT_QUERY: + tmp = htons(fb_sequence_number); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + break; + case FASTBOOT_INIT: + tmp = htons(fb_udp_version); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + tmp = htons(fb_packet_size); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + break; + case FASTBOOT_ERROR: + memcpy(packet, error_msg, strlen(error_msg)); + packet += strlen(error_msg); + break; + case FASTBOOT_FASTBOOT: + if (!cmd_string) { + /* Parse command and send ack */ + cmd_parameter = fastboot_data; + cmd_string = strsep(&cmd_parameter, ":"); + cmd_string = strdup(cmd_string); + if (cmd_parameter) + cmd_parameter = strdup(cmd_parameter); + } else if (!strcmp("getvar", cmd_string)) { + fb_getvar(response); + } else if (!strcmp("download", cmd_string)) { + fb_download(fastboot_data, fastboot_data_len, response); + } else if (!strcmp("flash", cmd_string)) { + fb_flash(response); + } else if (!strcmp("erase", cmd_string)) { + fb_erase(response); + } else if (!strcmp("boot", cmd_string)) { + write_fb_response("OKAY", "", response); + } else if (!strcmp("continue", cmd_string)) { + fb_continue(response); + } else if (!strncmp("reboot", cmd_string, 6)) { + fb_reboot(response); + } else if (!strcmp("set_active", cmd_string)) { + /* A/B not implemented, for now do nothing */ + write_fb_response("OKAY", "", response); + } else { + pr_err("command %s not implemented.\n", cmd_string); + write_fb_response("FAIL", "unrecognized command", + response); + } + /* Sent some INFO packets, need to update sequence number in + * header + */ + if (fb_header.seq != fb_sequence_number) { + fb_response_header.seq = htons(fb_sequence_number); + memcpy(packet_base, &fb_response_header, + sizeof(fb_response_header)); + } + /* Write response to packet */ + memcpy(packet, response, strlen(response)); + packet += strlen(response); + break; + default: + pr_err("ID %d not implemented.\n", fb_header.id); + return; + } + + len = packet - packet_base; + + /* Save packet for retransmitting */ + last_packet_len = len; + memcpy(last_packet, packet_base, last_packet_len); + + net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, + fastboot_remote_port, fastboot_our_port, len); + + /* Continue boot process after sending response */ + if (!strncmp("OKAY", response, 4)) { + if (!strcmp("boot", cmd_string)) { + boot_downloaded_image(); + } else if (!strcmp("continue", cmd_string)) { + run_command(env_get("bootcmd"), CMD_FLAG_ENV); + } else if (!strncmp("reboot", cmd_string, 6)) { + /* Matches reboot or reboot-bootloader */ + do_reset(NULL, 0, 0, NULL); + } + } + + /* OKAY and FAIL indicate command is complete */ + if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) + cleanup_command_data(); +} + +/** + * Writes ascii string specified by cmd_parameter to response. + * + * @param repsonse Pointer to fastboot response buffer + */ +static void fb_getvar(char *response) +{ + if (!cmd_parameter) { + write_fb_response("FAIL", "missing var", response); + } else if (!strcmp("version", cmd_parameter)) { + write_fb_response("OKAY", FASTBOOT_VERSION, response); + } else if (!strcmp("bootloader-version", cmd_parameter) || + !strcmp("version-bootloader", cmd_parameter)) { + write_fb_response("OKAY", U_BOOT_VERSION, response); + } else if (!strcmp("downloadsize", cmd_parameter) || + !strcmp("max-download-size", cmd_parameter)) { + char buf_size_str[12]; + + sprintf(buf_size_str, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE); + write_fb_response("OKAY", buf_size_str, response); + } else if (!strcmp("serialno", cmd_parameter)) { + const char *tmp = env_get("serial#"); + + if (tmp) + write_fb_response("OKAY", tmp, response); + else + write_fb_response("FAIL", "Value not set", response); + } else if (!strcmp("version-baseband", cmd_parameter)) { + write_fb_response("OKAY", "N/A", response); + } else if (!strcmp("product", cmd_parameter)) { + const char *board = env_get("board"); + + if (board) + write_fb_response("OKAY", board, response); + else + write_fb_response("FAIL", "Board not set", response); + } else if (!strcmp("current-slot", cmd_parameter)) { + /* A/B not implemented, for now always return _a */ + write_fb_response("OKAY", "_a", response); + } else if (!strcmp("slot-suffixes", cmd_parameter)) { + write_fb_response("OKAY", "_a,_b", response); + } else if (!strncmp("has-slot", cmd_parameter, 8)) { + char *part_name = cmd_parameter; + + cmd_parameter = strsep(&part_name, ":"); + if (!strcmp(part_name, "boot") || !strcmp(part_name, "system")) + write_fb_response("OKAY", "yes", response); + else + write_fb_response("OKAY", "no", response); + } else if (!strncmp("partition-type", cmd_parameter, 14) || + !strncmp("partition-size", cmd_parameter, 14)) { + disk_partition_t part_info; + struct blk_desc *dev_desc; + char *part_name = cmd_parameter; + char part_size_str[20]; + + cmd_parameter = strsep(&part_name, ":"); + dev_desc = blk_get_dev("mmc", 0); + if (!dev_desc) { + write_fb_response("FAIL", "block device not found", + response); + } else if (part_get_info_by_name(dev_desc, part_name, + &part_info) < 0) { + write_fb_response("FAIL", "partition not found", + response); + } else if (!strncmp("partition-type", cmd_parameter, 14)) { + write_fb_response("OKAY", (char *)part_info.type, + response); + } else if (!strncmp("partition-size", cmd_parameter, 14)) { + sprintf(part_size_str, "0x%016x", (int)part_info.size); + write_fb_response("OKAY", part_size_str, response); + } + } else { + printf("WARNING: unknown variable: %s\n", cmd_parameter); + write_fb_response("FAIL", "Variable not implemented", + response); + } +} + +/** + * Copies image data from fastboot_data to CONFIG_FASTBOOT_BUF_ADDR. + * Writes to response. + * + * @param fastboot_data Pointer to received fastboot data + * @param fastboot_data_len Length of received fastboot data + * @param repsonse Pointer to fastboot response buffer + */ +static void fb_download(char *fastboot_data, unsigned int fastboot_data_len, + char *response) +{ + char *tmp; + + if (bytes_expected == 0) { + if (!cmd_parameter) { + write_fb_response("FAIL", "Expected command parameter", + response); + return; + } + bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16); + if (bytes_expected == 0) { + write_fb_response("FAIL", "Expected nonzero image size", + response); + return; + } + } + if (fastboot_data_len == 0 && bytes_received == 0) { + /* Nothing to download yet. Response is of the form: + * [DATA|FAIL]$cmd_parameter + * + * where cmd_parameter is an 8 digit hexadecimal number + */ + if (bytes_expected > CONFIG_FASTBOOT_BUF_SIZE) + write_fb_response("FAIL", cmd_parameter, response); + else + write_fb_response("DATA", cmd_parameter, response); + } else if (fastboot_data_len == 0 && + (bytes_received >= bytes_expected)) { + /* Download complete. Respond with "OKAY" */ + write_fb_response("OKAY", "", response); + image_size = bytes_received; + bytes_expected = 0; + bytes_received = 0; + } else { + if (fastboot_data_len == 0 || + (bytes_received + fastboot_data_len) > bytes_expected) { + write_fb_response("FAIL", + "Received invalid data length", + response); + return; + } + /* Download data to CONFIG_FASTBOOT_BUF_ADDR */ + memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + bytes_received, + fastboot_data, fastboot_data_len); + bytes_received += fastboot_data_len; + } +} + +/** + * Writes the previously downloaded image to the partition indicated by + * cmd_parameter. Writes to response. + * + * @param repsonse Pointer to fastboot response buffer + */ +static void fb_flash(char *response) +{ + fb_mmc_flash_write(cmd_parameter, (void *)CONFIG_FASTBOOT_BUF_ADDR, + image_size, response); +} + +/** + * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes + * to response. + * + * @param repsonse Pointer to fastboot response buffer + */ +static void fb_erase(char *response) +{ + fb_mmc_erase(cmd_parameter, response); +} + +/** + * Continues normal boot process by running "bootcmd". Writes + * to response. + * + * @param repsonse Pointer to fastboot response buffer + */ +static void fb_continue(char *response) +{ + char *bootcmd; + + bootcmd = env_get("bootcmd"); + if (bootcmd) + write_fb_response("OKAY", "", response); + else + write_fb_response("FAIL", "bootcmd not set", response); +} + +/** + * Sets reboot bootloader flag if requested. Writes to response. + * + * @param repsonse Pointer to fastboot response buffer + */ +static void fb_reboot(char *response) +{ + write_fb_response("OKAY", "", response); + if (!strcmp("reboot-bootloader", cmd_string)) + strcpy((char *)CONFIG_FASTBOOT_BUF_ADDR, "reboot-bootloader"); +} + +/** + * Boots into downloaded image. + */ +static void boot_downloaded_image(void) +{ + char kernel_addr[12]; + char *fdt_addr = env_get("fdt_addr_r"); + char *const bootm_args[] = { + "bootm", kernel_addr, "-", fdt_addr, NULL + }; + + sprintf(kernel_addr, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR); + + printf("\nBooting kernel at %s with fdt at %s...\n\n\n", + kernel_addr, fdt_addr); + do_bootm(NULL, 0, 4, bootm_args); + + /* This only happens if image is faulty so we start over. */ + do_reset(NULL, 0, 0, NULL); +} + +/** + * Writes a response to response buffer of the form "$tag$reason". + * + * @param tag The first part of the response + * @param reason The second part of the response + * @param repsonse Pointer to fastboot response buffer + */ +static void write_fb_response(const char *tag, const char *reason, + char *response) +{ + strncpy(response, tag, strlen(tag)); + strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(tag) - 1); +} + +/** + * Frees any resources allocated during current fastboot command. + */ +static void cleanup_command_data(void) +{ + /* cmd_parameter and cmd_string potentially point to memory allocated by + * strdup + */ + if (cmd_parameter) + free(cmd_parameter); + if (cmd_string) + free(cmd_string); + cmd_parameter = NULL; + cmd_string = NULL; +} + +/** + * Incoming UDP packet handler. + * + * @param packet Pointer to incoming UDP packet + * @param dport Destination UDP port + * @param sip Source IP address + * @param sport Source UDP port + * @param len Packet length + */ +static void fastboot_handler(uchar *packet, unsigned int dport, + struct in_addr sip, unsigned int sport, + unsigned int len) +{ + struct fastboot_header fb_header; + char fastboot_data[DATA_SIZE] = {0}; + unsigned int fastboot_data_len = 0; + + if (dport != fastboot_our_port) + return; + + fastboot_remote_ip = sip; + fastboot_remote_port = sport; + + if (len < FASTBOOT_HEADER_SIZE || len > PACKET_SIZE) + return; + memcpy(&fb_header, packet, sizeof(fb_header)); + fb_header.flags = 0; + fb_header.seq = ntohs(fb_header.seq); + packet += sizeof(fb_header); + len -= sizeof(fb_header); + + switch (fb_header.id) { + case FASTBOOT_QUERY: + fastboot_send(fb_header, fastboot_data, 0, 0); + break; + case FASTBOOT_INIT: + case FASTBOOT_FASTBOOT: + fastboot_data_len = len; + if (len > 0) + memcpy(fastboot_data, packet, len); + if (fb_header.seq == fb_sequence_number) { + fastboot_send(fb_header, fastboot_data, + fastboot_data_len, 0); + fb_sequence_number++; + } else if (fb_header.seq == fb_sequence_number - 1) { + /* Retransmit last sent packet */ + fastboot_send(fb_header, fastboot_data, + fastboot_data_len, 1); + } + break; + default: + pr_err("ID %d not implemented.\n", fb_header.id); + fb_header.id = FASTBOOT_ERROR; + fastboot_send(fb_header, fastboot_data, 0, 0); + break; + } +} + +void fastboot_start_server(void) +{ + printf("Using %s device\n", eth_get_name()); + printf("Listening for fastboot command on %pI4\n", &net_ip); + + fastboot_our_port = WELL_KNOWN_PORT; + + net_set_udp_handler(fastboot_handler); + + /* zero out server ether in case the server ip has changed */ + memset(net_server_ethaddr, 0, 6); +} diff --git a/net/net.c b/net/net.c index 8a9b69c..42ecb1a 100644 --- a/net/net.c +++ b/net/net.c @@ -87,6 +87,9 @@ #include #include #include +#if defined(CONFIG_UDP_FUNCTION_FASTBOOT) +#include +#endif #include #if defined(CONFIG_LED_STATUS) #include @@ -453,6 +456,11 @@ restart: tftp_start_server(); break; #endif +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT + case FASTBOOT: + fastboot_start_server(); + break; +#endif #if defined(CONFIG_CMD_DHCP) case DHCP: bootp_reset(); @@ -1324,6 +1332,7 @@ common: /* Fall through */ case NETCONS: + case FASTBOOT: case TFTPSRV: if (net_ip.s_addr == 0) { puts("*** ERROR: `ipaddr' not set\n");