diff --git a/common/ubiformat.c b/common/ubiformat.c index f728119..9fe1c7c 100644 --- a/common/ubiformat.c +++ b/common/ubiformat.c @@ -187,16 +187,22 @@ static int flash_image(struct ubiformat_args *args, struct mtd_info *mtd, const struct ubigen_info *ui, struct ubi_scan_info *si) { - int fd, img_ebs, eb, written_ebs = 0, ret = -1, eb_cnt; + int fd = 0, img_ebs, eb, written_ebs = 0, ret = -1, eb_cnt; off_t st_size; char *buf = NULL; uint64_t lastprint = 0; + const void *inbuf = NULL; eb_cnt = mtd_num_pebs(mtd); - fd = open_file(args->image, &st_size); - if (fd < 0) - return fd; + if (args->image) { + fd = open_file(args->image, &st_size); + if (fd < 0) + return fd; + } else { + inbuf = args->image_buf; + st_size = args->image_size; + } buf = malloc(mtd->erasesize); if (!buf) { @@ -207,20 +213,20 @@ img_ebs = st_size / mtd->erasesize; if (img_ebs > si->good_cnt) { - sys_errmsg("file \"%s\" is too large (%lld bytes)", - args->image, (long long)st_size); + sys_errmsg("image is too large (%lld bytes)", + (long long)st_size); goto out_close; } if (st_size % mtd->erasesize) { - sys_errmsg("file \"%s\" (size %lld bytes) is not multiple of " + sys_errmsg("image (size %lld bytes) is not multiple of " "eraseblock size (%d bytes)", - args->image, (long long)st_size, mtd->erasesize); + (long long)st_size, mtd->erasesize); goto out_close; } if (st_size == 0) { - sys_errmsg("file \"%s\" has size 0 bytes", args->image); + sys_errmsg("image has size 0 bytes"); goto out_close; } @@ -260,11 +266,16 @@ continue; } - err = read_full(fd, buf, mtd->erasesize); - if (err < 0) { - sys_errmsg("failed to read eraseblock %d from \"%s\"", - written_ebs, args->image); - goto out_close; + if (args->image) { + err = read_full(fd, buf, mtd->erasesize); + if (err < 0) { + sys_errmsg("failed to read eraseblock %d from image", + written_ebs); + goto out_close; + } + } else { + memcpy(buf, inbuf, mtd->erasesize); + inbuf += mtd->erasesize; } if (args->override_ec) @@ -280,8 +291,8 @@ err = change_ech((struct ubi_ec_hdr *)buf, ui->image_seq, ec); if (err) { - errmsg("bad EC header at eraseblock %d of \"%s\"", - written_ebs, args->image); + errmsg("bad EC header at eraseblock %d of image", + written_ebs); goto out_close; } @@ -317,7 +328,8 @@ out_close: free(buf); - close(fd); + if (args->image) + close(fd); return ret; } @@ -454,6 +466,11 @@ return -1; } +static bool ubiformat_has_image(struct ubiformat_args *args) +{ + return args->image || args->image_buf; +} + int ubiformat(struct mtd_info *mtd, struct ubiformat_args *args) { int err, verbose, eb_cnt; @@ -555,7 +572,7 @@ goto out_free; } - if (si->good_cnt < 2 && (!args->novtbl || args->image)) { + if (si->good_cnt < 2 && (!args->novtbl || ubiformat_has_image(args))) { errmsg("too few non-bad eraseblocks (%d) on %s", si->good_cnt, mtd->name); err = -EINVAL; @@ -654,7 +671,7 @@ } } - if (args->image) { + if (ubiformat_has_image(args)) { err = flash_image(args, mtd, &ui, si); if (err < 0) goto out_free; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b612d39..b0408e3 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -57,7 +57,27 @@ bool select BANNER select FILE_LIST - select IMAGE_SPARSE prompt "Android Fastboot support" +config USB_GADGET_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 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. endif diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 87a43cc..787b120 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -71,16 +71,29 @@ struct f_fastboot { struct usb_function func; - /* IN/OUT EP's and correspoinding requests */ + /* 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; + 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); @@ -317,13 +330,17 @@ 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); - var = fb_addvar(f_fb, "max-download-size"); - fb_setvar(var, "%u", fastboot_max_download_size); + if (IS_ENABLED(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); @@ -555,10 +572,8 @@ restart_machine(); } -static void cb_reboot(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_reboot(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; - f_fb->in_req->complete = compl_do_reset; fastboot_tx_print(f_fb, "OKAY"); } @@ -570,9 +585,8 @@ return strncmp(s1, s2, strlen(s1)); } -static void cb_getvar(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_getvar(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; struct fb_variable *var; pr_debug("getvar: \"%s\"\n", cmd); @@ -606,10 +620,14 @@ return; } - ret = write(f_fb->download_fd, buffer, req->actual); - if (ret < 0) { - fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret)); - 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, "FAIL%s", strerror(-ret)); + return; + } } f_fb->download_bytes += req->actual; @@ -638,10 +656,8 @@ usb_ep_queue(ep, req); } -static void cb_download(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_download(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; - f_fb->download_size = simple_strtoul(cmd, NULL, 16); f_fb->download_bytes = 0; @@ -649,15 +665,26 @@ init_progression_bar(f_fb->download_size); - f_fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC); - if (f_fb->download_fd < 0) { - fastboot_tx_print(f_fb, "FAILInternal Error"); - return; + 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, "FAILnot 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, "FAILInternal Error"); + return; + } } if (!f_fb->download_size) { fastboot_tx_print(f_fb, "FAILdata invalid size"); } else { + struct usb_request *req = f_fb->out_req; + struct usb_ep *ep = f_fb->out_ep; fastboot_tx_print(f_fb, "DATA%08x", f_fb->download_size); req->complete = rx_handler_dl_image; req->length = EP_BUFFER_SIZE; @@ -686,11 +713,8 @@ pr_err("Booting failed\n"); } -static void __maybe_unused cb_boot(struct usb_ep *ep, struct usb_request *req, - const char *opt) +static void __maybe_unused cb_boot(struct f_fastboot *f_fb, const char *opt) { - struct f_fastboot *f_fb = req->context; - f_fb->in_req->complete = do_bootm_on_complete; fastboot_tx_print(f_fb, "OKAY"); } @@ -715,11 +739,13 @@ } static int do_ubiformat(struct f_fastboot *f_fb, struct mtd_info *mtd, - const char *file) + 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) @@ -854,7 +880,7 @@ } if (pos == 0) { - ret = do_ubiformat(f_fb, mtd, NULL); + ret = do_ubiformat(f_fb, mtd, NULL, NULL, 0); if (ret) goto out; } @@ -886,13 +912,20 @@ return ret; } -static void cb_flash(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_flash(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; struct file_list_entry *fentry; int ret; - const char *filename = NULL; - enum filetype filetype = file_name_detect_type(FASTBOOT_TMPFILE); + 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, "INFOCopying file to %s...", cmd); @@ -900,45 +933,61 @@ if (!fentry) { fastboot_tx_print(f_fb, "FAILNo such partition: %s", cmd); - return; + 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(USB_GADGET_FASTBOOT_SPARSE)) { + fastboot_tx_print(f_fb, "FAILsparse image not supported"); + ret = -EOPNOTSUPP; + goto out; + } + + if (fastboot_download_to_buf(f_fb)) { + fastboot_tx_print(f_fb, "FAILsparse image not supported"); + goto out; + } + ret = fastboot_handle_sparse(f_fb, fentry); - if (ret) { + if (ret) fastboot_tx_print(f_fb, "FAILwriting sparse image: %s", strerror(-ret)); - return; - } goto out; } ret = check_ubi(f_fb, fentry, filetype); if (ret < 0) - return; + goto out; if (ret > 0) { struct mtd_info *mtd; mtd = get_mtd(f_fb, fentry->filename); - ret = do_ubiformat(f_fb, mtd, FASTBOOT_TMPFILE); + ret = do_ubiformat(f_fb, mtd, sourcefile, f_fb->buf, + f_fb->download_size); if (ret) { fastboot_tx_print(f_fb, "FAILwrite partition: %s", strerror(-ret)); - return; + goto out; } goto out; } if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && filetype_is_barebox_image(filetype)) { - void *image; struct bbu_data data = { .devicefile = filename, - .imagefile = FASTBOOT_TMPFILE, .flags = BBU_FLAG_YES, }; @@ -947,43 +996,50 @@ fastboot_tx_print(f_fb, "INFOThis is a barebox image..."); - image = read_file(data.imagefile, &data.len); - if (!image) { - fastboot_tx_print(f_fb, "FAILreading barebox"); - return; + if (fastboot_download_to_buf(f_fb)) { + data.len = f_fb->download_size; + } else { + ret = read_file_2(data.imagefile, &data.len, &f_fb->buf, + f_fb->download_size); + if (ret) { + fastboot_tx_print(f_fb, "FAILreading barebox"); + goto out; + } } - data.image = image; + data.image = f_fb->buf; + data.imagefile = sourcefile; ret = barebox_update(&data); - free(image); - - if (ret) { + if (ret) fastboot_tx_print(f_fb, "FAILupdate barebox: %s", strerror(-ret)); - return; - } goto out; } copy: - ret = copy_file(FASTBOOT_TMPFILE, filename, 1); + 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); - unlink(FASTBOOT_TMPFILE); - - if (ret) { + if (ret) fastboot_tx_print(f_fb, "FAILwrite partition: %s", strerror(-ret)); - return; - } out: - fastboot_tx_print(f_fb, "OKAY"); + if (!ret) + fastboot_tx_print(f_fb, "OKAY"); + + free(f_fb->buf); + f_fb->buf = NULL; + + if (!fastboot_download_to_buf(f_fb)) + unlink(FASTBOOT_TMPFILE); } -static void cb_erase(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_erase(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; struct file_list_entry *fentry; int ret; const char *filename = NULL; @@ -1020,33 +1076,32 @@ struct cmd_dispatch_info { char *cmd; - void (*cb)(struct usb_ep *ep, struct usb_request *req, const char *opt); + void (*cb)(struct f_fastboot *f_fb, const char *opt); }; -static void fb_run_command(struct usb_ep *ep, struct usb_request *req, const char *cmd, +static void fb_run_command(struct f_fastboot *f_fb, const char *cmdbuf, const struct cmd_dispatch_info *cmds, int num_commands) { - void (*func_cb)(struct usb_ep *ep, struct usb_request *req, const char *cmd) = NULL; - struct f_fastboot *f_fb = req->context; + const struct cmd_dispatch_info *cmd; int i; console_countdown_abort(); for (i = 0; i < num_commands; i++) { - if (!strcmp_l1(cmds[i].cmd, cmd)) { - func_cb = cmds[i].cb; - cmd += strlen(cmds[i].cmd); - func_cb(ep, req, cmd); + cmd = &cmds[i]; + + if (!strcmp_l1(cmd->cmd, cmdbuf)) { + cmd->cb(f_fb, cmdbuf + strlen(cmd->cmd)); + return; } } - fastboot_tx_print(f_fb, "FAILunknown command %s", cmd); + fastboot_tx_print(f_fb, "FAILunknown command %s", cmdbuf); } -static void cb_oem_getenv(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_oem_getenv(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; const char *value; pr_debug("%s: \"%s\"\n", __func__, cmd); @@ -1057,9 +1112,8 @@ fastboot_tx_print(f_fb, "OKAY"); } -static void cb_oem_setenv(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_oem_setenv(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; char *var = xstrdup(cmd); char *value; int ret; @@ -1086,9 +1140,8 @@ fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret)); } -static void cb_oem_exec(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_oem_exec(struct f_fastboot *f_fb, const char *cmd) { - struct f_fastboot *f_fb = req->context; int ret; if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) { @@ -1118,11 +1171,11 @@ }, }; -static void cb_oem(struct usb_ep *ep, struct usb_request *req, const char *cmd) +static void cb_oem(struct f_fastboot *f_fb, const char *cmd) { pr_debug("%s: \"%s\"\n", __func__, cmd); - fb_run_command(ep, req, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info)); + fb_run_command(f_fb, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info)); } static const struct cmd_dispatch_info cmd_dispatch_info[] = { @@ -1155,15 +1208,23 @@ 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; *(cmdbuf + req->actual) = 0; - fb_run_command(ep, req, cmdbuf, cmd_dispatch_info, - ARRAY_SIZE(cmd_dispatch_info)); + 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: *cmdbuf = '\0'; req->actual = 0; memset(req->buf, 0, EP_BUFFER_SIZE); @@ -1172,7 +1233,8 @@ static int fastboot_globalvars_init(void) { - globalvar_add_simple_int("usbgadget.fastboot_max_download_size", + if (IS_ENABLED(USB_GADGET_FASTBOOT_SPARSE)) + globalvar_add_simple_int("usbgadget.fastboot_max_download_size", &fastboot_max_download_size, "%u"); return 0; diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 44969be..d6edfb8 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -128,6 +128,8 @@ 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; f_fastboot = usb_get_function(fi_fastboot); diff --git a/include/ubiformat.h b/include/ubiformat.h index 8305a85..8a7a16a 100644 --- a/include/ubiformat.h +++ b/include/ubiformat.h @@ -15,7 +15,14 @@ int ubi_ver; uint32_t image_seq; long long ec; + + /* + * Either set image if you have a file or set image_buf / image_size + * if you have a buffer. + */ const char *image; + const void *image_buf; + loff_t image_size; }; int ubiformat(struct mtd_info *mtd, struct ubiformat_args *args); diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h index ced890c..00c9d00 100644 --- a/include/usb/fastboot.h +++ b/include/usb/fastboot.h @@ -5,6 +5,8 @@ #include #include +struct f_fastboot; + /** * struct f_fastboot_opts - options to configure the fastboot gadget * @func_inst: The USB function instance to register on @@ -15,6 +17,21 @@ 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 + +int fastboot_tx_print(struct f_fastboot *f_fb, const char *fmt, ...); + #endif /* _USB_FASTBOOT_H */