diff --git a/Documentation/user/booting-linux.rst b/Documentation/user/booting-linux.rst index 98628fa..f26299e 100644 --- a/Documentation/user/booting-linux.rst +++ b/Documentation/user/booting-linux.rst @@ -99,6 +99,20 @@ Kernel command line: mtdparts=physmap-flash.0:512k(bootloader),512k(env),4M(kernel),-(root); mxc_nand:1M(bootloader),1M(env),4M(kernel),-(root) +Creating root= options for the Kernel +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It's a common case that the Linux Kernel is loaded from a filesystem +that later becomes the root filesystem for the Kernel. For several +filesystems barebox can automatically append a suitable root= option +to the Kernel command line. This is done when ``global.bootm.appendroot`` +is true. How the root= option is appended depends on the device type +and filesystem the kernel is booted from. For disk like devices (SD/MMC, +ATA) the partition UUID will be used, the root= option will be something +like ``root=PARTUUID=deadbeef-1``. For UBIFS fileystems it will be +``root=ubi0:volname ubi.mtd=mtdpartname rootfstype=ubifs``. NFS +filesystems will result in ``root=/dev/nfs nfsroot=ip:/path/to/nfsroot,v3,tcp``. +The ``v3,tcp`` part is configurable in ``global.linux.rootnfsopts``. The boot command ---------------- diff --git a/commands/bootm.c b/commands/bootm.c index 7a19fa2..bfec62c 100644 --- a/commands/bootm.c +++ b/commands/bootm.c @@ -166,6 +166,7 @@ BAREBOX_MAGICVAR_NAMED(global_bootm_initrd_loadaddr, global.bootm.initrd.loadaddr, "bootm default initrd loadaddr"); BAREBOX_MAGICVAR_NAMED(global_bootm_oftree, global.bootm.oftree, "bootm default oftree"); BAREBOX_MAGICVAR_NAMED(global_bootm_verify, global.bootm.verify, "bootm default verify level"); +BAREBOX_MAGICVAR_NAMED(global_bootm_appendroot, global.bootm.appendroot, "Add root= option to Kernel to mount rootfs from the device the Kernel comes from"); static struct binfmt_hook binfmt_uimage_hook = { .type = filetype_uimage, diff --git a/common/blspec.c b/common/blspec.c index 1800556..ac8f512 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -637,29 +637,6 @@ return blspec_scan_device(blspec, dev); } -static int blspec_append_root(struct blspec_entry *entry) -{ - const char *appendroot; - char *rootarg; - - appendroot = blspec_entry_var_get(entry, "linux-appendroot"); - if (!appendroot || strcmp(appendroot, "true")) - return 0; - - rootarg = path_get_linux_rootarg(entry->rootpath); - if (IS_ERR(rootarg)) { - pr_err("Getting root argument for %s failed with: %s\n", - entry->rootpath, strerrorp(rootarg)); - return PTR_ERR(rootarg); - } - - globalvar_add_simple("linux.bootargs.dyn.blspec.appendroot", rootarg); - - free(rootarg); - - return 0; -} - /* * blspec_boot - boot an entry * @@ -671,6 +648,7 @@ { int ret; const char *abspath, *devicetree, *options, *initrd, *linuximage; + const char *appendroot; struct bootm_data data = { .initrd_address = UIMAGE_INVALID_ADDRESS, .os_address = UIMAGE_SOME_ADDRESS, @@ -709,9 +687,18 @@ globalvar_add_simple("linux.bootargs.dyn.blspec", options); - ret = blspec_append_root(entry); - if (ret) - goto err_out; + appendroot = blspec_entry_var_get(entry, "linux-appendroot"); + if (appendroot) { + int val; + + ret = strtobool(appendroot, &val); + if (ret) { + pr_err("Invalid value \"%s\" for appendroot option\n", + appendroot); + goto err_out; + } + data.appendroot = val; + } pr_info("booting %s from %s\n", blspec_entry_var_get(entry, "title"), entry->cdev ? dev_name(entry->cdev->dev) : "none"); diff --git a/common/bootm.c b/common/bootm.c index 6d22aab..cad8c73 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -48,6 +48,8 @@ return NULL; } +static int bootm_appendroot; + void bootm_data_init_defaults(struct bootm_data *data) { data->initrd_address = UIMAGE_INVALID_ADDRESS; @@ -58,6 +60,7 @@ getenv_ul("global.bootm.initrd.loadaddr", &data->initrd_address); data->initrd_file = getenv_nonempty("global.bootm.initrd"); data->verify = bootm_get_verify_mode(); + data->appendroot = bootm_appendroot; } static enum bootm_verify bootm_verify_mode = BOOTM_VERIFY_HASH; @@ -576,6 +579,18 @@ } } + if (bootm_data->appendroot) { + char *rootarg; + + 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", + rootarg); + free(rootarg); + } + } + printf("\nLoading %s '%s'", file_type_to_string(os_type), data->os_file); if (os_type == filetype_uimage && @@ -621,6 +636,7 @@ if (data->of_root_node && data->of_root_node != of_get_root_node()) of_delete_node(data->of_root_node); + globalvar_remove("linux.bootargs.bootm.appendroot"); free(data->os_file); free(data->oftree_file); free(data->initrd_file); @@ -634,6 +650,7 @@ globalvar_add_simple("bootm.image", NULL); globalvar_add_simple("bootm.image.loadaddr", NULL); globalvar_add_simple("bootm.oftree", NULL); + globalvar_add_simple_bool("bootm.appendroot", &bootm_appendroot); if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD)) { globalvar_add_simple("bootm.initrd", NULL); globalvar_add_simple("bootm.initrd.loadaddr", NULL); diff --git a/common/env.c b/common/env.c index c98ed73..d6cab52 100644 --- a/common/env.c +++ b/common/env.c @@ -28,6 +28,7 @@ #include #include #include +#include #include static struct env_context root = { @@ -323,20 +324,25 @@ } EXPORT_SYMBOL(getenv_uint); +/** + * getenv_bool - get a boolean value from an environment variable + * @var - Variable name + * @val - The boolean value returned. + * + * This function treats + * - any positive (nonzero) number as true + * - "0" as false + * - "true" (case insensitive) as true + * - "false" (case insensitive) as false + * + * Returns 0 for success or negative error code if the variable does + * not exist or contains something this function does not recognize + * as true or false. + */ int getenv_bool(const char *var, int *val) { const char *valstr = getenv(var); - if (!valstr || !*valstr) - return -EINVAL; - - if (!*valstr) - *val = false; - else if (*valstr == '0') - *val = false; - else - *val = true; - - return 0; + return strtobool(valstr, val); } EXPORT_SYMBOL(getenv_bool); diff --git a/common/globalvar.c b/common/globalvar.c index 9a793ac..05c3e79 100644 --- a/common/globalvar.c +++ b/common/globalvar.c @@ -33,6 +33,16 @@ return 0; } +void globalvar_remove(const char *name) +{ + struct param_d *param = get_param_by_name(&global_device, name); + + if (!param) + return; + + dev_remove_param(param); +} + static int nv_save(const char *name, const char *val) { int fd, ret; diff --git a/include/boot.h b/include/boot.h index 0198cc8..8fcbb7f 100644 --- a/include/boot.h +++ b/include/boot.h @@ -21,6 +21,11 @@ enum bootm_verify verify; bool force; bool dryrun; + /* + * appendroot - if true, try to add a suitable root= Kernel option to + * mount the rootfs from the same device as the Kernel comes from. + */ + bool appendroot; unsigned long initrd_address; unsigned long os_address; unsigned long os_entry; diff --git a/include/globalvar.h b/include/globalvar.h index e77209b..d0c79c0 100644 --- a/include/globalvar.h +++ b/include/globalvar.h @@ -14,6 +14,7 @@ int (*set)(struct device_d *dev, struct param_d *p, const char *val), const char *(*get)(struct device_d *, struct param_d *p), unsigned long flags); +void globalvar_remove(const char *name); char *globalvar_get_match(const char *match, const char *separator); void globalvar_set_match(const char *match, const char *val); diff --git a/include/string.h b/include/string.h index a833da1..0c557d6 100644 --- a/include/string.h +++ b/include/string.h @@ -4,5 +4,6 @@ #include void *memdup(const void *, size_t); +int strtobool(const char *str, int *val); #endif /* __STRING_H */ diff --git a/lib/parameter.c b/lib/parameter.c index fd05b49..cf34817 100644 --- a/lib/parameter.c +++ b/lib/parameter.c @@ -27,6 +27,7 @@ #include #include #include +#include #include struct param_d *get_param_by_name(struct device_d *dev, const char *name) @@ -314,10 +315,13 @@ if (!val) return -EINVAL; - *pi->value = simple_strtol(val, NULL, 0); - - if (pi->flags & PARAM_INT_FLAG_BOOL) - *pi->value = !!*pi->value; + if (pi->flags & PARAM_INT_FLAG_BOOL) { + ret = strtobool(val, pi->value); + if (ret) + return ret; + } else { + *pi->value = simple_strtol(val, NULL, 0); + } if (!pi->set) return 0; diff --git a/lib/string.c b/lib/string.c index 6a39eb5..a3e9fd8 100644 --- a/lib/string.c +++ b/lib/string.c @@ -739,3 +739,46 @@ return buf; } EXPORT_SYMBOL(memdup); + +/** + * strtobool - convert a string to a boolean value + * @str - The string + * @val - The boolean value returned. + * + * This function treats + * - any positive (nonzero) number as true + * - "0" as false + * - "true" (case insensitive) as true + * - "false" (case insensitive) as false + * + * Every other value results in an error and the @val is not + * modified. The caller is expected to initialize @val with the + * correct default before calling strtobool. + * + * Returns 0 for success or negative error code if the variable does + * not exist or contains something this function does not recognize + * as true or false. + */ +int strtobool(const char *str, int *val) +{ + if (!str || !*str) + return -EINVAL; + + if (simple_strtoul(str, NULL, 0) > 0) { + *val = true; + return 0; + } + + if (!strcmp(str, "0") || !strcasecmp(str, "false")) { + *val = false; + return 0; + } + + if (!strcasecmp(str, "true")) { + *val = true; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL(strtobool);