From mboxrd@z Thu Jan 1 00:00:00 1970 From: Alex Kiernan Date: Tue, 15 May 2018 14:48:53 +0000 Subject: [U-Boot] [PATCH v4 14/14] net: fastboot: Merge AOSP UDP fastboot In-Reply-To: <1526395734-333-1-git-send-email-alex.kiernan@gmail.com> References: <1526395734-333-1-git-send-email-alex.kiernan@gmail.com> Message-ID: <1526395734-333-15-git-send-email-alex.kiernan@gmail.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 Signed-off-by: Alex Deymo Signed-off-by: Jocelyn Bohr Reviewed-by: Simon Glass --- Changes in v4: - guard fb_getvar/fb_command with UDP_FUNCTION_FASTBOOT in Makefile - add docbook comments - remove parameter from fastboot_boot() since we always want fastboot_buf_addr (and if we're using fastboot_bootcmd then it's ignored) Changes in v3: - use FASTBOOT as our guard in Kconfig not a list of USB || UDP - correct mis-translation from AOSP introduced when cleaning up for checkpatch - we should write when buffer is not NULL, rather than erasing, and erase when buffer is NULL - use CMD_RET_USAGE from do_fastboot - remove do_fastboot_udp from cmd/net.c and rewrite using net_loop() - rename timed_send_info to fastboot_send_info, rename fastboot_send_info to fastboot_udp_send_info - replace FASTBOOT_HEADER_SIZE with sizeof(struct fastboot_header) - move start time into timed_send_info() rather than passing it in - make calls to fastboot_udp_send_info a runtime dependency, not a compile time one - set ${filesize} to size of downloaded image - add progress meter from USB path during download - add support for 'oem format' command from the USB path - rename 'fastbootcmd' to 'fastboot_bootcmd' to make clear that this is the fastboot boot command - make getvar implementation table driven - add fastboot_buf_addr, fastboot_buf_size to override buffer address and size - return correct filesystem type in getvar partition-type on MMC - process "fastboot." prefixed env variables in getvar first so you can override the normal values (this also lets you set a fs type for NAND devices) - make global variables static and add accessors for the things which the transport layers need - squash subsequent patches which change this code into this one: - If the fastboot flash/erase commands are disabled, remove that support so we still build correctly. - Add NAND support to fastboot UDP flash/erase commands - If we don't have a partition name passed, report it as not found. - Change the behaviour of the fastboot net code such that "reboot-bootloader" is no longer written to CONFIG_FASTBOOT_BUF_ADDR for use as a marker on reboot (the AOSP code in common/android-bootloader.c uses this marker - this code could be reinstated there if that gets merged). - Merge USB and UDP boot code. The USB implementation stays the same, but UDP no longer passes an fdt. We introduce a new environment variable 'fastboot_bootcmd' which if set overrides the hardcoded boot command, setting this then allows the UDP implementation to remain the same. If after running 'fastboot_bootcmd' the board has not booted, control is returned to U-Boot and the fastboot process ends. - Separate the fastboot protocol handling from the fastboot UDP code in preparation for reusing it in the USB code. Changes in v2: - ensure fastboot syntax is backward compatible - 'fastboot 0' means 'fastboot usb 0' cmd/fastboot.c | 91 +++++++++++- drivers/fastboot/Kconfig | 15 ++ drivers/fastboot/Makefile | 3 +- drivers/fastboot/fb_command.c | 327 ++++++++++++++++++++++++++++++++++++++++++ drivers/fastboot/fb_common.c | 113 +++++++++++++++ drivers/fastboot/fb_getvar.c | 229 +++++++++++++++++++++++++++++ drivers/fastboot/fb_mmc.c | 76 +++++++++- drivers/fastboot/fb_nand.c | 12 +- include/fastboot.h | 157 ++++++++++++++++++++ include/fb_mmc.h | 8 +- include/fb_nand.h | 10 +- include/net.h | 2 +- include/net/fastboot.h | 21 +++ net/Makefile | 1 + net/fastboot.c | 312 ++++++++++++++++++++++++++++++++++++++++ net/net.c | 7 + 16 files changed, 1367 insertions(+), 17 deletions(-) create mode 100644 drivers/fastboot/fb_command.c create mode 100644 drivers/fastboot/fb_getvar.c create mode 100644 include/net/fastboot.h create mode 100644 net/fastboot.c diff --git a/cmd/fastboot.c b/cmd/fastboot.c index a5ec5f4..557257a 100644 --- a/cmd/fastboot.c +++ b/cmd/fastboot.c @@ -10,10 +10,32 @@ #include #include #include +#include +#include #include -static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +static int do_fastboot_udp(int argc, char *const argv[], + uintptr_t buf_addr, size_t buf_size) { +#if CONFIG_IS_ENABLED(UDP_FUNCTION_FASTBOOT) + int err = net_loop(FASTBOOT); + + if (err < 0) { + printf("fastboot udp error: %d\n", err); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +#else + pr_err("Fastboot UDP not enabled\n"); + return CMD_RET_FAILURE; +#endif +} + +static int do_fastboot_usb(int argc, char *const argv[], + uintptr_t buf_addr, size_t buf_size) +{ +#if CONFIG_IS_ENABLED(USB_FUNCTION_FASTBOOT) int controller_index; char *usb_controller; int ret; @@ -58,11 +80,70 @@ exit: board_usb_cleanup(controller_index, USB_INIT_DEVICE); return ret; +#else + pr_err("Fastboot USB not enabled\n"); + return CMD_RET_FAILURE; +#endif +} + +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + uintptr_t buf_addr = (uintptr_t)NULL; + size_t buf_size = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + while (argc > 1 && **(argv + 1) == '-') { + char *arg = *++argv; + + --argc; + while (*++arg) { + switch (*arg) { + case 'l': + if (--argc <= 0) + return CMD_RET_USAGE; + buf_addr = simple_strtoul(*++argv, NULL, 16); + goto NXTARG; + + case 's': + if (--argc <= 0) + return CMD_RET_USAGE; + buf_size = simple_strtoul(*++argv, NULL, 16); + goto NXTARG; + + default: + return CMD_RET_USAGE; + } + } +NXTARG: + ; + } + + fastboot_init((void *)buf_addr, buf_size); + + if (!strcmp(argv[1], "udp")) + return do_fastboot_udp(argc, argv, buf_addr, buf_size); + + if (!strcmp(argv[1], "usb")) { + argv++; + argc--; + } + + return do_fastboot_usb(argc, argv, buf_addr, buf_size); } +#ifdef CONFIG_SYS_LONGHELP +static char fastboot_help_text[] = + "[-l addr] [-s size] usb | udp\n" + "\taddr - address of buffer used during data transfers (" + __stringify(CONFIG_FASTBOOT_BUF_ADDR) ")\n" + "\tsize - size of buffer used during data transfers (" + __stringify(CONFIG_FASTBOOT_BUF_SIZE) ")" + ; +#endif + U_BOOT_CMD( - fastboot, 2, 1, do_fastboot, - "use USB Fastboot protocol", - "\n" - " - run as a fastboot usb device" + fastboot, CONFIG_SYS_MAXARGS, 1, do_fastboot, + "run as a fastboot usb or udp device", fastboot_help_text ); diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index 82e1420..5844f3f 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -14,6 +14,13 @@ config USB_FUNCTION_FASTBOOT help This enables the USB part of the fastboot gadget. +config UDP_FUNCTION_FASTBOOT + depends on NET + select FASTBOOT + bool "Enable fastboot protocol over UDP" + help + This enables the fastboot protocol over UDP. + if FASTBOOT config FASTBOOT_BUF_ADDR @@ -119,6 +126,14 @@ 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". +config FASTBOOT_CMD_OEM_FORMAT + bool "Enable the 'oem format' command" + depends on FASTBOOT_FLASH_MMC && CMD_GPT + help + Add support for the "oem format" command from a client. This + relies on the env variable partitions to contain the list of + partitions as required by the gpt command. + endif # FASTBOOT endmenu diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile index e4bd389..8831096 100644 --- a/drivers/fastboot/Makefile +++ b/drivers/fastboot/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += fb_common.o - +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_getvar.o +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_command.o obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c new file mode 100644 index 0000000..0ca05ec --- /dev/null +++ b/drivers/fastboot/fb_command.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2016 The Android Open Source Project + */ + +#include +#include +#include +#include +#include +#include + +/** + * image_size - final fastboot image size + */ +static u32 image_size; + +/** + * fastboot_bytes_received - number of bytes received in the current download + */ +static u32 fastboot_bytes_received; + +/** + * fastboot_bytes_expected - number of bytes expected in the current download + */ +static u32 fastboot_bytes_expected; + +static void fb_okay(char *, char *); +static void fb_getvar(char *, char *); +static void fb_download(char *, char *); +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) +static void fb_flash(char *, char *); +static void fb_erase(char *, char *); +#endif +static void fb_reboot_bootloader(char *, char *); +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) +static void fb_oem_format(char *, char *); +#endif + +static const struct { + const char *command; + void (*dispatch)(char *cmd_parameter, char *response); +} fb_commands[FASTBOOT_COMMAND_COUNT] = { + [FASTBOOT_COMMAND_GETVAR] = { + .command = "getvar", + .dispatch = fb_getvar + }, + [FASTBOOT_COMMAND_DOWNLOAD] = { + .command = "download", + .dispatch = fb_download + }, +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) + [FASTBOOT_COMMAND_FLASH] = { + .command = "flash", + .dispatch = fb_flash + }, + [FASTBOOT_COMMAND_ERASE] = { + .command = "erase", + .dispatch = fb_erase + }, +#endif + [FASTBOOT_COMMAND_BOOT] = { + .command = "boot", + .dispatch = fb_okay + }, + [FASTBOOT_COMMAND_CONTINUE] = { + .command = "continue", + .dispatch = fb_okay + }, + [FASTBOOT_COMMAND_REBOOT] = { + .command = "reboot", + .dispatch = fb_okay + }, + [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] = { + .command = "reboot-bootloader", + .dispatch = fb_reboot_bootloader + }, + [FASTBOOT_COMMAND_SET_ACTIVE] = { + .command = "set_active", + .dispatch = fb_okay + }, +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) + [FASTBOOT_COMMAND_OEM_FORMAT] = { + .command = "oem format", + .dispatch = fb_oem_format, + }, +#endif +}; + +/** + * fastboot_get_bytes_remaining() - return bytes remaining in current transfer + * + * Return: Number of bytes left in the current download + */ +u32 fastboot_get_bytes_remaining(void) +{ + return fastboot_bytes_expected - fastboot_bytes_received; +} + +/** + * fastboot_handle_command - Handle fastboot command + * + * @cmd_string: Pointer to command string + * @response: Pointer to fastboot response buffer + * + * Return: Executed command, or -1 if not recognized + */ +int fastboot_handle_command(char *cmd_string, char *response) +{ + int i; + char *cmd_parameter; + + cmd_parameter = cmd_string; + strsep(&cmd_parameter, ":"); + + for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) { + if (!strcmp(fb_commands[i].command, cmd_string)) { + if (fb_commands[i].dispatch) { + fb_commands[i].dispatch(cmd_parameter, + response); + return i; + } else { + break; + } + } + } + + pr_err("command %s not recognized.\n", cmd_string); + fastboot_fail("unrecognized command", response); + return -1; +} + +/** + * fb_okay() - Send bare OKAY response + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + * + * Send a bare OKAY fastboot response. This is used where the command is + * valid, but all the work is done after the response has been sent (e.g. + * boot, reboot etc.) + */ +static void fb_okay(char *cmd_parameter, char *response) +{ + fastboot_okay(NULL, response); +} + +/** + * fb_getvar() - Read a config/version variable + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + */ +static void fb_getvar(char *cmd_parameter, char *response) +{ + fastboot_getvar(cmd_parameter, response); +} + +/** + * fastboot_download() - Start a download transfer from the client + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + */ +static void fb_download(char *cmd_parameter, char *response) +{ + char *tmp; + + if (!cmd_parameter) { + fastboot_fail("Expected command parameter", response); + return; + } + fastboot_bytes_received = 0; + fastboot_bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16); + if (fastboot_bytes_expected == 0) { + fastboot_fail("Expected nonzero image size", response); + return; + } + /* + * Nothing to download yet. Response is of the form: + * [DATA|FAIL]$cmd_parameter + * + * where cmd_parameter is an 8 digit hexadecimal number + */ + if (fastboot_bytes_expected > fastboot_get_buf_size()) { + fastboot_fail(cmd_parameter, response); + } else { + printf("Starting download of %d bytes\n", + fastboot_bytes_expected); + fastboot_response("DATA", response, "%s", cmd_parameter); + } +} + +/** + * fastboot_download_data() - Copy image data to fastboot_buf_addr. + * + * @fastboot_data: Pointer to received fastboot data + * @fastboot_data_len: Length of received fastboot data + * @response: Pointer to fastboot response buffer + * + * Copies image data from fastboot_data to fastboot_buf_addr. Writes to + * response. fastboot_bytes_received is updated to indicate the number + * of bytes that have been transferred. + * + * On completion sets image_size and ${filesize} to the total size of the + * downloaded image. + */ +void fastboot_download_data(const void *fastboot_data, + unsigned int fastboot_data_len, + char *response) +{ + if (fastboot_data_len == 0 && + fastboot_bytes_received >= fastboot_bytes_expected) { + /* Download complete. Respond with "OKAY" */ + fastboot_okay(NULL, response); + printf("\ndownloading of %d bytes finished\n", + fastboot_bytes_received); + image_size = fastboot_bytes_received; + env_set_hex("filesize", image_size); + fastboot_bytes_expected = 0; + fastboot_bytes_received = 0; + } else { +#define BYTES_PER_DOT 0x20000 + u32 pre_dot_num, now_dot_num; + + if (fastboot_data_len == 0 || + (fastboot_bytes_received + fastboot_data_len) > + fastboot_bytes_expected) { + fastboot_fail("Received invalid data length", + response); + return; + } + /* Download data to fastboot_buf_addr */ + memcpy(fastboot_get_buf_addr() + fastboot_bytes_received, + fastboot_data, fastboot_data_len); + + pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT; + fastboot_bytes_received += fastboot_data_len; + now_dot_num = fastboot_bytes_received / BYTES_PER_DOT; + + if (pre_dot_num != now_dot_num) { + putc('.'); + if (!(now_dot_num % 74)) + putc('\n'); + } + } +} + +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) +/** + * fb_flash() - write the downloaded image to the indicated partition. + * + * @cmd_parameter: Pointer to partition name + * @response: Pointer to fastboot response buffer + * + * Writes the previously downloaded image to the partition indicated by + * cmd_parameter. Writes to response. + */ +static void fb_flash(char *cmd_parameter, char *response) +{ +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) + fb_mmc_flash_write(cmd_parameter, fastboot_get_buf_addr(), image_size, + response); +#endif +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) + fb_nand_flash_write(cmd_parameter, fastboot_get_buf_addr(), image_size, + response); +#endif +} + +/** + * fb_erase() - erase the indicated partition. + * + * @cmd_parameter: Pointer to partition name + * @response: Pointer to fastboot response buffer + * + * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes + * to response. + */ +static void fb_erase(char *cmd_parameter, char *response) +{ +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) + fb_mmc_erase(cmd_parameter, response); +#endif +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) + fb_nand_erase(cmd_parameter, response); +#endif +} +#endif + +/** + * fb_reboot_bootloader() - Sets reboot bootloader flag. + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + */ +static void fb_reboot_bootloader(char *cmd_parameter, char *response) +{ + if (fastboot_set_reboot_flag()) + fastboot_fail("Cannot set reboot flag", response); + else + fastboot_okay(NULL, response); +} + +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) +/** + * fb_oem_format() - Execute the OEM format command + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + */ +static void fb_oem_format(char *cmd_parameter, char *response) +{ + char cmdbuf[32]; + + if (!env_get("partitions")) { + fastboot_fail("partitions not set", response); + } else { + sprintf(cmdbuf, "gpt write mmc %x $partitions", + CONFIG_FASTBOOT_FLASH_MMC_DEV); + if (run_command(cmdbuf, 0)) + fastboot_fail("", response); + else + fastboot_okay(NULL, response); + } +} +#endif diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index 853ad9c..3a2aaa2 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -12,6 +12,22 @@ #include #include +#include + +/** + * fastboot_buf_addr - base address of the fastboot download buffer + */ +static void *fastboot_buf_addr; + +/** + * fastboot_buf_size - size of the fastboot download buffer + */ +static u32 fastboot_buf_size; + +/** + * fastboot_progress_callback - callback executed during long operations + */ +static void (*fastboot_progress_callback)(const char *msg); /** * fastboot_response() - Writes a response of the form "$tag$reason". @@ -75,3 +91,100 @@ int __weak fastboot_set_reboot_flag(void) { return -ENOSYS; } + +/** + * fastboot_get_buf_addr() - Return address of the fastboot transfer buffer + * + * Return: Address of the fastboot transfer buffer + */ +void *fastboot_get_buf_addr(void) +{ + return fastboot_buf_addr; +} + +/** + * fastboot_get_buf_size() - Return size of the fastboot transfer buffer + * + * Return: Size of the fastboot transfer buffer + */ +u32 fastboot_get_buf_size(void) +{ + return fastboot_buf_size; +} + +/** + * fastboot_get_progress_callback() - Return progress callback + * + * Return: Pointer to function called during long operations + */ +void (*fastboot_get_progress_callback(void))(const char *) +{ + return fastboot_progress_callback; +} + +/** + * fastboot_boot() - Execute fastboot boot command + * + * If ${fastboot_bootcmd} is set, run that command to execute the boot + * process, if that returns, then exit the fastboot server and return + * control to the caller. + * + * Otherwise execute "bootm ", if that fails, reset + * the board. + */ +void fastboot_boot(void) +{ + char *s; + + s = env_get("fastboot_bootcmd"); + if (s) { + run_command(s, CMD_FLAG_ENV); + } else { + static char boot_addr_start[12]; + static char *const bootm_args[] = { + "bootm", boot_addr_start, NULL + }; + + snprintf(boot_addr_start, sizeof(boot_addr_start) - 1, + "0x%p", fastboot_buf_addr); + printf("Booting kernel at %s...\n\n\n", boot_addr_start); + + do_bootm(NULL, 0, 2, bootm_args); + + /* + * This only happens if image is somehow faulty so we start + * over. We deliberately leave this policy to the invocation + * of fastbootcmd if that's what's being run + */ + do_reset(NULL, 0, 0, NULL); + } +} + +/** + * fastboot_set_progress_callback() - set progress callback + * + * @progress: Pointer to progress callback + * + * Set a callback which is invoked periodically during long running operations + * (flash and erase). This can be used (for example) by the UDP transport to + * send INFO responses to keep the client alive whilst those commands are + * executing. + */ +void fastboot_set_progress_callback(void (*progress)(const char *msg)) +{ + fastboot_progress_callback = progress; +} + +/* + * fastboot_init() - initialise new fastboot protocol session + * + * @buf_addr: Pointer to download buffer, or NULL for default + * @buf_size: Size of download buffer, or zero for default + */ +void fastboot_init(void *buf_addr, u32 buf_size) +{ + fastboot_buf_addr = buf_addr ? buf_addr : + (void *)CONFIG_FASTBOOT_BUF_ADDR; + fastboot_buf_size = buf_size ? buf_size : CONFIG_FASTBOOT_BUF_SIZE; + fastboot_set_progress_callback(NULL); +} diff --git a/drivers/fastboot/fb_getvar.c b/drivers/fastboot/fb_getvar.c new file mode 100644 index 0000000..736efbf --- /dev/null +++ b/drivers/fastboot/fb_getvar.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2016 The Android Open Source Project + */ + +#include +#include +#include +#include +#include +#include + +static void fb_getvar_version(char *var_parameter, char *response); +static void fb_getvar_bootloader_version(char *var_parameter, char *response); +static void fb_getvar_downloadsize(char *var_parameter, char *response); +static void fb_getvar_serialno(char *var_parameter, char *response); +static void fb_getvar_version_baseband(char *var_parameter, char *response); +static void fb_getvar_product(char *var_parameter, char *response); +static void fb_getvar_current_slot(char *var_parameter, char *response); +static void fb_getvar_slot_suffixes(char *var_parameter, char *response); +static void fb_getvar_has_slot(char *var_parameter, char *response); +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) +static void fb_getvar_partition_type(char *part_name, char *response); +#endif +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) +static void fb_getvar_partition_size(char *part_name, char *response); +#endif + +static const struct { + const char *variable; + void (*dispatch)(char *var_parameter, char *response); +} fb_getvar_dispatch[] = { + { + .variable = "version", + .dispatch = fb_getvar_version + }, { + .variable = "bootloader-version", + .dispatch = fb_getvar_bootloader_version + }, { + .variable = "version-bootloader", + .dispatch = fb_getvar_bootloader_version + }, { + .variable = "downloadsize", + .dispatch = fb_getvar_downloadsize + }, { + .variable = "max-download-size", + .dispatch = fb_getvar_downloadsize + }, { + .variable = "serialno", + .dispatch = fb_getvar_serialno + }, { + .variable = "version-baseband", + .dispatch = fb_getvar_version_baseband + }, { + .variable = "product", + .dispatch = fb_getvar_product + }, { + .variable = "current-slot", + .dispatch = fb_getvar_current_slot + }, { + .variable = "slot-suffixes", + .dispatch = fb_getvar_slot_suffixes + }, { + .variable = "has_slot", + .dispatch = fb_getvar_has_slot +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) + }, { + .variable = "partition-type", + .dispatch = fb_getvar_partition_type +#endif +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) + }, { + .variable = "partition-size", + .dispatch = fb_getvar_partition_size +#endif + } +}; + +static void fb_getvar_version(char *var_parameter, char *response) +{ + fastboot_okay(FASTBOOT_VERSION, response); +} + +static void fb_getvar_bootloader_version(char *var_parameter, char *response) +{ + fastboot_okay(U_BOOT_VERSION, response); +} + +static void fb_getvar_downloadsize(char *var_parameter, char *response) +{ + fastboot_response("OKAY", response, "0x%08x", fastboot_get_buf_size()); +} + +static void fb_getvar_serialno(char *var_parameter, char *response) +{ + const char *tmp = env_get("serial#"); + + if (tmp) + fastboot_okay(tmp, response); + else + fastboot_fail("Value not set", response); +} + +static void fb_getvar_version_baseband(char *var_parameter, char *response) +{ + fastboot_okay("N/A", response); +} + +static void fb_getvar_product(char *var_parameter, char *response) +{ + const char *board = env_get("board"); + + if (board) + fastboot_okay(board, response); + else + fastboot_fail("Board not set", response); +} + +static void fb_getvar_current_slot(char *var_parameter, char *response) +{ + /* A/B not implemented, for now always return _a */ + fastboot_okay("_a", response); +} + +static void fb_getvar_slot_suffixes(char *var_parameter, char *response) +{ + fastboot_okay("_a,_b", response); +} + +static void fb_getvar_has_slot(char *part_name, char *response) +{ + if (part_name && (!strcmp(part_name, "boot") || + !strcmp(part_name, "system"))) + fastboot_okay("yes", response); + else + fastboot_okay("no", response); +} + +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) +static void fb_getvar_partition_type(char *part_name, char *response) +{ + int r; + struct blk_desc *dev_desc; + disk_partition_t part_info; + + r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info, + response); + if (r >= 0) { + r = fs_set_blk_dev_with_part(dev_desc, r); + if (r < 0) + fastboot_fail("failed to set partition", response); + else + fastboot_okay(fs_get_type_name(), response); + } +} +#endif + +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) +static void fb_getvar_partition_size(char *part_name, char *response) +{ + int r; + size_t size; + +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) + struct blk_desc *dev_desc; + disk_partition_t part_info; + + r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info, + response); + if (r >= 0) + size = part_info.size; +#endif +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) + struct part_info *part_info; + + r = fastboot_nand_get_part_info(part_name, &part_info, response); + if (r >= 0) + size = part_info->size; +#endif + if (r >= 0) + fastboot_response("OKAY", response, "0x%016zx", size); +} +#endif + +/** + * fastboot_getvar() - Writes variable indicated by cmd_parameter to response. + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + * + * Look up cmd_parameter first as an environment variable of the form + * fastboot., if that exists return use its value to set + * response. + * + * Otherwise lookup the name of variable and execute the appropriate + * function to return the requested value. + */ +void fastboot_getvar(char *cmd_parameter, char *response) +{ + if (!cmd_parameter) { + fastboot_fail("missing var", response); + } else { +#define FASTBOOT_ENV_PREFIX "fastboot." + int i; + char *var_parameter = cmd_parameter; + char envstr[FASTBOOT_RESPONSE_LEN]; + const char *s; + + snprintf(envstr, sizeof(envstr) - 1, + FASTBOOT_ENV_PREFIX "%s", cmd_parameter); + s = env_get(envstr); + if (s) { + fastboot_response("OKAY", response, "%s", s); + return; + } + + strsep(&var_parameter, ":"); + for (i = 0; i < ARRAY_SIZE(fb_getvar_dispatch); ++i) { + if (!strcmp(fb_getvar_dispatch[i].variable, + cmd_parameter)) { + fb_getvar_dispatch[i].dispatch(var_parameter, + response); + return; + } + } + pr_warn("WARNING: unknown variable: %s\n", cmd_parameter); + fastboot_fail("Variable not implemented", response); + } +} diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index 038905f..0b8794a 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -15,6 +15,8 @@ #include #include +#define FASTBOOT_MAX_BLK_WRITE 16384 + #define BOOT_PARTITION_NAME "boot" struct fb_mmc_sparse { @@ -43,13 +45,49 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc, return ret; } +/** + * fb_mmc_blk_write() - Write/erase MMC in chunks of FASTBOOT_MAX_BLK_WRITE + * + * @block_dev: Pointer to block device + * @start: First block to write/erase + * @blkcnt: Count of blocks + * @buffer: Pointer to data buffer for write or NULL for erase + */ +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; + void (*progress)(const char *) = fastboot_get_progress_callback(); + + for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) { + cur_blkcnt = min((int)blkcnt - i, FASTBOOT_MAX_BLK_WRITE); + if (buffer) { + if (progress) + progress("writing"); + blks_written = blk_dwrite(block_dev, blk, cur_blkcnt, + buffer + (i * block_dev->blksz)); + } else { + if (progress) + progress("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, @@ -60,7 +98,7 @@ static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info, static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info, const char *part_name, void *buffer, - unsigned int download_bytes, char *response) + u32 download_bytes, char *response) { lbaint_t blkcnt; lbaint_t blks; @@ -77,7 +115,8 @@ 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); @@ -148,7 +187,7 @@ static lbaint_t fb_mmc_get_boot_header(struct blk_desc *dev_desc, */ static int fb_mmc_update_zimage(struct blk_desc *dev_desc, void *download_buffer, - unsigned int download_bytes, + u32 download_bytes, char *response) { uintptr_t hdr_addr; /* boot image header address */ @@ -251,8 +290,32 @@ static int fb_mmc_update_zimage(struct blk_desc *dev_desc, } #endif +int fastboot_mmc_get_part_info(char *part_name, struct blk_desc **dev_desc, + disk_partition_t *part_info, char *response) +{ + int r; + + *dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV); + if (!*dev_desc) { + fastboot_fail("block device not found", response); + return -ENOENT; + } + if (!part_name) { + fastboot_fail("partition not found", response); + return -ENOENT; + } + + r = part_get_info_by_name_or_alias(*dev_desc, part_name, part_info); + if (r < 0) { + fastboot_fail("partition not found", response); + return r; + } + + return r; +} + void fb_mmc_flash_write(const char *cmd, void *download_buffer, - unsigned int download_bytes, char *response) + u32 download_bytes, char *response) { struct blk_desc *dev_desc; disk_partition_t info; @@ -389,7 +452,8 @@ 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\n", dev_desc->devnum); fastboot_fail("failed erasing from device", response); diff --git a/drivers/fastboot/fb_nand.c b/drivers/fastboot/fb_nand.c index 849a6f1..a7cbc34 100644 --- a/drivers/fastboot/fb_nand.c +++ b/drivers/fastboot/fb_nand.c @@ -88,7 +88,7 @@ static int _fb_nand_erase(struct mtd_info *mtd, struct part_info *part) } static int _fb_nand_write(struct mtd_info *mtd, struct part_info *part, - void *buffer, unsigned int offset, + void *buffer, u32 offset, size_t length, size_t *written) { int flags = WITH_WR_VERIFY; @@ -145,8 +145,16 @@ static lbaint_t fb_nand_sparse_reserve(struct sparse_storage *info, return blkcnt + bad_blocks; } +int fastboot_nand_get_part_info(char *part_name, struct part_info **part_info, + char *response) +{ + struct mtd_info *mtd = NULL; + + return fb_nand_lookup(part_name, &mtd, part_info, response); +} + void fb_nand_flash_write(const char *cmd, void *download_buffer, - unsigned int download_bytes, char *response) + u32 download_bytes, char *response) { struct part_info *part; struct mtd_info *mtd = NULL; diff --git a/include/fastboot.h b/include/fastboot.h index 593e6a7..6473ec9 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -15,13 +15,170 @@ #define FASTBOOT_VERSION "0.4" /* The 64 defined bytes plus \0 */ +#define FASTBOOT_COMMAND_LEN (64 + 1) #define FASTBOOT_RESPONSE_LEN (64 + 1) +/** + * All known commands to fastboot + */ +enum { + FASTBOOT_COMMAND_GETVAR = 0, + FASTBOOT_COMMAND_DOWNLOAD, +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) + FASTBOOT_COMMAND_FLASH, + FASTBOOT_COMMAND_ERASE, +#endif + FASTBOOT_COMMAND_BOOT, + FASTBOOT_COMMAND_CONTINUE, + FASTBOOT_COMMAND_REBOOT, + FASTBOOT_COMMAND_REBOOT_BOOTLOADER, + FASTBOOT_COMMAND_SET_ACTIVE, +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) + FASTBOOT_COMMAND_OEM_FORMAT, +#endif + + FASTBOOT_COMMAND_COUNT +}; + +/** + * fastboot_response() - Writes a response of the form "$tag$reason". + * + * @param tag The first part of the response + * @param response Pointer to fastboot response buffer + * @param format printf style format string + */ void fastboot_response(const char *tag, char *response, const char *format, ...) __attribute__ ((format (__printf__, 3, 4))); +/** + * fastboot_fail() - Write a FAIL response of the form "FAIL$reason". + * + * @param reason Pointer to returned reason string + * @param response Pointer to fastboot response buffer + */ void fastboot_fail(const char *reason, char *response); + +/** + * fastboot_okay() - Write an OKAY response of the form "OKAY$reason". + * + * @param reason Pointer to returned reason string. + * Can be NULL to send a bare "OKAY" response. + * @param response Pointer to fastboot response buffer + */ void fastboot_okay(const char *reason, char *response); + +/** + * fastboot_set_reboot_flag() - Set flag to indicate reboot-bootloader + * + * Set flag which indicates that we should reboot into the bootloader + * following the reboot that fastboot executes after this function. + * + * This function should be overridden in your board file with one + * which sets whatever flag your board specific Android bootloader flow + * requires in order to re-enter the bootloader. + */ int fastboot_set_reboot_flag(void); + +/** + * fastboot_get_buf_addr() - Return address of the fastboot transfer buffer + * + * Return: Address of the fastboot transfer buffer + */ +void *fastboot_get_buf_addr(void); + +/** + * fastboot_get_buf_size() - Return size of the fastboot transfer buffer + * + * Return: Size of the fastboot transfer buffer + */ +u32 fastboot_get_buf_size(void); + +/** + * fastboot_get_bytes_remaining() - return bytes remaining in current transfer + * + * Return: Number of bytes left in the current download + */ +u32 fastboot_get_bytes_remaining(void); + +/** + * fastboot_get_progress_callback() - Return progress callback + * + * Return: Pointer to function called during long operations + */ +void (*fastboot_get_progress_callback(void))(const char *); + +/** + * fastboot_getvar() - Writes variable indicated by cmd_parameter to response. + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + * + * Look up cmd_parameter first as an environment variable of the form + * fastboot., if that exists return use its value to set + * response. + * + * Otherwise lookup the name of variable and execute the appropriate + * function to return the requested value. + */ +void fastboot_getvar(char *cmd_parameter, char *response); + +/** + * fastboot_set_progress_callback() - set progress callback + * + * @progress: Pointer to progress callback + * + * Set a callback which is invoked periodically during long running operations + * (flash and erase). This can be used (for example) by the UDP transport to + * send INFO responses to keep the client alive whilst those commands are + * executing. + */ +void fastboot_set_progress_callback(void (*progress)(const char *msg)); + +/* + * fastboot_init() - initialise new fastboot protocol session + * + * @buf_addr: Pointer to download buffer, or NULL for default + * @buf_size: Size of download buffer, or zero for default + */ +void fastboot_init(void *buf_addr, u32 buf_size); + +/** + * fastboot_boot() - Execute fastboot boot command + * + * If ${fastboot_bootcmd} is set, run that command to execute the boot + * process, if that returns, then exit the fastboot server and return + * control to the caller. + * + * Otherwise execute "bootm ", if that fails, reset + * the board. + */ +void fastboot_boot(void); + +/** + * fastboot_handle_command() - Handle fastboot command + * + * @cmd_string: Pointer to command string + * @response: Pointer to fastboot response buffer + * + * Return: Executed command, or -1 if not recognized + */ +int fastboot_handle_command(char *cmd_string, char *response); + +/** + * fastboot_download_data() - Copy image data to fastboot_buf_addr. + * + * @fastboot_data: Pointer to received fastboot data + * @fastboot_data_len: Length of received fastboot data + * @response: Pointer to fastboot response buffer + * + * Copies image data from fastboot_data to fastboot_buf_addr. Writes to + * response. fastboot_bytes_received is updated to indicate the number + * of bytes that have been transferred. + * + * On completion sets image_size and ${filesize} to the total size of the + * downloaded image. + */ +void fastboot_download_data(const void *fastboot_data, + unsigned int fastboot_data_len, char *response); #endif /* _FASTBOOT_H_ */ diff --git a/include/fb_mmc.h b/include/fb_mmc.h index 39a960c..ae01f99 100644 --- a/include/fb_mmc.h +++ b/include/fb_mmc.h @@ -3,6 +3,12 @@ * Copyright 2014 Broadcom Corporation. */ +#ifndef _FB_MMC_H_ +#define _FB_MMC_H_ + +int fastboot_mmc_get_part_info(char *part_name, struct blk_desc **dev_desc, + disk_partition_t *part_info, char *response); void fb_mmc_flash_write(const char *cmd, void *download_buffer, - unsigned int download_bytes, char *response); + u32 download_bytes, char *response); void fb_mmc_erase(const char *cmd, char *response); +#endif diff --git a/include/fb_nand.h b/include/fb_nand.h index 2c92a4e..937f9e4 100644 --- a/include/fb_nand.h +++ b/include/fb_nand.h @@ -4,6 +4,14 @@ * Copyright 2015 Free Electrons. */ +#ifndef _FB_NAND_H_ +#define _FB_NAND_H_ + +#include + +int fastboot_nand_get_part_info(char *part_name, struct part_info **part_info, + char *response); void fb_nand_flash_write(const char *cmd, void *download_buffer, - unsigned int download_bytes, char *response); + u32 download_bytes, char *response); void fb_nand_erase(const char *cmd, char *response); +#endif diff --git a/include/net.h b/include/net.h index 65f51d7..5760685 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 */ diff --git a/include/net/fastboot.h b/include/net/fastboot.h new file mode 100644 index 0000000..6860209 --- /dev/null +++ b/include/net/fastboot.h @@ -0,0 +1,21 @@ +/* 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); + +/**********************************************************************/ + +#endif /* __NET_FASTBOOT_H__ */ diff --git a/net/Makefile b/net/Makefile index d1e8e01..0746687 100644 --- a/net/Makefile +++ b/net/Makefile @@ -23,6 +23,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..3c9f71e --- /dev/null +++ b/net/fastboot.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2016 The Android Open Source Project + */ + +#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 DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header)) + +/* 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; + +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 boot_downloaded_image(void); + +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) +/** + * fastboot_udp_send_info() - Send an INFO packet during long commands. + * + * @msg: String describing the reason for waiting + */ +static void fastboot_udp_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 */ + fastboot_response("INFO", response, "%s", msg); + 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); +} + +/** + * fastboot_timed_send_info() - Send INFO packet every 30 seconds + * + * @msg: String describing the reason for waiting + * + * Send an INFO packet during long commands based on timer. An INFO packet + * is sent if the time is 30 seconds after start. Else, noop. + */ +static void fastboot_timed_send_info(const char *msg) +{ + static ulong start; + + /* 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_udp_send_info(msg); + } +} +#endif + +/** + * fastboot_send() - Sends a packet in response to received fastboot packet + * + * @fb_header: Header for response packet + * @fastboot_data: Pointer to received fastboot data + * @fastboot_data_len: Length of received fastboot data + * @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; + static char command[FASTBOOT_COMMAND_LEN]; + static int cmd = -1; + static bool pending_command; + 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 == FASTBOOT_COMMAND_DOWNLOAD) { + fastboot_download_data(fastboot_data, fastboot_data_len, + response); + } else if (!pending_command) { + strlcpy(command, fastboot_data, + min((size_t)fastboot_data_len + 1, + sizeof(command))); + pending_command = true; + } else { + cmd = fastboot_handle_command(command, response); + pending_command = false; + } + /* + * 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)) { + switch (cmd) { + case FASTBOOT_COMMAND_BOOT: + boot_downloaded_image(); + break; + + case FASTBOOT_COMMAND_CONTINUE: + net_set_state(NETLOOP_SUCCESS); + break; + + case FASTBOOT_COMMAND_REBOOT: + case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: + do_reset(NULL, 0, 0, NULL); + break; + } + } + + if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) + cmd = -1; +} + +/** + * boot_downloaded_image() - Boots into downloaded image. + */ +static void boot_downloaded_image(void) +{ + fastboot_boot(); + net_set_state(NETLOOP_SUCCESS); +} + +/** + * fastboot_handler() - Incoming UDP packet handler. + * + * @packet: Pointer to incoming UDP packet + * @dport: Destination UDP port + * @sip: Source IP address + * @sport: Source UDP port + * @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 < sizeof(struct fastboot_header) || 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; + + fastboot_set_progress_callback(fastboot_timed_send_info); + 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 7f85211..a4932f4 100644 --- a/net/net.c +++ b/net/net.c @@ -87,6 +87,7 @@ #include #include #include +#include #include #if defined(CONFIG_LED_STATUS) #include @@ -451,6 +452,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(); @@ -1322,6 +1328,7 @@ common: /* Fall through */ case NETCONS: + case FASTBOOT: case TFTPSRV: if (net_ip.s_addr == 0) { puts("*** ERROR: `ipaddr' not set\n"); -- 2.7.4