diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Kconfig | 12 | ||||
-rw-r--r-- | common/Makefile | 3 | ||||
-rw-r--r-- | common/blspec.c | 16 | ||||
-rw-r--r-- | common/bootm.c | 31 | ||||
-rw-r--r-- | common/complete.c | 6 | ||||
-rw-r--r-- | common/efi/efi.c | 2 | ||||
-rw-r--r-- | common/fastboot.c | 59 | ||||
-rw-r--r-- | common/globalvar.c | 67 | ||||
-rw-r--r-- | common/hush.c | 6 | ||||
-rw-r--r-- | common/imd.c | 1 | ||||
-rw-r--r-- | common/kallsyms.c | 4 | ||||
-rw-r--r-- | common/poller.c | 11 | ||||
-rw-r--r-- | common/ratp/ratp.c | 57 | ||||
-rw-r--r-- | common/restart.c | 37 | ||||
-rw-r--r-- | common/slice.c | 335 | ||||
-rw-r--r-- | common/startup.c | 18 | ||||
-rw-r--r-- | common/tlsf.c | 29 | ||||
-rw-r--r-- | common/usbgadget.c | 21 | ||||
-rw-r--r-- | common/workqueue.c | 61 |
19 files changed, 708 insertions, 68 deletions
diff --git a/common/Kconfig b/common/Kconfig index b350f5c355..9e6918189b 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -411,6 +411,7 @@ choice select COMMAND_SUPPORT select PARAMETER select STDDEV + select CMD_SETENV help simple shell. No if/then, no return values from commands, no loops @@ -699,7 +700,6 @@ config IMD config IMD_TARGET bool "build bareboximd target tool" depends on IMD - depends on !SANDBOX config KERNEL_INSTALL_TARGET bool @@ -1375,11 +1375,11 @@ config PBL_BREAK source "lib/Kconfig.ubsan" -config KASAN - bool "KASAN: runtime memory debugger" - depends on HAVE_ARCH_KASAN +config ASAN + bool "ASAN: runtime memory debugger" + depends on HAVE_ARCH_ASAN help - Enables KASAN (KernelAddressSANitizer) - runtime memory debugger, + Enables ASAN (AddressSANitizer) - runtime memory debugger, designed to find out-of-bounds accesses and use-after-free bugs. config COMPILE_TEST @@ -1404,5 +1404,5 @@ config DDR_SPD bool select CRC_ITU_T -config HAVE_ARCH_KASAN +config HAVE_ARCH_ASAN bool diff --git a/common/Makefile b/common/Makefile index ad5146a301..c3ae3ca1b9 100644 --- a/common/Makefile +++ b/common/Makefile @@ -11,6 +11,8 @@ obj-y += bootsource.o obj-$(CONFIG_ELF) += elf.o obj-y += restart.o obj-y += poweroff.o +obj-y += slice.o +obj-y += workqueue.o obj-$(CONFIG_MACHINE_ID) += machine_id.o obj-$(CONFIG_AUTO_COMPLETE) += complete.o obj-y += version.o @@ -38,6 +40,7 @@ obj-$(CONFIG_GREGORIAN_CALENDER) += date.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_MALLOC_DLMALLOC) += dlmalloc.o obj-$(CONFIG_MALLOC_TLSF) += tlsf_malloc.o tlsf.o calloc.o +KASAN_SANITIZE_tlsf.o := n obj-$(CONFIG_MALLOC_DUMMY) += dummy_malloc.o calloc.o obj-$(CONFIG_MEMINFO) += meminfo.o obj-$(CONFIG_MENU) += menu.o diff --git a/common/blspec.c b/common/blspec.c index 9e1036c834..9499d32477 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -109,8 +109,11 @@ static void blspec_apply_oftree_overlays(const char *overlays, sep = freep = xstrdup(overlays); - while ((overlay = strsep(&sep, " "))) + while ((overlay = strsep(&sep, " "))) { + if (!*overlay) + continue; blspec_apply_oftree_overlay(overlay, abspath, dryrun); + } free(freep); } @@ -130,19 +133,18 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun) const char *overlays; const char *appendroot; struct bootm_data data = { - .initrd_address = UIMAGE_INVALID_ADDRESS, - .os_address = UIMAGE_SOME_ADDRESS, - .verbose = verbose, .dryrun = dryrun, }; globalvar_set_match("linux.bootargs.dyn.", ""); - globalvar_set_match("bootm.image", ""); - globalvar_set_match("bootm.oftree", ""); - globalvar_set_match("bootm.initrd", ""); + globalvar_set("bootm.image", ""); + globalvar_set("bootm.oftree", ""); + globalvar_set("bootm.initrd", ""); bootm_data_init_defaults(&data); + data.verbose = verbose || data.verbose; + devicetree = blspec_entry_var_get(entry, "devicetree"); initrd = blspec_entry_var_get(entry, "initrd"); options = blspec_entry_var_get(entry, "options"); diff --git a/common/bootm.c b/common/bootm.c index 7f22ca5ced..60b8bf10a8 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -65,6 +65,7 @@ void bootm_data_init_defaults(struct bootm_data *data) getenv_ul("global.bootm.image.loadaddr", &data->os_address); getenv_ul("global.bootm.initrd.loadaddr", &data->initrd_address); data->initrd_file = getenv_nonempty("global.bootm.initrd"); + data->root_dev = getenv_nonempty("global.bootm.root_dev"); data->verify = bootm_get_verify_mode(); data->appendroot = bootm_appendroot; data->provide_machine_id = bootm_provide_machine_id; @@ -232,7 +233,11 @@ int bootm_load_initrd(struct image_data *data, unsigned long load_address) ret = fit_open_image(data->os_fit, data->fit_config, "ramdisk", &initrd, &initrd_size); - + if (ret) { + pr_err("Cannot open ramdisk image in FIT image: %s\n", + strerror(-ret)); + return ret; + } data->initrd_res = request_sdram_region("initrd", load_address, initrd_size); @@ -711,7 +716,25 @@ int bootm_boot(struct bootm_data *bootm_data) if (bootm_data->appendroot) { char *rootarg; - rootarg = path_get_linux_rootarg(data->os_file); + if (bootm_data->root_dev) { + const char *root_dev_name = devpath_to_name(bootm_data->root_dev); + const struct cdev *root_cdev = cdev_by_name(root_dev_name); + + if (root_cdev && root_cdev->partuuid[0] != 0) { + rootarg = basprintf("root=PARTUUID=%s", root_cdev->partuuid); + } else { + rootarg = ERR_PTR(-EINVAL); + + if (!root_cdev) + pr_err("no cdev found for %s, cannot set root= option\n", + root_dev_name); + else if (!root_cdev->partuuid[0]) + pr_err("%s doesn't have a PARTUUID, cannot set root= option\n", + root_dev_name); + } + } else { + rootarg = path_get_linux_rootarg(data->os_file); + } if (!IS_ERR(rootarg)) { printf("Adding \"%s\" to Kernel commandline\n", rootarg); globalvar_add_simple("linux.bootargs.bootm.appendroot", @@ -802,6 +825,7 @@ static int bootm_init(void) globalvar_add_simple("bootm.image", NULL); globalvar_add_simple("bootm.image.loadaddr", NULL); globalvar_add_simple("bootm.oftree", NULL); + globalvar_add_simple("bootm.root_dev", NULL); globalvar_add_simple("bootm.tee", NULL); globalvar_add_simple_bool("bootm.appendroot", &bootm_appendroot); globalvar_add_simple_bool("bootm.provide_machine_id", &bootm_provide_machine_id); @@ -831,5 +855,6 @@ BAREBOX_MAGICVAR_NAMED(global_bootm_oftree, global.bootm.oftree, "bootm default BAREBOX_MAGICVAR_NAMED(global_bootm_tee, global.bootm.tee, "bootm default tee image"); BAREBOX_MAGICVAR_NAMED(global_bootm_verify, global.bootm.verify, "bootm default verify level"); BAREBOX_MAGICVAR_NAMED(global_bootm_verbose, global.bootm.verbose, "bootm default verbosity level (0=quiet)"); -BAREBOX_MAGICVAR_NAMED(global_bootm_appendroot, global.bootm.appendroot, "Add root= option to Kernel to mount rootfs from the device the Kernel comes from"); +BAREBOX_MAGICVAR_NAMED(global_bootm_appendroot, global.bootm.appendroot, "Add root= option to Kernel to mount rootfs from the device the Kernel comes from (default, device can be overridden via global.bootm.root_dev)"); +BAREBOX_MAGICVAR_NAMED(global_bootm_root_dev, global.bootm.root_dev, "bootm default root device (overrides default device in global.bootm.appendroot)"); BAREBOX_MAGICVAR_NAMED(global_bootm_provide_machine_id, global.bootm.provide_machine_id, "If true, add systemd.machine_id= with value of global.machine_id to Kernel"); diff --git a/common/complete.c b/common/complete.c index 919e5abc6a..36e10405c8 100644 --- a/common/complete.c +++ b/common/complete.c @@ -336,6 +336,12 @@ static int env_param_complete(struct string_list *sl, char *instr, int eval) return 0; } +int env_param_noeval_complete(struct string_list *sl, char *instr) +{ + return env_param_complete(sl, instr, 0); +} +EXPORT_SYMBOL(env_param_noeval_complete); + static int tab_pressed = 0; void complete_reset(void) diff --git a/common/efi/efi.c b/common/efi/efi.c index 6f55e3970e..01003dc00f 100644 --- a/common/efi/efi.c +++ b/common/efi/efi.c @@ -292,7 +292,7 @@ static void __noreturn efi_poweroff_system(struct poweroff_handler *handler) static int restart_register_feature(void) { - restart_handler_register_fn(efi_restart_system); + restart_handler_register_fn("efi", efi_restart_system); poweroff_handler_register_fn(efi_poweroff_system); return 0; diff --git a/common/fastboot.c b/common/fastboot.c index 302720c43d..bcfadfad3d 100644 --- a/common/fastboot.c +++ b/common/fastboot.c @@ -46,6 +46,8 @@ #define FASTBOOT_VERSION "0.4" static unsigned int fastboot_max_download_size = SZ_8M; +static int fastboot_bbu; +static char *fastboot_partitions; struct fb_variable { char *name; @@ -169,6 +171,8 @@ int fastboot_generic_init(struct fastboot *fb, bool export_bbu) struct file_list_entry *fentry; struct fb_variable *var; + INIT_LIST_HEAD(&fb->variables); + var = fb_addvar(fb, "version"); fb_setvar(var, "0.4"); var = fb_addvar(fb, "bootloader-version"); @@ -241,6 +245,7 @@ static char *fastboot_msg[] = { [FASTBOOT_MSG_FAIL] = "FAIL", [FASTBOOT_MSG_INFO] = "INFO", [FASTBOOT_MSG_DATA] = "DATA", + [FASTBOOT_MSG_NONE] = "", }; int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type, @@ -269,6 +274,7 @@ int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type, case FASTBOOT_MSG_INFO: pr_info("%pV\n", &vaf); break; + case FASTBOOT_MSG_NONE: case FASTBOOT_MSG_DATA: break; } @@ -283,6 +289,7 @@ int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type, static void cb_reboot(struct fastboot *fb, const char *cmd) { + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); restart_machine(); } @@ -335,6 +342,7 @@ int fastboot_handle_download_data(struct fastboot *fb, const void *buffer, void fastboot_download_finished(struct fastboot *fb) { close(fb->download_fd); + fb->download_fd = 0; printf("\n"); @@ -344,6 +352,18 @@ void fastboot_download_finished(struct fastboot *fb) fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); } +void fastboot_abort(struct fastboot *fb) +{ + if (fb->download_fd > 0) { + close(fb->download_fd); + fb->download_fd = 0; + } + + fb->active = false; + + unlink(fb->tempname); +} + static void cb_download(struct fastboot *fb, const char *cmd) { fb->download_size = simple_strtoul(cmd, NULL, 16); @@ -354,6 +374,11 @@ static void cb_download(struct fastboot *fb, const char *cmd) init_progression_bar(fb->download_size); + if (fb->download_fd > 0) { + pr_err("%s called and %s is still opened\n", __func__, fb->tempname); + close(fb->download_fd); + } + fb->download_fd = open(fb->tempname, O_WRONLY | O_CREAT | O_TRUNC); if (fb->download_fd < 0) { fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "internal error"); @@ -383,7 +408,7 @@ static void __maybe_unused cb_boot(struct fastboot *fb, const char *opt) fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Booting kernel..\n"); globalvar_set_match("linux.bootargs.dyn.", ""); - globalvar_set_match("bootm.image", ""); + globalvar_set("bootm.image", ""); data.os_file = fb->tempname; @@ -903,17 +928,43 @@ void fastboot_exec_cmd(struct fastboot *fb, const char *cmdbuf) ARRAY_SIZE(cmd_dispatch_info)); } +bool get_fastboot_bbu(void) +{ + return fastboot_bbu; +} + +const char *get_fastboot_partitions(void) +{ + return fastboot_partitions; +} + static int fastboot_globalvars_init(void) { if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) - globalvar_add_simple_int("usbgadget.fastboot_max_download_size", + globalvar_add_simple_int("fastboot.max_download_size", &fastboot_max_download_size, "%u"); + globalvar_add_simple_bool("fastboot.bbu", &fastboot_bbu); + globalvar_add_simple_string("fastboot.partitions", + &fastboot_partitions); + + globalvar_alias_deprecated("usbgadget.fastboot_function", + "fastboot.partitions"); + globalvar_alias_deprecated("usbgadget.fastboot_bbu", + "fastboot.bbu"); + globalvar_alias_deprecated("usbgadget.fastboot_max_download_size", + "fastboot.max_download_size"); return 0; } device_initcall(fastboot_globalvars_init); -BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size, - global.usbgadget.fastboot_max_download_size, +BAREBOX_MAGICVAR_NAMED(global_fastboot_max_download_size, + global.fastboot.max_download_size, "Fastboot maximum download size"); +BAREBOX_MAGICVAR_NAMED(global_fastboot_partitions, + global.fastboot.partitions, + "Partitions exported for update via fastboot"); +BAREBOX_MAGICVAR_NAMED(global_fastboot_bbu, + global.fastboot.bbu, + "Export barebox update handlers via fastboot"); diff --git a/common/globalvar.c b/common/globalvar.c index 98a028a68a..5bde86aad0 100644 --- a/common/globalvar.c +++ b/common/globalvar.c @@ -293,6 +293,53 @@ int nvvar_remove(const char *name) return ret; } +struct globalvar_deprecated { + char *newname; + char *oldname; + struct list_head list; +}; + +static LIST_HEAD(globalvar_deprecated_list); + +/* + * globalvar_alias_deprecated - add an alias + * + * @oldname: The old name for the variable + * @newname: The new name for the variable + * + * This function is a helper for globalvars that are renamed from one + * release to another. when a variable @oldname is found in the persistent + * environment a warning is issued and its value is written to @newname. + * + * Note that when both @oldname and @newname contain values then the values + * existing in @newname are overwritten. + */ +void globalvar_alias_deprecated(const char *oldname, const char *newname) +{ + struct globalvar_deprecated *gd; + + gd = xzalloc(sizeof(*gd)); + gd->newname = xstrdup(newname); + gd->oldname = xstrdup(oldname); + list_add_tail(&gd->list, &globalvar_deprecated_list); +} + +static const char *globalvar_new_name(const char *oldname) +{ + struct globalvar_deprecated *gd; + + list_for_each_entry(gd, &globalvar_deprecated_list, list) { + if (!strcmp(oldname, gd->oldname)) { + pr_warn("nv.%s is deprecated, converting to nv.%s\n", oldname, + gd->newname); + nv_dirty = 1; + return gd->newname; + } + } + + return oldname; +} + int nvvar_load(void) { char *val; @@ -308,6 +355,8 @@ int nvvar_load(void) return -ENOENT; while ((d = readdir(dir))) { + const char *n; + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; @@ -316,10 +365,11 @@ int nvvar_load(void) pr_debug("%s: Setting \"%s\" to \"%s\"\n", __func__, d->d_name, val); - ret = __nvvar_add(d->d_name, val); + n = globalvar_new_name(d->d_name); + ret = __nvvar_add(n, val); if (ret) pr_err("failed to create nv variable %s: %s\n", - d->d_name, strerror(-ret)); + n, strerror(-ret)); } closedir(dir); @@ -399,6 +449,11 @@ void globalvar_set_match(const char *match, const char *val) } } +void globalvar_set(const char *name, const char *val) +{ + dev_set_param(&global_device, name, val); +} + static int globalvar_simple_set(struct device_d *dev, struct param_d *p, const char *val) { struct device_d *rdev; @@ -514,7 +569,9 @@ int globalvar_add_simple_int(const char *name, int *value, return 0; } -int globalvar_add_simple_bool(const char *name, int *value) +int globalvar_add_bool(const char *name, + int (*set)(struct param_d *, void *), + int *value, void *priv) { struct param_d *p; int ret; @@ -523,8 +580,8 @@ int globalvar_add_simple_bool(const char *name, int *value) if (ret) return ret; - p = dev_add_param_bool(&global_device, name, NULL, NULL, - value, NULL); + p = dev_add_param_bool(&global_device, name, set, NULL, + value, priv); if (IS_ERR(p)) return PTR_ERR(p); diff --git a/common/hush.c b/common/hush.c index c24b2c7cd2..ec0d5129b7 100644 --- a/common/hush.c +++ b/common/hush.c @@ -121,6 +121,7 @@ #include <libbb.h> #include <password.h> #include <glob.h> +#include <slice.h> #include <getopt.h> #include <libfile.h> #include <libbb.h> @@ -460,7 +461,12 @@ static void get_user_input(struct in_str *i) else prompt = CONFIG_PROMPT_HUSH_PS2; + command_slice_release(); + n = readline(prompt, console_buffer, CONFIG_CBSIZE); + + command_slice_acquire(); + if (n == -1 ) { i->interrupt = 1; n = 0; diff --git a/common/imd.c b/common/imd.c index 96496514a5..0644e6d3bf 100644 --- a/common/imd.c +++ b/common/imd.c @@ -312,6 +312,7 @@ static int imd_calculate_crc32(void *input, const struct imd_header *imd_start, const struct imd_header *imd; int length; int end_ofs = (char *)imd_start - (char *)input + sizeof(char) * 8; + *imd_crc = NULL; /* search the checksum imd token */ imd_for_each(imd_start, imd) { diff --git a/common/kallsyms.c b/common/kallsyms.c index e15dec5dfc..2c16ab2884 100644 --- a/common/kallsyms.c +++ b/common/kallsyms.c @@ -15,8 +15,8 @@ extern const unsigned long kallsyms_markers[] __attribute__((weak)); static inline int is_kernel_text(unsigned long addr) { - if ((addr >= (unsigned long)_stext && addr <= (unsigned long)_etext)) - return 1; + if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end) + return 1; return 0; } diff --git a/common/poller.c b/common/poller.c index 95f828b439..50c518697b 100644 --- a/common/poller.c +++ b/common/poller.c @@ -12,9 +12,11 @@ #include <param.h> #include <poller.h> #include <clock.h> +#include <work.h> +#include <slice.h> static LIST_HEAD(poller_list); -static int poller_active; +int poller_active; int poller_register(struct poller_struct *poller, const char *name) { @@ -110,15 +112,22 @@ int poller_async_unregister(struct poller_async *pa) void poller_call(void) { struct poller_struct *poller, *tmp; + bool run_workqueues = !slice_acquired(&command_slice); if (poller_active) return; + command_slice_acquire(); + + if (run_workqueues) + wq_do_all_works(); + poller_active = 1; list_for_each_entry_safe(poller, tmp, &poller_list, list) poller->func(poller); + command_slice_release(); poller_active = 0; } diff --git a/common/ratp/ratp.c b/common/ratp/ratp.c index 9ca299eef2..424c9406d2 100644 --- a/common/ratp/ratp.c +++ b/common/ratp/ratp.c @@ -22,6 +22,7 @@ #include <environment.h> #include <kfifo.h> #include <poller.h> +#include <work.h> #include <linux/sizes.h> #include <ratp_bb.h> #include <fs.h> @@ -49,9 +50,11 @@ struct ratp_ctx { struct ratp_bb_pkt *fs_rx; struct poller_struct poller; + struct work_queue wq; bool console_registered; bool poller_registered; + bool wq_registered; }; static int compare_ratp_command(struct list_head *a, struct list_head *b) @@ -193,9 +196,14 @@ static int ratp_bb_send_command_return(struct ratp_ctx *ctx, uint32_t errno) return ret; } -static char *ratp_command; static struct ratp_ctx *ratp_ctx; +struct ratp_work { + struct work_struct work; + struct ratp_ctx *ctx; + char *command; +}; + static int ratp_bb_dispatch(struct ratp_ctx *ctx, const void *buf, int len) { const struct ratp_bb *rbb = buf; @@ -204,6 +212,7 @@ static int ratp_bb_dispatch(struct ratp_ctx *ctx, const void *buf, int len) int ret = 0; uint16_t type = be16_to_cpu(rbb->type); struct ratp_command *cmd; + struct ratp_work *rw; /* See if there's a command registered to this type */ cmd = find_ratp_request(type); @@ -221,12 +230,16 @@ static int ratp_bb_dispatch(struct ratp_ctx *ctx, const void *buf, int len) switch (type) { case BB_RATP_TYPE_COMMAND: - if (!IS_ENABLED(CONFIG_CONSOLE_RATP) || ratp_command) + if (!IS_ENABLED(CONFIG_CONSOLE_RATP)) return 0; - ratp_command = xstrndup((const char *)rbb->data, dlen); - ratp_ctx = ctx; - pr_debug("got command: %s\n", ratp_command); + rw = xzalloc(sizeof(*rw)); + rw->ctx = ctx; + rw->command = xstrndup((const char *)rbb->data, dlen); + + wq_queue_work(&ctx->wq, &rw->work); + + pr_debug("got command: %s\n", rw->command); break; @@ -297,21 +310,20 @@ static void ratp_console_putc(struct console_device *cdev, char c) kfifo_putc(ctx->console_transmit_fifo, c); } -void barebox_ratp_command_run(void) +static void ratp_command_run(struct work_struct *w) { + struct ratp_work *rw = container_of(w, struct ratp_work, work); + struct ratp_ctx *ctx = rw->ctx; int ret; - if (!ratp_command) - return; - - pr_debug("running command: %s\n", ratp_command); + pr_debug("running command: %s\n", rw->command); - ret = run_command(ratp_command); + ret = run_command(rw->command); - free(ratp_command); - ratp_command = NULL; + free(rw->command); + free(rw); - ratp_bb_send_command_return(ratp_ctx, ret); + ratp_bb_send_command_return(ctx, ret); } static const char *ratpfs_mount_path; @@ -336,6 +348,9 @@ static void ratp_unregister(struct ratp_ctx *ctx) if (ctx->poller_registered) poller_unregister(&ctx->poller); + if (ctx->wq_registered) + wq_unregister(&ctx->wq); + ratp_close(&ctx->ratp); console_set_active(ctx->cdev, ctx->old_active); @@ -368,6 +383,7 @@ static void ratp_poller(struct poller_struct *poller) ret = ratp_poll(&ctx->ratp); if (ret == -EINTR) goto out; + if (ratp_closed(&ctx->ratp)) goto out; @@ -423,6 +439,14 @@ int barebox_ratp_fs_call(struct ratp_bb_pkt *tx, struct ratp_bb_pkt **rx) return 0; } +static void ratp_work_cancel(struct work_struct *w) +{ + struct ratp_work *rw = container_of(w, struct ratp_work, work); + + free(rw->command); + free(rw); +} + int barebox_ratp(struct console_device *cdev) { int ret; @@ -466,6 +490,11 @@ int barebox_ratp(struct console_device *cdev) if (ret < 0) goto out; + ctx->wq.fn = ratp_command_run; + ctx->wq.cancel = ratp_work_cancel; + wq_register(&ctx->wq); + ctx->wq_registered = true; + ret = poller_register(&ctx->poller, "ratp"); if (ret) goto out; diff --git a/common/restart.c b/common/restart.c index b19ae54657..2bf7b166b0 100644 --- a/common/restart.c +++ b/common/restart.c @@ -19,6 +19,7 @@ #include <of.h> static LIST_HEAD(restart_handler_list); +static unsigned resetidx; /** * restart_handler_register() - register a handler for restarting the system @@ -31,7 +32,7 @@ static LIST_HEAD(restart_handler_list); int restart_handler_register(struct restart_handler *rst) { if (!rst->name) - rst->name = RESTART_DEFAULT_NAME; + rst->name = basprintf("reset%u", resetidx); if (!rst->priority) rst->priority = RESTART_DEFAULT_PRIORITY; @@ -40,11 +41,13 @@ int restart_handler_register(struct restart_handler *rst) pr_debug("registering restart handler \"%s\" with priority %d\n", rst->name, rst->priority); + resetidx++; return 0; } /** * restart_handler_register_fn() - register a handler function + * @name: restart method name or NULL if name should be auto-generated * @restart_fn: The restart function * * convenience wrapper for restart_handler_register() to register a handler @@ -52,13 +55,15 @@ int restart_handler_register(struct restart_handler *rst) * * return: 0 for success or negative error code */ -int restart_handler_register_fn(void (*restart_fn)(struct restart_handler *)) +int restart_handler_register_fn(const char *name, + void (*restart_fn)(struct restart_handler *)) { struct restart_handler *rst; int ret; rst = xzalloc(sizeof(*rst)); + rst->name = xstrdup(name); rst->restart = restart_fn; ret = restart_handler_register(rst); @@ -70,20 +75,33 @@ int restart_handler_register_fn(void (*restart_fn)(struct restart_handler *)) } /** - * restart_machine() - reset the whole system + * restart_handler_get_by_name() - reset the whole system */ -void __noreturn restart_machine(void) +struct restart_handler *restart_handler_get_by_name(const char *name) { struct restart_handler *rst = NULL, *tmp; unsigned int priority = 0; list_for_each_entry(tmp, &restart_handler_list, list) { + if (name && tmp->name && strcmp(name, tmp->name)) + continue; if (tmp->priority > priority) { priority = tmp->priority; rst = tmp; } } + return rst; +} + +/** + * restart_machine() - reset the whole system + */ +void __noreturn restart_machine(void) +{ + struct restart_handler *rst; + + rst = restart_handler_get_by_name(NULL); if (rst) { pr_debug("%s: using restart handler %s\n", __func__, rst->name); console_flush(); @@ -107,3 +125,14 @@ unsigned int of_get_restart_priority(struct device_node *node) return priority; } + +/* + * restart_handlers_print - print informations about all restart handlers + */ +void restart_handlers_print(void) +{ + struct restart_handler *tmp; + + list_for_each_entry(tmp, &restart_handler_list, list) + printf("%-20s %6d\n", tmp->name, tmp->priority); +} diff --git a/common/slice.c b/common/slice.c new file mode 100644 index 0000000000..9d7e0d16cf --- /dev/null +++ b/common/slice.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define pr_fmt(fmt) "slice: " fmt + +#include <common.h> +#include <init.h> +#include <slice.h> + +/* + * slices, the barebox idea of locking + * + * barebox has pollers which execute code in the background whenever one of the + * delay functions (udelay, mdelay, ...) or is_timeout() are called. This + * introduces resource problems when some device triggers a poller by calling + * a delay function and then the poller code calls into the same device again. + * + * As an example consider a I2C GPIO expander which drives a LED which shall + * be used as a heartbeat LED: + * + * poller -> LED on/off -> GPIO high/low -> I2C transfer + * + * The I2C controller has a timeout loop using is_timeout() and thus can trigger + * a poller run. With this the following can happen during an unrelated I2C + * transfer: + * + * I2C transfer -> is_timeout() -> poller -> LED on/off -> GPIO high/low -> I2C transfer + * + * We end up with issuing an I2C transfer during another I2C transfer and + * things go downhill. + * + * Due to the lack of interrupts we can't do real locking in barebox. We use + * a mechanism called slices instead. A slice describes a resource to which + * other slices can be attached. Whenever a slice is needed it must be acquired. + * Acquiring a slice never fails, it just increases the acquired counter of + * the slice and its dependent slices. when a slice shall be used inside a + * poller it must first be tested if the slice is already in use. If it is, + * we can't do the operation on the slice now and must return and hope that + * we have more luck in the next poller call. + * + * slices can be attached other slices as dependencies. In the example above + * LED driver would add a dependency to the GPIO controller and the GPIO driver + * would add a dependency to the I2C bus: + * + * GPIO driver probe: + * + * slice_depends_on(&gpio->slice, i2c_device_slice(i2cdev)); + * + * LED driver probe: + * + * slice_depends_on(&led->slice, gpio_slice(gpio)); + * + * The GPIO code would call slice_acquire(&gpio->slice) before doing any + * operation on the GPIO chip providing this GPIO, likewise the I2C core + * would call slice_acquire(&i2cbus->slice) before doing an operation on + * this I2C bus. + * + * The heartbeat poller code would call slice_acquired(led_slice(led)) and + * only continue when the slice is not acquired. + */ + +/* + * slices are not required to have a device and thus a name, but it really + * helps debugging when it has one. + */ +static const char *slice_name(struct slice *slice) +{ + return slice->name; +} + +static void __slice_acquire(struct slice *slice, int add) +{ + slice->acquired += add; + + if (slice->acquired < 0) { + pr_err("Slice %s acquired count drops below zero\n", + slice_name(slice)); + dump_stack(); + slice->acquired = 0; + return; + } +} + +/** + * slice_acquire: acquire a slice + * @slice: The slice to acquire + * + * This acquires a slice and its dependencies + */ +void slice_acquire(struct slice *slice) +{ + __slice_acquire(slice, 1); +} + +/** + * slice_release: release a slice + * @slice: The slice to release + * + * This releases a slice and its dependencies + */ +void slice_release(struct slice *slice) +{ + __slice_acquire(slice, -1); +} + +/** + * slice_acquired: test if a slice is acquired + * @slice: The slice to test + * + * This tests if a slice is acquired. Returns true if it is, false otherwise + */ +bool slice_acquired(struct slice *slice) +{ + struct slice_entry *se; + bool ret = false; + + if (slice->acquired > 0) + return true; + + if (slice->acquired < 0) { + pr_err("Recursive dependency detected in slice %s\n", + slice_name(slice)); + panic("Cannot continue"); + } + + slice->acquired = -1; + + list_for_each_entry(se, &slice->deps, list) + if (slice_acquired(se->slice)) { + ret = true; + break; + } + + slice->acquired = 0; + + return ret; +} + +static void __slice_debug_acquired(struct slice *slice, int level) +{ + struct slice_entry *se; + + pr_debug("%*s%s: %d\n", level * 4, "", + slice_name(slice), + slice->acquired); + + list_for_each_entry(se, &slice->deps, list) + __slice_debug_acquired(se->slice, level + 1); +} + +/** + * slice_debug_acquired: print the acquired state of a slice + * + * @slice: The slice to print + * + * This prints the acquired state of a slice and its dependencies. + */ +void slice_debug_acquired(struct slice *slice) +{ + if (!slice_acquired(slice)) + return; + + __slice_debug_acquired(slice, 0); +} + +/** + * slice_depends_on: Add a dependency to a slice + * + * @slice: The slice to add the dependency to + * @dep: The slice @slice depends on + * + * This makes @slice dependent on @dep. In other words, acquiring @slice + * will lead to also acquiring @dep. + */ +void slice_depends_on(struct slice *slice, struct slice *dep) +{ + struct slice_entry *se = xzalloc(sizeof(*se)); + + pr_debug("Adding dependency %s (%d) to slice %s (%d)\n", + slice_name(dep), dep->acquired, + slice_name(slice), slice->acquired); + + se->slice = dep; + + list_add_tail(&se->list, &slice->deps); +} + +static LIST_HEAD(slices); + +/** + * slice_init - initialize a slice before usage + * @slice: The slice to initialize + * @name: The name of the slice + * + * This initializes a slice before usage. @name should be a meaningful name, when + * a device context is available to the caller it is recommended to pass its + * dev_name() as name argument. + */ +void slice_init(struct slice *slice, const char *name) +{ + INIT_LIST_HEAD(&slice->deps); + slice->name = xstrdup(name); + list_add_tail(&slice->list, &slices); +} + +/** + * slice_exit: Remove a slice + * @slice: The slice to remove the dependency from + */ +void slice_exit(struct slice *slice) +{ + struct slice *s; + struct slice_entry *se, *tmp; + + list_del(&slice->list); + + /* remove our dependencies */ + list_for_each_entry_safe(se, tmp, &slice->deps, list) { + list_del(&se->list); + free(se); + } + + /* remove this slice from other slices dependencies */ + list_for_each_entry(s, &slices, list) { + list_for_each_entry_safe(se, tmp, &s->deps, list) { + if (se->slice == slice) { + list_del(&se->list); + free(se); + } + } + } + + free(slice->name); +} + +struct slice command_slice; + +/** + * command_slice_acquire - acquire the command slice + * + * The command slice is acquired by default. It is only released + * at certain points we know it's safe to execute code in the + * background, like when the shell is waiting for input. + */ +void command_slice_acquire(void) +{ + slice_acquire(&command_slice); +} + +/** + * command_slice_release - release the command slice + */ +void command_slice_release(void) +{ + slice_release(&command_slice); +} + +static int command_slice_init(void) +{ + slice_init(&command_slice, "command"); + slice_acquire(&command_slice); + return 0; +} + +pure_initcall(command_slice_init); + +#if defined CONFIG_CMD_SLICE + +#include <command.h> +#include <getopt.h> + +static void slice_info(void) +{ + struct slice *slice; + struct slice_entry *se; + + list_for_each_entry(slice, &slices, list) { + printf("slice %s: acquired=%d\n", + slice_name(slice), slice->acquired); + list_for_each_entry(se, &slice->deps, list) + printf(" dep: %s\n", slice_name(se->slice)); + } +} + +static int __slice_acquire_name(const char *name, int add) +{ + struct slice *slice; + + list_for_each_entry(slice, &slices, list) { + if (!strcmp(slice->name, name)) { + __slice_acquire(slice, add); + return 0; + } + } + + printf("No such slice: %s\n", name); + + return 1; +} + +BAREBOX_CMD_HELP_START(slice) +BAREBOX_CMD_HELP_TEXT("slice debugging command") +BAREBOX_CMD_HELP_TEXT("") +BAREBOX_CMD_HELP_TEXT("Options:") +BAREBOX_CMD_HELP_OPT ("-i", "Print information about slices") +BAREBOX_CMD_HELP_OPT ("-a <devname>", "acquire a slice") +BAREBOX_CMD_HELP_OPT ("-r <devname>", "release a slice") +BAREBOX_CMD_HELP_END + +static int do_slice(int argc, char *argv[]) +{ + int opt; + + while ((opt = getopt(argc, argv, "a:r:i")) > 0) { + switch (opt) { + case 'a': + return __slice_acquire_name(optarg, 1); + case 'r': + return __slice_acquire_name(optarg, -1); + case 'i': + slice_info(); + return 0; + } + } + + return COMMAND_ERROR_USAGE; +} + +BAREBOX_CMD_START(slice) + .cmd = do_slice, + BAREBOX_CMD_DESC("debug slices") + BAREBOX_CMD_OPTS("[-ari]") + BAREBOX_CMD_GROUP(CMD_GRP_MISC) + BAREBOX_CMD_HELP(cmd_slice_help) +BAREBOX_CMD_END +#endif diff --git a/common/startup.c b/common/startup.c index 1c58e41288..ea7ce6b8da 100644 --- a/common/startup.c +++ b/common/startup.c @@ -34,6 +34,7 @@ #include <debug_ll.h> #include <fs.h> #include <errno.h> +#include <slice.h> #include <linux/stat.h> #include <envfs.h> #include <magicvar.h> @@ -272,8 +273,10 @@ enum autoboot_state do_autoboot_countdown(void) break; } + command_slice_release(); ret = console_countdown(global_autoboot_timeout, flags, abortkeys, &outkey); + command_slice_acquire(); if (ret == 0) autoboot_state = AUTOBOOT_BOOT; @@ -366,6 +369,19 @@ static int run_init(void) return 0; } +typedef void (*ctor_fn_t)(void); + +/* Call all constructor functions linked into the kernel. */ +static void do_ctors(void) +{ +#ifdef CONFIG_CONSTRUCTORS + ctor_fn_t *fn = (ctor_fn_t *) __ctors_start; + + for (; fn < (ctor_fn_t *) __ctors_end; fn++) + (*fn)(); +#endif +} + int (*barebox_main)(void); void __noreturn start_barebox(void) @@ -376,6 +392,8 @@ void __noreturn start_barebox(void) if (!IS_ENABLED(CONFIG_SHELL_NONE) && IS_ENABLED(CONFIG_COMMAND_SUPPORT)) barebox_main = run_init; + do_ctors(); + for (initcall = __barebox_initcalls_start; initcall < __barebox_initcalls_end; initcall++) { pr_debug("initcall-> %pS\n", *initcall); diff --git a/common/tlsf.c b/common/tlsf.c index 86cc684ab6..4247a9d3c7 100644 --- a/common/tlsf.c +++ b/common/tlsf.c @@ -3,9 +3,14 @@ #include <string.h> #include <tlsf.h> #include "tlsfbits.h" +#include <linux/kasan.h> #define CHAR_BIT 8 +#ifndef CONFIG_KASAN +#define __memcpy memcpy +#endif + /* ** Constants. */ @@ -529,7 +534,7 @@ static void block_trim_free(control_t* control, block_header_t* block, size_t si } /* Trim any trailing block space off the end of a used block, return to pool. */ -static void block_trim_used(control_t* control, block_header_t* block, size_t size) +static void block_trim_used(control_t* control, block_header_t* block, size_t size, size_t used) { tlsf_assert(!block_is_free(block) && "block must be used"); if (block_can_split(block, size)) @@ -541,6 +546,10 @@ static void block_trim_used(control_t* control, block_header_t* block, size_t si remaining_block = block_merge_next(control, remaining_block); block_insert(control, remaining_block); } + + kasan_poison_shadow(&block->size, size + 2 * sizeof(size_t), + KASAN_KMALLOC_REDZONE); + kasan_unpoison_shadow(block_to_ptr(block), used); } static block_header_t* block_trim_free_leading(control_t* control, block_header_t* block, size_t size) @@ -589,7 +598,8 @@ static block_header_t* block_locate_free(control_t* control, size_t size) return block; } -static void* block_prepare_used(control_t* control, block_header_t* block, size_t size) +static void* block_prepare_used(control_t* control, block_header_t* block, + size_t size, size_t used) { void* p = 0; if (block) @@ -598,6 +608,10 @@ static void* block_prepare_used(control_t* control, block_header_t* block, size_ block_trim_free(control, block, size); block_mark_as_used(block); p = block_to_ptr(block); + + kasan_poison_shadow(&block->size, size + 2 * sizeof(size_t), + KASAN_KMALLOC_REDZONE); + kasan_unpoison_shadow(p, used); } return p; } @@ -907,6 +921,7 @@ tlsf_t tlsf_create_with_pool(void* mem, size_t bytes) { tlsf_t tlsf = tlsf_create(mem); tlsf_add_pool(tlsf, (char*)mem + tlsf_size(), bytes - tlsf_size()); + kasan_poison_shadow(mem, bytes, KASAN_TAG_INVALID); return tlsf; } @@ -926,7 +941,8 @@ void* tlsf_malloc(tlsf_t tlsf, size_t size) control_t* control = tlsf_cast(control_t*, tlsf); const size_t adjust = adjust_request_size(size, ALIGN_SIZE); block_header_t* block = block_locate_free(control, adjust); - return block_prepare_used(control, block, adjust); + + return block_prepare_used(control, block, adjust, size); } void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size) @@ -983,7 +999,7 @@ void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size) } } - return block_prepare_used(control, block, adjust); + return block_prepare_used(control, block, adjust, size); } void tlsf_free(tlsf_t tlsf, void* ptr) @@ -994,6 +1010,7 @@ void tlsf_free(tlsf_t tlsf, void* ptr) control_t* control = tlsf_cast(control_t*, tlsf); block_header_t* block = block_from_ptr(ptr); tlsf_assert(!block_is_free(block) && "block already marked as free"); + kasan_poison_shadow(ptr, block_size(block), 0xff); block_mark_as_free(block); block = block_merge_prev(control, block); block = block_merge_next(control, block); @@ -1050,7 +1067,7 @@ void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) if (p) { const size_t minsize = tlsf_min(cursize, size); - memcpy(p, ptr, minsize); + __memcpy(p, ptr, minsize); tlsf_free(tlsf, ptr); } } @@ -1064,7 +1081,7 @@ void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) } /* Trim the resulting block and return the original pointer. */ - block_trim_used(control, block, adjust); + block_trim_used(control, block, adjust, size); p = ptr; } } diff --git a/common/usbgadget.c b/common/usbgadget.c index a8f104cf1c..b4f4ba04ca 100644 --- a/common/usbgadget.c +++ b/common/usbgadget.c @@ -30,8 +30,6 @@ static int autostart; static int acm; static char *dfu_function; -static char *fastboot_function; -static int fastboot_bbu; static struct file_list *parse(const char *files) { @@ -51,13 +49,14 @@ int usbgadget_register(bool dfu, const char *dfu_opts, int ret; struct device_d *dev; struct f_multi_opts *opts; + const char *fastboot_partitions = get_fastboot_partitions(); if (dfu && !dfu_opts && dfu_function && *dfu_function) dfu_opts = dfu_function; - if (fastboot && !fastboot_opts && - fastboot_function && *fastboot_function) - fastboot_opts = fastboot_function; + if (IS_ENABLED(CONFIG_FASTBOOT_BASE) && fastboot && !fastboot_opts && + fastboot_partitions && *fastboot_partitions) + fastboot_opts = fastboot_partitions; if (!dfu_opts && !fastboot_opts && !acm) return COMMAND_ERROR_USAGE; @@ -104,6 +103,8 @@ int usbgadget_register(bool dfu, const char *dfu_opts, static int usbgadget_autostart(void) { + bool fastboot_bbu = get_fastboot_bbu(); + if (!IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART) || !autostart) return 0; @@ -116,12 +117,8 @@ static int usbgadget_globalvars_init(void) if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART)) { globalvar_add_simple_bool("usbgadget.autostart", &autostart); globalvar_add_simple_bool("usbgadget.acm", &acm); - globalvar_add_simple_bool("usbgadget.fastboot_bbu", - &fastboot_bbu); } globalvar_add_simple_string("usbgadget.dfu_function", &dfu_function); - globalvar_add_simple_string("usbgadget.fastboot_function", - &fastboot_function); return 0; } @@ -136,9 +133,3 @@ BAREBOX_MAGICVAR_NAMED(global_usbgadget_acm, BAREBOX_MAGICVAR_NAMED(global_usbgadget_dfu_function, global.usbgadget.dfu_function, "usbgadget: Create DFU function"); -BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_function, - global.usbgadget.fastboot_function, - "usbgadget: Create Android Fastboot function"); -BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_bbu, - global.usbgadget.fastboot_bbu, - "usbgadget: export barebox update handlers via fastboot"); diff --git a/common/workqueue.c b/common/workqueue.c new file mode 100644 index 0000000000..dc6941dec1 --- /dev/null +++ b/common/workqueue.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <common.h> +#include <work.h> + +static void wq_do_pending_work(struct work_queue *wq) +{ + struct work_struct *work, *tmp; + + list_for_each_entry_safe(work, tmp, &wq->work, list) { + if (work->delayed && get_time_ns() < work->timeout) + continue; + + list_del(&work->list); + wq->fn(work); + } +} + +void wq_cancel_work(struct work_queue *wq) +{ + struct work_struct *work, *tmp; + + list_for_each_entry_safe(work, tmp, &wq->work, list) { + list_del(&work->list); + wq->cancel(work); + } +} + +static LIST_HEAD(work_queues); + +/** + * wq_do_all_works - do all pending work + * + * This calls all pending work functions + */ +void wq_do_all_works(void) +{ + struct work_queue *wq; + + list_for_each_entry(wq, &work_queues, list) + wq_do_pending_work(wq); +} + +/** + * wq_register - register a new work queue + * @wq: The work queue + */ +void wq_register(struct work_queue *wq) +{ + INIT_LIST_HEAD(&wq->work); + list_add_tail(&wq->list, &work_queues); +} + +/** + * wq_unregister - unregister a work queue + * @wq: The work queue + */ +void wq_unregister(struct work_queue *wq) +{ + wq_cancel_work(wq); + list_del(&wq->list); +} |