From 2879373370eb3685cd6622f5d800e9f72ec81df1 Mon Sep 17 00:00:00 2001 From: Edmund Henniges Date: Thu, 14 May 2020 20:21:54 +0200 Subject: fastboot: split generic code from USB gadget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fastboot specification describes other protocols beyond USB. Allow these to reuse the generic parts of the existing fastboot code when they are implemented. Most of the changes in common/fastboot.c are due to the renaming of struct f_fastboot *f_fb to struct fastboot *fb. Signed-off-by: Edmund Henniges Signed-off-by: Daniel Glöckner Signed-off-by: Sascha Hauer --- common/Kconfig | 40 ++ common/Makefile | 1 + common/fastboot.c | 957 +++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/Kconfig | 36 +- drivers/usb/gadget/f_fastboot.c | 970 ++-------------------------------------- drivers/usb/gadget/multi.c | 5 +- include/fastboot.h | 66 +++ include/usb/fastboot.h | 34 +- include/usb/gadget-multi.h | 2 +- 9 files changed, 1112 insertions(+), 999 deletions(-) create mode 100644 common/fastboot.c create mode 100644 include/fastboot.h diff --git a/common/Kconfig b/common/Kconfig index 460ac487cb..18796c6888 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -113,6 +113,9 @@ config USBGADGET_START config BOOT bool +config FASTBOOT_BASE + bool + menu "General Settings" config LOCALVERSION @@ -1009,6 +1012,43 @@ config PBL_OPTEE endmenu +if FASTBOOT_BASE + +menu "Android Fastboot" + +config FASTBOOT_SPARSE + bool + select IMAGE_SPARSE + prompt "Enable Fastboot sparse image support" + help + Sparse images are a way for the fastboot protocol to write + images that are bigger than the available memory. If unsure, + say yes here. + +config FASTBOOT_BUF + bool + prompt "Download files to temporary buffer instead of file" + help + With this option enabled the fastboot code will download files to a + temporary buffer instead of a temporary file. Normally you want to + use a file as this also works when your memory is fragmented. However, + in some special cases, when the file consumer also better copes with + a buffer, then using a buffer might be better. + + Say no here unless you know what you are doing. + +config FASTBOOT_CMD_OEM + bool + prompt "Enable OEM commands" + help + This option enables the fastboot "oem" group of commands. They allow to + executing arbitrary barebox commands and may be disabled in secure + environments. + +endmenu + +endif + endmenu menu "Debugging" diff --git a/common/Makefile b/common/Makefile index c14af692f9..53859d8d14 100644 --- a/common/Makefile +++ b/common/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o obj-$(CONFIG_USBGADGET_START) += usbgadget.o pbl-$(CONFIG_PBL_OPTEE) += optee.o obj-$(CONFIG_BOOTM_OPTEE) += optee.o +obj-$(CONFIG_FASTBOOT_BASE) += fastboot.o ifdef CONFIG_PASSWORD diff --git a/common/fastboot.c b/common/fastboot.c new file mode 100644 index 0000000000..d58f68f1bb --- /dev/null +++ b/common/fastboot.c @@ -0,0 +1,957 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, + * Tom Rix + * + * Copyright 2011 Sebastian Andrzej Siewior + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring + * + * Copyright 2014 Sascha Hauer + * Ported to barebox + * + * Copyright 2020 Edmund Henniges + * Copyright 2020 Daniel Glöckner + * Split off of generic parts + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define pr_fmt(fmt) "fastboot: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FASTBOOT_VERSION "0.4" + +#define FASTBOOT_TMPFILE "/.fastboot.img" + +static unsigned int fastboot_max_download_size = SZ_8M; + +struct fb_variable { + char *name; + char *value; + struct list_head list; +}; + +static inline bool fastboot_download_to_buf(struct fastboot *fb) +{ + if (IS_ENABLED(CONFIG_FASTBOOT_BUF)) + return true; + else + return false; +} + +static void fb_setvar(struct fb_variable *var, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + var->value = bvasprintf(fmt, ap); + va_end(ap); +} + +static struct fb_variable *fb_addvar(struct fastboot *fb, const char *fmt, ...) +{ + struct fb_variable *var = xzalloc(sizeof(*var)); + va_list ap; + + va_start(ap, fmt); + var->name = bvasprintf(fmt, ap); + va_end(ap); + + list_add_tail(&var->list, &fb->variables); + + return var; +} + +static int fastboot_add_partition_variables(struct fastboot *fb, + struct file_list_entry *fentry) +{ + struct stat s; + size_t size = 0; + int fd, ret; + struct mtd_info_user mtdinfo; + char *type = NULL; + struct fb_variable *var; + + ret = stat(fentry->filename, &s); + if (ret) { + device_detect_by_name(devpath_to_name(fentry->filename)); + ret = stat(fentry->filename, &s); + } + + if (ret) { + if (fentry->flags & FILE_LIST_FLAG_CREATE) { + ret = 0; + type = "file"; + goto out; + } + + goto out; + } + + fd = open(fentry->filename, O_RDWR); + if (fd < 0) { + ret = -EINVAL; + goto out; + } + + size = s.st_size; + + ret = ioctl(fd, MEMGETINFO, &mtdinfo); + + close(fd); + + if (!ret) { + switch (mtdinfo.type) { + case MTD_NANDFLASH: + type = "NAND-flash"; + break; + case MTD_NORFLASH: + type = "NOR-flash"; + break; + case MTD_UBIVOLUME: + type = "UBI"; + break; + default: + type = "flash"; + break; + } + + goto out; + } + + type = "basic"; + ret = 0; + +out: + if (ret) + return ret; + + var = fb_addvar(fb, "partition-size:%s", fentry->name); + fb_setvar(var, "%08zx", size); + var = fb_addvar(fb, "partition-type:%s", fentry->name); + fb_setvar(var, "%s", type); + + return ret; +} + +static int fastboot_add_bbu_variables(struct bbu_handler *handler, void *ctx) +{ + struct fastboot *fb = ctx; + char *name; + int ret; + + name = basprintf("bbu-%s", handler->name); + + ret = file_list_add_entry(fb->files, name, handler->devicefile, 0); + + free(name); + + return ret; +} + +int fastboot_generic_init(struct fastboot *fb, bool export_bbu) +{ + int ret; + struct file_list_entry *fentry; + struct fb_variable *var; + + var = fb_addvar(fb, "version"); + fb_setvar(var, "0.4"); + var = fb_addvar(fb, "bootloader-version"); + fb_setvar(var, release_string); + if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) { + var = fb_addvar(fb, "max-download-size"); + fb_setvar(var, "%u", fastboot_max_download_size); + } + + if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && export_bbu) + bbu_handlers_iterate(fastboot_add_bbu_variables, fb); + + file_list_for_each_entry(fb->files, fentry) { + ret = fastboot_add_partition_variables(fb, fentry); + if (ret) + return ret; + } + + return 0; +} + +void fastboot_generic_free(struct fastboot *fb) +{ + struct fb_variable *var, *tmp; + + list_for_each_entry_safe(var, tmp, &fb->variables, list) { + free(var->name); + free(var->value); + list_del(&var->list); + free(var); + } + + fb->active = false; +} + +static struct fastboot *g_fb; + +void fastboot_generic_close(struct fastboot *fb) +{ + if (g_fb == fb) + g_fb = NULL; +} + +/* + * A "oem exec bootm" or similar commands will stop barebox. Tell the + * fastboot command on the other side so that it doesn't run into a + * timeout. + */ +static void fastboot_shutdown(void) +{ + struct fastboot *fb = g_fb; + + if (!fb || !fb->active) + return; + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "barebox shutting down"); + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +} + +early_exitcall(fastboot_shutdown); + +static char *fastboot_msg[] = { + [FASTBOOT_MSG_OKAY] = "OKAY", + [FASTBOOT_MSG_FAIL] = "FAIL", + [FASTBOOT_MSG_INFO] = "INFO", + [FASTBOOT_MSG_DATA] = "DATA", +}; + +int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type, + const char *fmt, ...) +{ + struct va_format vaf; + char buf[64]; + va_list ap; + int n; + const char *msg = fastboot_msg[type]; + + va_start(ap, fmt); + vaf.fmt = fmt; + vaf.va = ≈ + + n = snprintf(buf, 64, "%s%pV", msg, &vaf); + + switch (type) { + case FASTBOOT_MSG_OKAY: + fb->active = false; + break; + case FASTBOOT_MSG_FAIL: + fb->active = false; + pr_err("%pV\n", &vaf); + break; + case FASTBOOT_MSG_INFO: + pr_info("%pV\n", &vaf); + break; + case FASTBOOT_MSG_DATA: + break; + } + + va_end(ap); + + if (n > 64) + n = 64; + + return fb->write(fb, buf, n); +} + +static void cb_reboot(struct fastboot *fb, const char *cmd) +{ + restart_machine(); +} + +static int strcmp_l1(const char *s1, const char *s2) +{ + if (!s1 || !s2) + return -1; + return strncmp(s1, s2, strlen(s1)); +} + +static void cb_getvar(struct fastboot *fb, const char *cmd) +{ + struct fb_variable *var; + + pr_debug("getvar: \"%s\"\n", cmd); + + if (!strcmp_l1(cmd, "all")) { + list_for_each_entry(var, &fb->variables, list) { + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "%s: %s", + var->name, var->value); + } + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); + return; + } + + list_for_each_entry(var, &fb->variables, list) { + if (!strcmp(cmd, var->name)) { + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, var->value); + return; + } + } + + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +} + +int fastboot_handle_download_data(struct fastboot *fb, const void *buffer, + unsigned int len) +{ + int ret; + + if (fastboot_download_to_buf(fb)) { + memcpy(fb->buf + fb->download_bytes, buffer, len); + } else { + ret = write(fb->download_fd, buffer, len); + if (ret < 0) + return ret; + } + + fb->download_bytes += len; + show_progress(fb->download_bytes); + return 0; +} + +void fastboot_download_finished(struct fastboot *fb) +{ + close(fb->download_fd); + + printf("\n"); + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Downloading %d bytes finished", + fb->download_bytes); + + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +} + +static void cb_download(struct fastboot *fb, const char *cmd) +{ + fb->download_size = simple_strtoul(cmd, NULL, 16); + fb->download_bytes = 0; + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Downloading %d bytes...", + fb->download_size); + + init_progression_bar(fb->download_size); + + if (fastboot_download_to_buf(fb)) { + free(fb->buf); + fb->buf = malloc(fb->download_size); + if (!fb->buf) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "not enough memory"); + return; + } + } else { + fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC); + if (fb->download_fd < 0) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "internal error"); + return; + } + } + + if (!fb->download_size) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "data invalid size"); + else + fb->start_download(fb); +} + +void fastboot_start_download_generic(struct fastboot *fb) +{ + fastboot_tx_print(fb, FASTBOOT_MSG_DATA, "%08x", fb->download_size); +} + +static void __maybe_unused cb_boot(struct fastboot *fb, const char *opt) +{ + int ret; + struct bootm_data data = { + .initrd_address = UIMAGE_INVALID_ADDRESS, + .os_address = UIMAGE_SOME_ADDRESS, + }; + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Booting kernel..\n"); + + globalvar_set_match("linux.bootargs.dyn.", ""); + globalvar_set_match("bootm.image", ""); + + data.os_file = FASTBOOT_TMPFILE; + + ret = bootm_boot(&data); + + if (ret) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "Booting failed: %s", + strerror(-ret)); + else + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +} + +static struct mtd_info *get_mtd(struct fastboot *fb, const char *filename) +{ + int fd, ret; + struct mtd_info_user meminfo; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return ERR_PTR(-errno); + + ret = ioctl(fd, MEMGETINFO, &meminfo); + + close(fd); + + if (ret) + return ERR_PTR(ret); + + return meminfo.mtd; +} + +static int do_ubiformat(struct fastboot *fb, struct mtd_info *mtd, + const char *file, const void *buf, size_t len) +{ + struct ubiformat_args args = { + .yes = 1, + .image = file, + .image_buf = buf, + .image_size = len, + }; + + if (!file) + args.novtbl = 1; + + if (!IS_ENABLED(CONFIG_UBIFORMAT)) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "ubiformat is not available"); + return -ENODEV; + } + + return ubiformat(mtd, &args); +} + + +static int check_ubi(struct fastboot *fb, struct file_list_entry *fentry, + enum filetype filetype) +{ + struct mtd_info *mtd; + + mtd = get_mtd(fb, fentry->filename); + + /* + * Issue a warning when we are about to write a UBI image to a MTD device + * and the FILE_LIST_FLAG_UBI is not given as this means we loose all + * erase counters. + */ + if (!IS_ERR(mtd) && filetype == filetype_ubi && + !(fentry->flags & FILE_LIST_FLAG_UBI)) { + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, + "writing UBI image to MTD device, " + "add the 'u' "); + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, + "flag to the partition description"); + return 0; + } + + if (!(fentry->flags & FILE_LIST_FLAG_UBI)) + return 0; + + if (!IS_ENABLED(CONFIG_UBIFORMAT)) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "ubiformat not available"); + return -ENOSYS; + } + + if (IS_ERR(mtd)) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "UBI flag given on non-MTD device"); + return -EINVAL; + } + + if (filetype == filetype_ubi) { + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, + "This is a UBI image..."); + return 1; + } else { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "This is no UBI image but %s", + file_type_to_string(filetype)); + return -EINVAL; + } +} + +static int fastboot_handle_sparse(struct fastboot *fb, + struct file_list_entry *fentry) +{ + struct sparse_image_ctx *sparse; + void *buf = NULL; + int ret, fd; + unsigned int flags = O_RDWR; + int bufsiz = SZ_128K; + struct stat s; + struct mtd_info *mtd = NULL; + + ret = stat(fentry->filename, &s); + if (ret) { + if (fentry->flags & FILE_LIST_FLAG_CREATE) + flags |= O_CREAT; + else + return ret; + } + + fd = open(fentry->filename, flags); + if (fd < 0) + return -errno; + + ret = fstat(fd, &s); + if (ret) + goto out_close_fd; + + sparse = sparse_image_open(FASTBOOT_TMPFILE); + if (IS_ERR(sparse)) { + pr_err("Cannot open sparse image\n"); + ret = PTR_ERR(sparse); + goto out_close_fd; + } + + if (S_ISREG(s.st_mode)) { + ret = ftruncate(fd, sparse_image_size(sparse)); + if (ret) + goto out; + } + + buf = malloc(bufsiz); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + if (fentry->flags & FILE_LIST_FLAG_UBI) { + mtd = get_mtd(fb, fentry->filename); + if (IS_ERR(mtd)) { + ret = PTR_ERR(mtd); + goto out; + } + } + + while (1) { + int retlen; + loff_t pos; + + ret = sparse_image_read(sparse, buf, &pos, bufsiz, &retlen); + if (ret) + goto out; + if (!retlen) + break; + + if (pos == 0) { + ret = check_ubi(fb, fentry, file_detect_type(buf, retlen)); + if (ret < 0) + goto out; + } + + if (fentry->flags & FILE_LIST_FLAG_UBI) { + if (!IS_ENABLED(CONFIG_UBIFORMAT)) { + ret = -ENOSYS; + goto out; + } + + if (pos == 0) { + ret = do_ubiformat(fb, mtd, NULL, NULL, 0); + if (ret) + goto out; + } + + ret = ubiformat_write(mtd, buf, retlen, pos); + if (ret) + goto out; + } else { + discard_range(fd, retlen, pos); + + pos = lseek(fd, pos, SEEK_SET); + if (pos == -1) { + ret = -errno; + goto out; + } + + ret = write_full(fd, buf, retlen); + if (ret < 0) + goto out; + } + } + + ret = 0; + +out: + free(buf); + sparse_image_close(sparse); +out_close_fd: + close(fd); + + return ret; +} + +static void cb_flash(struct fastboot *fb, const char *cmd) +{ + struct file_list_entry *fentry; + int ret; + const char *filename = NULL, *sourcefile; + enum filetype filetype; + + if (fastboot_download_to_buf(fb)) { + sourcefile = NULL; + filetype = file_detect_type(fb->buf, fb->download_bytes); + } else { + sourcefile = FASTBOOT_TMPFILE; + filetype = file_name_detect_type(FASTBOOT_TMPFILE); + } + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Copying file to %s...", + cmd); + + fentry = file_list_entry_by_name(fb->files, cmd); + + if (!fentry) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "No such partition: %s", + cmd); + ret = -ENOENT; + goto out; + } + + if (fb->cmd_flash) { + ret = fb->cmd_flash(fb, fentry, sourcefile, fb->buf, + fb->download_size); + if (ret != FASTBOOT_CMD_FALLTHROUGH) + goto out; + } + + filename = fentry->filename; + + if (filetype == filetype_android_sparse) { + if (!IS_ENABLED(CONFIG_FASTBOOT_SPARSE) || + fastboot_download_to_buf(fb)) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "sparse image not supported"); + ret = -EOPNOTSUPP; + goto out; + } + + ret = fastboot_handle_sparse(fb, fentry); + if (ret) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "writing sparse image: %s", + strerror(-ret)); + + goto out; + } + + ret = check_ubi(fb, fentry, filetype); + if (ret < 0) + goto out; + + if (ret > 0) { + struct mtd_info *mtd; + + mtd = get_mtd(fb, fentry->filename); + + ret = do_ubiformat(fb, mtd, sourcefile, fb->buf, + fb->download_size); + if (ret) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "write partition: %s", + strerror(-ret)); + goto out; + } + + goto out; + } + + if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && filetype_is_barebox_image(filetype)) { + struct bbu_handler *handler; + struct bbu_data data = { + .devicefile = filename, + .flags = BBU_FLAG_YES, + }; + + handler = bbu_find_handler_by_device(data.devicefile); + if (!handler) + goto copy; + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, + "This is a barebox image..."); + + if (fastboot_download_to_buf(fb)) { + data.len = fb->download_size; + } else { + ret = read_file_2(sourcefile, &data.len, &fb->buf, + fb->download_size); + if (ret) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "reading barebox"); + goto out; + } + } + + data.image = fb->buf; + data.imagefile = sourcefile; + + ret = barebox_update(&data, handler); + + if (ret) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "update barebox: %s", strerror(-ret)); + + goto out; + } + +copy: + if (fastboot_download_to_buf(fb)) + ret = write_file(filename, fb->buf, fb->download_size); + else + ret = copy_file(FASTBOOT_TMPFILE, filename, 1); + + if (ret) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "write partition: %s", strerror(-ret)); + +out: + if (!ret) + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); + + free(fb->buf); + fb->buf = NULL; + + if (!fastboot_download_to_buf(fb)) + unlink(FASTBOOT_TMPFILE); +} + +static void cb_erase(struct fastboot *fb, const char *cmd) +{ + struct file_list_entry *fentry; + int ret; + const char *filename = NULL; + int fd; + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Erasing %s...", cmd); + + file_list_for_each_entry(fb->files, fentry) { + if (!strcmp(cmd, fentry->name)) { + filename = fentry->filename; + break; + } + } + + if (!filename) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "No such partition: %s", cmd); + return; + } + + fd = open(filename, O_RDWR); + if (fd < 0) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-fd)); + + ret = erase(fd, ERASE_SIZE_ALL, 0); + + close(fd); + + if (ret) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "cannot erase partition %s: %s", + filename, strerror(-ret)); + else + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +} + +struct cmd_dispatch_info { + char *cmd; + void (*cb)(struct fastboot *fb, const char *opt); +}; + +static void fb_run_command(struct fastboot *fb, const char *cmdbuf, + const struct cmd_dispatch_info *cmds, int num_commands) +{ + const struct cmd_dispatch_info *cmd; + int i; + + console_countdown_abort(); + + for (i = 0; i < num_commands; i++) { + cmd = &cmds[i]; + + if (!strcmp_l1(cmd->cmd, cmdbuf)) { + cmd->cb(fb, cmdbuf + strlen(cmd->cmd)); + + return; + } + } + + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "unknown command %s", + cmdbuf); +} + +static void cb_oem_getenv(struct fastboot *fb, const char *cmd) +{ + const char *value; + + pr_debug("%s: \"%s\"\n", __func__, cmd); + + value = getenv(cmd); + + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, value ? value : ""); + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +} + +static void cb_oem_setenv(struct fastboot *fb, const char *cmd) +{ + char *var = xstrdup(cmd); + char *value; + int ret; + + pr_debug("%s: \"%s\"\n", __func__, cmd); + + value = strchr(var, '='); + if (!value) { + ret = -EINVAL; + goto out; + } + + *value++ = 0; + + ret = setenv(var, value); + if (ret) + goto out; + + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +out: + free(var); + + if (ret) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-ret)); +} + +static void cb_oem_exec(struct fastboot *fb, const char *cmd) +{ + int ret; + + if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, + "no command support available"); + return; + } + + ret = run_command(cmd); + if (ret < 0) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-ret)); + else if (ret > 0) + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, ""); + else + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +} + +static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = { + { + .cmd = "getenv ", + .cb = cb_oem_getenv, + }, { + .cmd = "setenv ", + .cb = cb_oem_setenv, + }, { + .cmd = "exec ", + .cb = cb_oem_exec, + }, +}; + +static void __maybe_unused cb_oem(struct fastboot *fb, const char *cmd) +{ + pr_debug("%s: \"%s\"\n", __func__, cmd); + + fb_run_command(fb, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info)); +} + +static const struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = "reboot", + .cb = cb_reboot, + }, { + .cmd = "getvar:", + .cb = cb_getvar, + }, { + .cmd = "download:", + .cb = cb_download, +#if defined(CONFIG_BOOTM) + }, { + .cmd = "boot", + .cb = cb_boot, +#endif + }, { + .cmd = "flash:", + .cb = cb_flash, + }, { + .cmd = "erase:", + .cb = cb_erase, +#if defined(CONFIG_FASTBOOT_CMD_OEM) + }, { + .cmd = "oem ", + .cb = cb_oem, +#endif + }, +}; + +void fastboot_exec_cmd(struct fastboot *fb, const char *cmdbuf) +{ + int ret; + + g_fb = fb; + fb->active = true; + + if (fb->cmd_exec) { + ret = fb->cmd_exec(fb, cmdbuf); + if (ret != FASTBOOT_CMD_FALLTHROUGH) + return; + } + + fb_run_command(fb, cmdbuf, cmd_dispatch_info, + ARRAY_SIZE(cmd_dispatch_info)); +} + +static int fastboot_globalvars_init(void) +{ + if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) + globalvar_add_simple_int("usbgadget.fastboot_max_download_size", + &fastboot_max_download_size, "%u"); + + return 0; +} + +device_initcall(fastboot_globalvars_init); + +BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size, + global.usbgadget.fastboot_max_download_size, + "Fastboot maximum download size"); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 6e60c7aee8..977f6c0dba 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -57,38 +57,6 @@ config USB_GADGET_FASTBOOT bool select BANNER select FILE_LIST - prompt "Android Fastboot support" - -config USB_GADGET_FASTBOOT_SPARSE - bool - depends on USB_GADGET_FASTBOOT - select IMAGE_SPARSE - prompt "Enable Fastboot sparse image support" - help - Sparse images are a way for the fastboot protocol to write - images that are bigger than the available memory. If unsure, - say yes here. - -config USB_GADGET_FASTBOOT_BUF - bool - depends on USB_GADGET_FASTBOOT - prompt "Download files to temporary buffer instead of file" - help - With this option enabled the fastboot code will download files to a - temporary buffer instead of a temporary file. Normally you want to - use a file as this also works when your memory is fragmented. However, - in some special cases, when the file consumer also better copes with - a buffer, then using a buffer might be better. - - Say no here unless you know what you are doing. - -config USB_GADGET_FASTBOOT_CMD_OEM - bool - depends on USB_GADGET_FASTBOOT - prompt "Enable OEM commands" - help - This option enables the fastboot "oem" group of commands. They allow to - executing arbitrary barebox commands and may be disabled in secure - environments. - + select FASTBOOT_BASE + prompt "Android Fastboot USB Gadget" endif diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index cf3cc6dac7..f8a9c32530 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -11,90 +11,36 @@ * Copyright 2014 Sascha Hauer * Ported to barebox * + * Copyright 2020 Edmund Henniges + * Copyright 2020 Daniel Glöckner + * Split off of generic parts + * * SPDX-License-Identifier: GPL-2.0+ */ #define pr_fmt(fmt) "fastboot: " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include - -#define FASTBOOT_VERSION "0.4" #define FASTBOOT_INTERFACE_CLASS 0xff #define FASTBOOT_INTERFACE_SUB_CLASS 0x42 #define FASTBOOT_INTERFACE_PROTOCOL 0x03 -#define FASTBOOT_TMPFILE "/.fastboot.img" - #define EP_BUFFER_SIZE 4096 -static unsigned int fastboot_max_download_size = SZ_8M; - -struct fb_variable { - char *name; - char *value; - struct list_head list; -}; - struct f_fastboot { + struct fastboot fastboot; struct usb_function func; /* IN/OUT EP's and corresponding requests */ struct usb_ep *in_ep, *out_ep; struct usb_request *in_req, *out_req; - struct file_list *files; - int (*cmd_exec)(struct f_fastboot *, const char *cmd); - int (*cmd_flash)(struct f_fastboot *, struct file_list_entry *entry, - const char *filename, const void *buf, size_t len); - int download_fd; - void *buf; - bool active; - - size_t download_bytes; - size_t download_size; - struct list_head variables; }; -static inline bool fastboot_download_to_buf(struct f_fastboot *f_fb) -{ - if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_BUF)) - return true; - else - return false; -} - static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) { return container_of(f, struct f_fastboot, func); @@ -182,6 +128,9 @@ static struct usb_gadget_strings *fastboot_strings[] = { }; static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +static int fastboot_write_usb(struct fastboot *fb, const char *buffer, + unsigned int buffer_size); +static void fastboot_start_download_usb(struct fastboot *fb); static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) { @@ -206,116 +155,6 @@ static struct usb_request *fastboot_alloc_request(struct usb_ep *ep) return req; } -static void fb_setvar(struct fb_variable *var, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - var->value = bvasprintf(fmt, ap); - va_end(ap); -} - -static struct fb_variable *fb_addvar(struct f_fastboot *f_fb, const char *fmt, ...) -{ - struct fb_variable *var = xzalloc(sizeof(*var)); - va_list ap; - - va_start(ap, fmt); - var->name = bvasprintf(fmt, ap); - va_end(ap); - - list_add_tail(&var->list, &f_fb->variables); - - return var; -} - -static int fastboot_add_partition_variables(struct f_fastboot *f_fb, - struct file_list_entry *fentry) -{ - struct stat s; - size_t size = 0; - int fd, ret; - struct mtd_info_user mtdinfo; - char *type = NULL; - struct fb_variable *var; - - ret = stat(fentry->filename, &s); - if (ret) { - device_detect_by_name(devpath_to_name(fentry->filename)); - ret = stat(fentry->filename, &s); - } - - if (ret) { - if (fentry->flags & FILE_LIST_FLAG_CREATE) { - ret = 0; - type = "file"; - goto out; - } - - goto out; - } - - fd = open(fentry->filename, O_RDWR); - if (fd < 0) { - ret = -EINVAL; - goto out; - } - - size = s.st_size; - - ret = ioctl(fd, MEMGETINFO, &mtdinfo); - - close(fd); - - if (!ret) { - switch (mtdinfo.type) { - case MTD_NANDFLASH: - type = "NAND-flash"; - break; - case MTD_NORFLASH: - type = "NOR-flash"; - break; - case MTD_UBIVOLUME: - type = "UBI"; - break; - default: - type = "flash"; - break; - } - - goto out; - } - - type = "basic"; - ret = 0; - -out: - if (ret) - return ret; - - var = fb_addvar(f_fb, "partition-size:%s", fentry->name); - fb_setvar(var, "%08zx", size); - var = fb_addvar(f_fb, "partition-type:%s", fentry->name); - fb_setvar(var, "%s", type); - - return ret; -} - -static int fastboot_add_bbu_variables(struct bbu_handler *handler, void *ctx) -{ - struct f_fastboot *f_fb = ctx; - char *name; - int ret; - - name = basprintf("bbu-%s", handler->name); - - ret = file_list_add_entry(f_fb->files, name, handler->devicefile, 0); - - free(name); - - return ret; -} - static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; @@ -325,30 +164,17 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) struct usb_string *us; const struct usb_function_instance *fi = f->fi; struct f_fastboot_opts *opts = container_of(fi, struct f_fastboot_opts, func_inst); - struct file_list_entry *fentry; - struct fb_variable *var; - - f_fb->files = opts->files; - f_fb->cmd_exec = opts->cmd_exec; - f_fb->cmd_flash = opts->cmd_flash; - - var = fb_addvar(f_fb, "version"); - fb_setvar(var, "0.4"); - var = fb_addvar(f_fb, "bootloader-version"); - fb_setvar(var, release_string); - if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE)) { - var = fb_addvar(f_fb, "max-download-size"); - fb_setvar(var, "%u", fastboot_max_download_size); - } - if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && opts->export_bbu) - bbu_handlers_iterate(fastboot_add_bbu_variables, f_fb); + f_fb->fastboot.write = fastboot_write_usb; + f_fb->fastboot.start_download = fastboot_start_download_usb; - file_list_for_each_entry(f_fb->files, fentry) { - ret = fastboot_add_partition_variables(f_fb, fentry); - if (ret) - return ret; - } + f_fb->fastboot.files = opts->common.files; + f_fb->fastboot.cmd_exec = opts->common.cmd_exec; + f_fb->fastboot.cmd_flash = opts->common.cmd_flash; + + ret = fastboot_generic_init(&f_fb->fastboot, opts->common.export_bbu); + if (ret) + return ret; /* DYNAMIC interface numbers assignments */ id = usb_interface_id(c, f); @@ -409,7 +235,6 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_fastboot *f_fb = func_to_fastboot(f); - struct fb_variable *var, *tmp; usb_ep_dequeue(f_fb->in_ep, f_fb->in_req); free(f_fb->in_req->buf); @@ -421,14 +246,7 @@ static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) usb_ep_free_request(f_fb->out_ep, f_fb->out_req); f_fb->out_req = NULL; - list_for_each_entry_safe(var, tmp, &f_fb->variables, list) { - free(var->name); - free(var->value); - list_del(&var->list); - free(var); - } - - f_fb->active = false; + fastboot_generic_free(&f_fb->fastboot); } static void fastboot_disable(struct usb_function *f) @@ -481,43 +299,21 @@ err: return ret; } -static struct f_fastboot *g_f_fb; - static void fastboot_free_func(struct usb_function *f) { struct f_fastboot *f_fb = container_of(f, struct f_fastboot, func); - if (g_f_fb == f_fb) - g_f_fb = NULL; - + fastboot_generic_close(&f_fb->fastboot); free(f_fb); } -/* - * A "oem exec bootm" or similar commands will stop barebox. Tell the - * fastboot command on the other side so that it doesn't run into a - * timeout. - */ -static void fastboot_shutdown(void) -{ - struct f_fastboot *f_fb = g_f_fb; - - if (!f_fb || !f_fb->active) - return; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "barebox shutting down"); - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -early_exitcall(fastboot_shutdown); - static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi) { struct f_fastboot *f_fb; f_fb = xzalloc(sizeof(*f_fb)); - INIT_LIST_HEAD(&f_fb->variables); + INIT_LIST_HEAD(&f_fb->fastboot.variables); f_fb->func.name = "fastboot"; f_fb->func.strings = fastboot_strings; @@ -527,9 +323,6 @@ static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi f_fb->func.unbind = fastboot_unbind; f_fb->func.free_func = fastboot_free_func; - if (!g_f_fb) - g_f_fb = f_fb; - return &f_fb->func; } @@ -553,8 +346,9 @@ static struct usb_function_instance *fastboot_alloc_instance(void) DECLARE_USB_FUNCTION_INIT(fastboot, fastboot_alloc_instance, fastboot_alloc_func); -static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsigned int buffer_size) +static int fastboot_write_usb(struct fastboot *fb, const char *buffer, unsigned int buffer_size) { + struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot); struct usb_request *in_req = f_fb->in_req; uint64_t start; int ret; @@ -580,91 +374,10 @@ static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsign return 0; } -static char *fastboot_msg[] = { - [FASTBOOT_MSG_OKAY] = "OKAY", - [FASTBOOT_MSG_FAIL] = "FAIL", - [FASTBOOT_MSG_INFO] = "INFO", - [FASTBOOT_MSG_DATA] = "DATA", -}; - -int fastboot_tx_print(struct f_fastboot *f_fb, enum fastboot_msg_type type, - const char *fmt, ...) -{ - struct va_format vaf; - char buf[64]; - va_list ap; - int n; - const char *msg = fastboot_msg[type]; - - va_start(ap, fmt); - vaf.fmt = fmt; - vaf.va = ≈ - - n = snprintf(buf, 64, "%s%pV", msg, &vaf); - - switch (type) { - case FASTBOOT_MSG_OKAY: - f_fb->active = false; - break; - case FASTBOOT_MSG_FAIL: - f_fb->active = false; - pr_err("%pV\n", &vaf); - break; - case FASTBOOT_MSG_INFO: - pr_info("%pV\n", &vaf); - break; - case FASTBOOT_MSG_DATA: - break; - } - - va_end(ap); - - if (n > 64) - n = 64; - - return fastboot_tx_write(f_fb, buf, n); -} - -static void cb_reboot(struct f_fastboot *f_fb, const char *cmd) -{ - restart_machine(); -} - -static int strcmp_l1(const char *s1, const char *s2) -{ - if (!s1 || !s2) - return -1; - return strncmp(s1, s2, strlen(s1)); -} - -static void cb_getvar(struct f_fastboot *f_fb, const char *cmd) -{ - struct fb_variable *var; - - pr_debug("getvar: \"%s\"\n", cmd); - - if (!strcmp_l1(cmd, "all")) { - list_for_each_entry(var, &f_fb->variables, list) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "%s: %s", - var->name, var->value); - } - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); - return; - } - - list_for_each_entry(var, &f_fb->variables, list) { - if (!strcmp(cmd, var->name)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, var->value); - return; - } - } - - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - static int rx_bytes_expected(struct f_fastboot *f_fb) { - int remaining = f_fb->download_size - f_fb->download_bytes; + int remaining = f_fb->fastboot.download_size + - f_fb->fastboot.download_bytes; if (remaining >= EP_BUFFER_SIZE) return EP_BUFFER_SIZE; @@ -683,651 +396,50 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) return; } - if (fastboot_download_to_buf(f_fb)) { - memcpy(f_fb->buf + f_fb->download_bytes, buffer, req->actual); - } else { - ret = write(f_fb->download_fd, buffer, req->actual); - if (ret < 0) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret)); - return; - } + ret = fastboot_handle_download_data(&f_fb->fastboot, buffer, + req->actual); + if (ret < 0) { + fastboot_tx_print(&f_fb->fastboot, FASTBOOT_MSG_FAIL, + strerror(-ret)); + return; } - f_fb->download_bytes += req->actual; - req->length = rx_bytes_expected(f_fb); - show_progress(f_fb->download_bytes); - /* Check if transfer is done */ - if (f_fb->download_bytes >= f_fb->download_size) { + if (f_fb->fastboot.download_bytes >= f_fb->fastboot.download_size) { req->complete = rx_handler_command; req->length = EP_BUFFER_SIZE; - close(f_fb->download_fd); - - printf("\n"); - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes finished", - f_fb->download_bytes); - - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); + fastboot_download_finished(&f_fb->fastboot); } req->actual = 0; usb_ep_queue(ep, req); } -static void cb_download(struct f_fastboot *f_fb, const char *cmd) -{ - f_fb->download_size = simple_strtoul(cmd, NULL, 16); - f_fb->download_bytes = 0; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes...", - f_fb->download_size); - - init_progression_bar(f_fb->download_size); - - if (fastboot_download_to_buf(f_fb)) { - free(f_fb->buf); - f_fb->buf = malloc(f_fb->download_size); - if (!f_fb->buf) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "not enough memory"); - return; - } - } else { - f_fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC); - if (f_fb->download_fd < 0) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "internal error"); - return; - } - } - - if (!f_fb->download_size) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "data invalid size"); - } else { - struct usb_request *req = f_fb->out_req; - fastboot_tx_print(f_fb, FASTBOOT_MSG_DATA, - "%08x", f_fb->download_size); - req->complete = rx_handler_dl_image; - req->length = rx_bytes_expected(f_fb); - } -} - -static void __maybe_unused cb_boot(struct f_fastboot *f_fb, const char *opt) -{ - int ret; - struct bootm_data data = { - .initrd_address = UIMAGE_INVALID_ADDRESS, - .os_address = UIMAGE_SOME_ADDRESS, - }; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Booting kernel..\n"); - - globalvar_set_match("linux.bootargs.dyn.", ""); - globalvar_set_match("bootm.image", ""); - - data.os_file = FASTBOOT_TMPFILE; - - ret = bootm_boot(&data); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "Booting failed: %s", - strerror(-ret)); - else - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -static struct mtd_info *get_mtd(struct f_fastboot *f_fb, const char *filename) -{ - int fd, ret; - struct mtd_info_user meminfo; - - fd = open(filename, O_RDONLY); - if (fd < 0) - return ERR_PTR(-errno); - - ret = ioctl(fd, MEMGETINFO, &meminfo); - - close(fd); - - if (ret) - return ERR_PTR(ret); - - return meminfo.mtd; -} - -static int do_ubiformat(struct f_fastboot *f_fb, struct mtd_info *mtd, - const char *file, const void *buf, size_t len) -{ - struct ubiformat_args args = { - .yes = 1, - .image = file, - .image_buf = buf, - .image_size = len, - }; - - if (!file) - args.novtbl = 1; - - if (!IS_ENABLED(CONFIG_UBIFORMAT)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "ubiformat is not available"); - return -ENODEV; - } - - return ubiformat(mtd, &args); -} - - -static int check_ubi(struct f_fastboot *f_fb, struct file_list_entry *fentry, - enum filetype filetype) -{ - struct mtd_info *mtd; - - mtd = get_mtd(f_fb, fentry->filename); - - /* - * Issue a warning when we are about to write a UBI image to a MTD device - * and the FILE_LIST_FLAG_UBI is not given as this means we loose all - * erase counters. - */ - if (!IS_ERR(mtd) && filetype == filetype_ubi && - !(fentry->flags & FILE_LIST_FLAG_UBI)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "writing UBI image to MTD device, " - "add the 'u' "); - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "flag to the partition description"); - return 0; - } - - if (!(fentry->flags & FILE_LIST_FLAG_UBI)) - return 0; - - if (!IS_ENABLED(CONFIG_UBIFORMAT)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "ubiformat not available"); - return -ENOSYS; - } - - if (IS_ERR(mtd)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "UBI flag given on non-MTD device"); - return -EINVAL; - } - - if (filetype == filetype_ubi) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "This is a UBI image..."); - return 1; - } else { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "This is no UBI image but %s", - file_type_to_string(filetype)); - return -EINVAL; - } -} - -static int fastboot_handle_sparse(struct f_fastboot *f_fb, - struct file_list_entry *fentry) -{ - struct sparse_image_ctx *sparse; - void *buf = NULL; - int ret, fd; - unsigned int flags = O_RDWR; - int bufsiz = SZ_128K; - struct stat s; - struct mtd_info *mtd = NULL; - - ret = stat(fentry->filename, &s); - if (ret) { - if (fentry->flags & FILE_LIST_FLAG_CREATE) - flags |= O_CREAT; - else - return ret; - } - - fd = open(fentry->filename, flags); - if (fd < 0) - return -errno; - - ret = fstat(fd, &s); - if (ret) - goto out_close_fd; - - sparse = sparse_image_open(FASTBOOT_TMPFILE); - if (IS_ERR(sparse)) { - pr_err("Cannot open sparse image\n"); - ret = PTR_ERR(sparse); - goto out_close_fd; - } - - if (S_ISREG(s.st_mode)) { - ret = ftruncate(fd, sparse_image_size(sparse)); - if (ret) - goto out; - } - - buf = malloc(bufsiz); - if (!buf) { - ret = -ENOMEM; - goto out; - } - - if (fentry->flags & FILE_LIST_FLAG_UBI) { - mtd = get_mtd(f_fb, fentry->filename); - if (IS_ERR(mtd)) { - ret = PTR_ERR(mtd); - goto out; - } - } - - while (1) { - int retlen; - loff_t pos; - - ret = sparse_image_read(sparse, buf, &pos, bufsiz, &retlen); - if (ret) - goto out; - if (!retlen) - break; - - if (pos == 0) { - ret = check_ubi(f_fb, fentry, file_detect_type(buf, retlen)); - if (ret < 0) - goto out; - } - - if (fentry->flags & FILE_LIST_FLAG_UBI) { - if (!IS_ENABLED(CONFIG_UBIFORMAT)) { - ret = -ENOSYS; - goto out; - } - - if (pos == 0) { - ret = do_ubiformat(f_fb, mtd, NULL, NULL, 0); - if (ret) - goto out; - } - - ret = ubiformat_write(mtd, buf, retlen, pos); - if (ret) - goto out; - } else { - discard_range(fd, retlen, pos); - - pos = lseek(fd, pos, SEEK_SET); - if (pos == -1) { - ret = -errno; - goto out; - } - - ret = write_full(fd, buf, retlen); - if (ret < 0) - goto out; - } - } - - ret = 0; - -out: - free(buf); - sparse_image_close(sparse); -out_close_fd: - close(fd); - - return ret; -} - -static void cb_flash(struct f_fastboot *f_fb, const char *cmd) -{ - struct file_list_entry *fentry; - int ret; - const char *filename = NULL, *sourcefile; - enum filetype filetype; - - if (fastboot_download_to_buf(f_fb)) { - sourcefile = NULL; - filetype = file_detect_type(f_fb->buf, f_fb->download_bytes); - } else { - sourcefile = FASTBOOT_TMPFILE; - filetype = file_name_detect_type(FASTBOOT_TMPFILE); - } - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Copying file to %s...", - cmd); - - fentry = file_list_entry_by_name(f_fb->files, cmd); - - if (!fentry) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "No such partition: %s", - cmd); - ret = -ENOENT; - goto out; - } - - if (f_fb->cmd_flash) { - ret = f_fb->cmd_flash(f_fb, fentry, sourcefile, f_fb->buf, - f_fb->download_size); - if (ret != FASTBOOT_CMD_FALLTHROUGH) - goto out; - } - - filename = fentry->filename; - - if (filetype == filetype_android_sparse) { - if (!IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE) || - fastboot_download_to_buf(f_fb)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "sparse image not supported"); - ret = -EOPNOTSUPP; - goto out; - } - - ret = fastboot_handle_sparse(f_fb, fentry); - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "writing sparse image: %s", - strerror(-ret)); - - goto out; - } - - ret = check_ubi(f_fb, fentry, filetype); - if (ret < 0) - goto out; - - if (ret > 0) { - struct mtd_info *mtd; - - mtd = get_mtd(f_fb, fentry->filename); - - ret = do_ubiformat(f_fb, mtd, sourcefile, f_fb->buf, - f_fb->download_size); - if (ret) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "write partition: %s", - strerror(-ret)); - goto out; - } - - goto out; - } - - if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && filetype_is_barebox_image(filetype)) { - struct bbu_handler *handler; - struct bbu_data data = { - .devicefile = filename, - .flags = BBU_FLAG_YES, - }; - - handler = bbu_find_handler_by_device(data.devicefile); - if (!handler) - goto copy; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, - "This is a barebox image..."); - - if (fastboot_download_to_buf(f_fb)) { - data.len = f_fb->download_size; - } else { - ret = read_file_2(sourcefile, &data.len, &f_fb->buf, - f_fb->download_size); - if (ret) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "reading barebox"); - goto out; - } - } - - data.image = f_fb->buf; - data.imagefile = sourcefile; - - ret = barebox_update(&data, handler); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "update barebox: %s", strerror(-ret)); - - goto out; - } - -copy: - if (fastboot_download_to_buf(f_fb)) - ret = write_file(filename, f_fb->buf, f_fb->download_size); - else - ret = copy_file(FASTBOOT_TMPFILE, filename, 1); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "write partition: %s", strerror(-ret)); - -out: - if (!ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); - - free(f_fb->buf); - f_fb->buf = NULL; - - if (!fastboot_download_to_buf(f_fb)) - unlink(FASTBOOT_TMPFILE); -} - -static void cb_erase(struct f_fastboot *f_fb, const char *cmd) -{ - struct file_list_entry *fentry; - int ret; - const char *filename = NULL; - int fd; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Erasing %s...", cmd); - - file_list_for_each_entry(f_fb->files, fentry) { - if (!strcmp(cmd, fentry->name)) { - filename = fentry->filename; - break; - } - } - - if (!filename) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "No such partition: %s", cmd); - return; - } - - fd = open(filename, O_RDWR); - if (fd < 0) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-fd)); - - ret = erase(fd, ERASE_SIZE_ALL, 0); - - close(fd); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "cannot erase partition %s: %s", - filename, strerror(-ret)); - else - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -struct cmd_dispatch_info { - char *cmd; - void (*cb)(struct f_fastboot *f_fb, const char *opt); -}; - -static void fb_run_command(struct f_fastboot *f_fb, const char *cmdbuf, - const struct cmd_dispatch_info *cmds, int num_commands) -{ - const struct cmd_dispatch_info *cmd; - int i; - - console_countdown_abort(); - - for (i = 0; i < num_commands; i++) { - cmd = &cmds[i]; - - if (!strcmp_l1(cmd->cmd, cmdbuf)) { - cmd->cb(f_fb, cmdbuf + strlen(cmd->cmd)); - - return; - } - } - - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "unknown command %s", - cmdbuf); -} - -static void cb_oem_getenv(struct f_fastboot *f_fb, const char *cmd) -{ - const char *value; - - pr_debug("%s: \"%s\"\n", __func__, cmd); - - value = getenv(cmd); - - fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, value ? value : ""); - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -static void cb_oem_setenv(struct f_fastboot *f_fb, const char *cmd) -{ - char *var = xstrdup(cmd); - char *value; - int ret; - - pr_debug("%s: \"%s\"\n", __func__, cmd); - - value = strchr(var, '='); - if (!value) { - ret = -EINVAL; - goto out; - } - - *value++ = 0; - - ret = setenv(var, value); - if (ret) - goto out; - - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -out: - free(var); - - if (ret) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret)); -} - -static void cb_oem_exec(struct f_fastboot *f_fb, const char *cmd) +static void fastboot_start_download_usb(struct fastboot *fb) { - int ret; + struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot); + struct usb_request *req = f_fb->out_req; - if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) { - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, - "no command support available"); - return; - } - - ret = run_command(cmd); - if (ret < 0) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret)); - else if (ret > 0) - fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, ""); - else - fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, ""); -} - -static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = { - { - .cmd = "getenv ", - .cb = cb_oem_getenv, - }, { - .cmd = "setenv ", - .cb = cb_oem_setenv, - }, { - .cmd = "exec ", - .cb = cb_oem_exec, - }, -}; - -static void __maybe_unused cb_oem(struct f_fastboot *f_fb, const char *cmd) -{ - pr_debug("%s: \"%s\"\n", __func__, cmd); - - fb_run_command(f_fb, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info)); + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(f_fb); + fastboot_start_download_generic(fb); } -static const struct cmd_dispatch_info cmd_dispatch_info[] = { - { - .cmd = "reboot", - .cb = cb_reboot, - }, { - .cmd = "getvar:", - .cb = cb_getvar, - }, { - .cmd = "download:", - .cb = cb_download, -#if defined(CONFIG_BOOTM) - }, { - .cmd = "boot", - .cb = cb_boot, -#endif - }, { - .cmd = "flash:", - .cb = cb_flash, - }, { - .cmd = "erase:", - .cb = cb_erase, -#if defined(CONFIG_USB_GADGET_FASTBOOT_CMD_OEM) - }, { - .cmd = "oem ", - .cb = cb_oem, -#endif - }, -}; - static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) { char *cmdbuf = req->buf; struct f_fastboot *f_fb = req->context; - int ret; if (req->status != 0) return; - f_fb->active = true; - *(cmdbuf + req->actual) = 0; - - if (f_fb->cmd_exec) { - ret = f_fb->cmd_exec(f_fb, cmdbuf); - if (ret != FASTBOOT_CMD_FALLTHROUGH) - goto done; - } - - fb_run_command(f_fb, cmdbuf, cmd_dispatch_info, - ARRAY_SIZE(cmd_dispatch_info)); -done: + fastboot_exec_cmd(&f_fb->fastboot, cmdbuf); *cmdbuf = '\0'; req->actual = 0; memset(req->buf, 0, EP_BUFFER_SIZE); usb_ep_queue(ep, req); } - -static int fastboot_globalvars_init(void) -{ - if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE)) - globalvar_add_simple_int("usbgadget.fastboot_max_download_size", - &fastboot_max_download_size, "%u"); - - return 0; -} - -device_initcall(fastboot_globalvars_init); - -BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size, - global.usbgadget.fastboot_max_download_size, - "Fastboot maximum download size"); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index d6edfb8cf2..445cc32420 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -127,10 +127,7 @@ static int multi_bind_fastboot(struct usb_composite_dev *cdev) } opts = container_of(fi_fastboot, struct f_fastboot_opts, func_inst); - opts->files = gadget_multi_opts->fastboot_opts.files; - opts->cmd_exec = gadget_multi_opts->fastboot_opts.cmd_exec; - opts->cmd_flash = gadget_multi_opts->fastboot_opts.cmd_flash; - opts->export_bbu = gadget_multi_opts->fastboot_opts.export_bbu; + opts->common = gadget_multi_opts->fastboot_opts; f_fastboot = usb_get_function(fi_fastboot); if (IS_ERR(f_fastboot)) { diff --git a/include/fastboot.h b/include/fastboot.h new file mode 100644 index 0000000000..3b6cae8a58 --- /dev/null +++ b/include/fastboot.h @@ -0,0 +1,66 @@ +#ifndef __FASTBOOT__ +#define __FASTBOOT__ + +#include +#include +#include + +/* + * Return codes for the exec_cmd callback above: + * + * FASTBOOT_CMD_FALLTHROUGH - Not handled by the external command dispatcher, + * handle it with internal dispatcher + * Other than these negative error codes mean errors handling the command and + * zero means the command has been successfully handled. + */ +#define FASTBOOT_CMD_FALLTHROUGH 1 + +struct fastboot { + int (*write)(struct fastboot *fb, const char *buf, unsigned int n); + void (*start_download)(struct fastboot *fb); + + struct file_list *files; + int (*cmd_exec)(struct fastboot *fb, const char *cmd); + int (*cmd_flash)(struct fastboot *fb, struct file_list_entry *entry, + const char *filename, const void *buf, size_t len); + int download_fd; + void *buf; + + bool active; + + size_t download_bytes; + size_t download_size; + struct list_head variables; +}; + +/** + * struct fastboot_opts - options to configure fastboot + * @files: A file_list containing the files (partitions) to export via fastboot + * @export_bbu: Automatically include the partitions provided by barebox update (bbu) + */ +struct fastboot_opts { + struct file_list *files; + bool export_bbu; + int (*cmd_exec)(struct fastboot *fb, const char *cmd); + int (*cmd_flash)(struct fastboot *fb, struct file_list_entry *entry, + const char *filename, const void *buf, size_t len); +}; + +enum fastboot_msg_type { + FASTBOOT_MSG_OKAY, + FASTBOOT_MSG_FAIL, + FASTBOOT_MSG_INFO, + FASTBOOT_MSG_DATA, +}; + +int fastboot_generic_init(struct fastboot *fb, bool export_bbu); +void fastboot_generic_close(struct fastboot *fb); +void fastboot_generic_free(struct fastboot *fb); +int fastboot_handle_download_data(struct fastboot *fb, const void *buffer, + unsigned int len); +int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type, + const char *fmt, ...); +void fastboot_start_download_generic(struct fastboot *fb); +void fastboot_download_finished(struct fastboot *fb); +void fastboot_exec_cmd(struct fastboot *fb, const char *cmdbuf); +#endif diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h index c0775c67dd..a3609ba5db 100644 --- a/include/usb/fastboot.h +++ b/include/usb/fastboot.h @@ -1,45 +1,17 @@ #ifndef _USB_FASTBOOT_H #define _USB_FASTBOOT_H -#include -#include #include - -struct f_fastboot; +#include /** * struct f_fastboot_opts - options to configure the fastboot gadget + * @common: Options common to all fastboot protocol variants * @func_inst: The USB function instance to register on - * @files: A file_list containing the files (partitions) to export via fastboot - * @export_bbu: Automatically include the partitions provided by barebox update (bbu) */ struct f_fastboot_opts { + struct fastboot_opts common; struct usb_function_instance func_inst; - struct file_list *files; - bool export_bbu; - int (*cmd_exec)(struct f_fastboot *, const char *cmd); - int (*cmd_flash)(struct f_fastboot *, struct file_list_entry *entry, - const char *filename, const void *buf, size_t len); -}; - -/* - * Return codes for the exec_cmd callback above: - * - * FASTBOOT_CMD_FALLTHROUGH - Not handled by the external command dispatcher, - * handle it with internal dispatcher - * Other than these negative error codes mean errors handling the command and - * zero means the command has been successfully handled. - */ -#define FASTBOOT_CMD_FALLTHROUGH 1 - -enum fastboot_msg_type { - FASTBOOT_MSG_OKAY, - FASTBOOT_MSG_FAIL, - FASTBOOT_MSG_INFO, - FASTBOOT_MSG_DATA, }; -int fastboot_tx_print(struct f_fastboot *f_fb, enum fastboot_msg_type type, - const char *fmt, ...); - #endif /* _USB_FASTBOOT_H */ diff --git a/include/usb/gadget-multi.h b/include/usb/gadget-multi.h index 030e604fe7..9bb6c889f3 100644 --- a/include/usb/gadget-multi.h +++ b/include/usb/gadget-multi.h @@ -6,7 +6,7 @@ #include struct f_multi_opts { - struct f_fastboot_opts fastboot_opts; + struct fastboot_opts fastboot_opts; struct f_dfu_opts dfu_opts; int create_acm; void (*release)(struct f_multi_opts *opts); -- cgit v1.2.3