diff options
-rw-r--r-- | boot/bootmeth-uclass.c | 25 | ||||
-rw-r--r-- | boot/bootmeth_extlinux.c | 73 | ||||
-rw-r--r-- | boot/bootmeth_pxe.c | 2 | ||||
-rw-r--r-- | boot/pxe_utils.c | 29 | ||||
-rw-r--r-- | cmd/bootmeth.c | 25 | ||||
-rw-r--r-- | cmd/pxe.c | 4 | ||||
-rw-r--r-- | cmd/sysboot.c | 2 | ||||
-rw-r--r-- | doc/README.pxe | 5 | ||||
-rw-r--r-- | doc/develop/bootstd/overview.rst | 10 | ||||
-rw-r--r-- | doc/usage/cmd/bootmeth.rst | 38 | ||||
-rw-r--r-- | include/bootmeth.h | 31 | ||||
-rw-r--r-- | include/pxe_utils.h | 12 | ||||
-rw-r--r-- | test/boot/bootmeth.c | 47 |
13 files changed, 291 insertions, 12 deletions
diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index c0abadef97c..5b5fea39b3b 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -251,6 +251,31 @@ int bootmeth_set_order(const char *order_str) return 0; } +int bootmeth_set_property(const char *name, const char *property, const char *value) +{ + int ret; + int len; + struct udevice *dev; + const struct bootmeth_ops *ops; + + len = strlen(name); + + ret = uclass_find_device_by_namelen(UCLASS_BOOTMETH, name, len, + &dev); + if (ret) { + printf("Unknown bootmeth '%s'\n", name); + return ret; + } + + ops = bootmeth_get_ops(dev); + if (!ops->set_property) { + printf("set_property not found\n"); + return -ENODEV; + } + + return ops->set_property(dev, property, value); +} + int bootmeth_setup_fs(struct bootflow *bflow, struct blk_desc *desc) { int ret; diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c index fbb05ef928e..be8fbf4df63 100644 --- a/boot/bootmeth_extlinux.c +++ b/boot/bootmeth_extlinux.c @@ -21,6 +21,39 @@ #include <mmc.h> #include <pxe_utils.h> +struct extlinux_plat { + bool use_fallback; +}; + +enum extlinux_option_type { + EO_FALLBACK, + EO_INVALID +}; + +struct extlinux_option { + char *name; + enum extlinux_option_type option; +}; + +static const struct extlinux_option options[] = { + {"fallback", EO_FALLBACK}, + {NULL, EO_INVALID} +}; + +static enum extlinux_option_type get_option(const char *option) +{ + int i = 0; + + while (options[i].name) { + if (!strcmp(options[i].name, option)) + return options[i].option; + + i++; + } + + return EO_INVALID; +}; + static int extlinux_get_state_desc(struct udevice *dev, char *buf, int maxsize) { if (IS_ENABLED(CONFIG_SANDBOX)) { @@ -142,14 +175,18 @@ static int extlinux_boot(struct udevice *dev, struct bootflow *bflow) struct cmd_tbl cmdtp = {}; /* dummy */ struct pxe_context ctx; struct extlinux_info info; + struct extlinux_plat *plat; ulong addr; int ret; addr = map_to_sysmem(bflow->buf); info.dev = dev; info.bflow = bflow; + + plat = dev_get_plat(dev); + ret = pxe_setup_ctx(&ctx, &cmdtp, extlinux_getfile, &info, true, - bflow->fname, false); + bflow->fname, false, plat->use_fallback); if (ret) return log_msg_ret("ctx", -EINVAL); @@ -160,6 +197,38 @@ static int extlinux_boot(struct udevice *dev, struct bootflow *bflow) return 0; } +static int extlinux_set_property(struct udevice *dev, const char *property, const char *value) +{ + struct extlinux_plat *plat; + static enum extlinux_option_type option; + + plat = dev_get_plat(dev); + + option = get_option(property); + if (option == EO_INVALID) { + printf("Invalid option\n"); + return -EINVAL; + } + + switch (option) { + case EO_FALLBACK: + if (!strcmp(value, "1")) { + plat->use_fallback = true; + } else if (!strcmp(value, "0")) { + plat->use_fallback = false; + } else { + printf("Unexpected value '%s'\n", value); + return -EINVAL; + } + break; + default: + printf("Unrecognised property '%s'\n", property); + return -EINVAL; + } + + return 0; +} + static int extlinux_bootmeth_bind(struct udevice *dev) { struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); @@ -176,6 +245,7 @@ static struct bootmeth_ops extlinux_bootmeth_ops = { .read_bootflow = extlinux_read_bootflow, .read_file = bootmeth_common_read_file, .boot = extlinux_boot, + .set_property = extlinux_set_property, }; static const struct udevice_id extlinux_bootmeth_ids[] = { @@ -190,4 +260,5 @@ U_BOOT_DRIVER(bootmeth_1extlinux) = { .of_match = extlinux_bootmeth_ids, .ops = &extlinux_bootmeth_ops, .bind = extlinux_bootmeth_bind, + .plat_auto = sizeof(struct extlinux_plat) }; diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index 03d2589c264..05c6bece2c1 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -150,7 +150,7 @@ static int extlinux_pxe_boot(struct udevice *dev, struct bootflow *bflow) info.bflow = bflow; info.cmdtp = &cmdtp; ret = pxe_setup_ctx(ctx, &cmdtp, extlinux_pxe_getfile, &info, false, - bflow->subdir, false); + bflow->subdir, false, false); if (ret) return log_msg_ret("ctx", -EINVAL); diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index 4e27842b088..d6a4b2cb859 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -781,6 +781,7 @@ enum token_type { T_IPAPPEND, T_BACKGROUND, T_KASLRSEED, + T_FALLBACK, T_INVALID }; @@ -814,6 +815,7 @@ static const struct token keywords[] = { {"ipappend", T_IPAPPEND,}, {"background", T_BACKGROUND,}, {"kaslrseed", T_KASLRSEED,}, + {"fallback", T_FALLBACK,}, {NULL, T_INVALID} }; @@ -1356,6 +1358,18 @@ static int parse_pxefile_top(struct pxe_context *ctx, char *p, unsigned long bas break; + case T_FALLBACK: + err = parse_sliteral(&p, &label_name); + + if (label_name) { + if (cfg->fallback_label) + free(cfg->fallback_label); + + cfg->fallback_label = label_name; + } + + break; + case T_INCLUDE: err = handle_include(ctx, &p, base + ALIGN(strlen(b), 4), cfg, @@ -1395,6 +1409,7 @@ void destroy_pxe_menu(struct pxe_menu *cfg) free(cfg->title); free(cfg->default_label); + free(cfg->fallback_label); list_for_each_safe(pos, n, &cfg->labels) { label = list_entry(pos, struct pxe_label, list); @@ -1421,6 +1436,16 @@ struct pxe_menu *parse_pxefile(struct pxe_context *ctx, unsigned long menucfg) buf = map_sysmem(menucfg, 0); r = parse_pxefile_top(ctx, buf, menucfg, cfg, 1); + + if (ctx->use_fallback) { + if (cfg->fallback_label) { + printf("Setting use of fallback\n"); + cfg->default_label = cfg->fallback_label; + } else { + printf("Selected fallback option, but not set\n"); + } + } + unmap_sysmem(buf); if (r < 0) { destroy_pxe_menu(cfg); @@ -1571,7 +1596,8 @@ void handle_pxe_menu(struct pxe_context *ctx, struct pxe_menu *cfg) int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp, pxe_getfile_func getfile, void *userdata, - bool allow_abs_path, const char *bootfile, bool use_ipv6) + bool allow_abs_path, const char *bootfile, bool use_ipv6, + bool use_fallback) { const char *last_slash; size_t path_len = 0; @@ -1582,6 +1608,7 @@ int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp, ctx->userdata = userdata; ctx->allow_abs_path = allow_abs_path; ctx->use_ipv6 = use_ipv6; + ctx->use_fallback = use_fallback; /* figure out the boot directory, if there is one */ if (bootfile && strlen(bootfile) >= MAX_TFTP_PATH_LEN) diff --git a/cmd/bootmeth.c b/cmd/bootmeth.c index ebf8b7e2530..2f41fa1bec6 100644 --- a/cmd/bootmeth.c +++ b/cmd/bootmeth.c @@ -103,10 +103,31 @@ static int do_bootmeth_order(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_bootmeth_set(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + if (argc < 4) { + printf("Required parameters not provided\n"); + return CMD_RET_FAILURE; + } + + ret = bootmeth_set_property(argv[1], argv[2], argv[3]); + if (ret) { + printf("Failed (err=%d)\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + U_BOOT_LONGHELP(bootmeth, "list [-a] - list available bootmeths (-a all)\n" - "bootmeth order [<bd> ...] - select bootmeth order / subset to use"); + "bootmeth order [<bd> ...] - select bootmeth order / subset to use\n" + "bootmeth set <bootmeth> <property> <value> - set optional property"); U_BOOT_CMD_WITH_SUBCMDS(bootmeth, "Boot methods", bootmeth_help_text, U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootmeth_list), - U_BOOT_SUBCMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_bootmeth_order)); + U_BOOT_SUBCMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_bootmeth_order), + U_BOOT_SUBCMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bootmeth_set)); diff --git a/cmd/pxe.c b/cmd/pxe.c index ae02c28c075..982e2b1e7ea 100644 --- a/cmd/pxe.c +++ b/cmd/pxe.c @@ -138,7 +138,7 @@ int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6) int i; if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false, - env_get("bootfile"), use_ipv6)) + env_get("bootfile"), use_ipv6, false)) return -ENOMEM; if (IS_ENABLED(CONFIG_BOOTP_PXE_DHCP_OPTION) && @@ -288,7 +288,7 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false, - env_get("bootfile"), use_ipv6)) { + env_get("bootfile"), use_ipv6, false)) { printf("Out of memory\n"); return CMD_RET_FAILURE; } diff --git a/cmd/sysboot.c b/cmd/sysboot.c index 0ea08fd7b53..8a060780cab 100644 --- a/cmd/sysboot.c +++ b/cmd/sysboot.c @@ -105,7 +105,7 @@ static int do_sysboot(struct cmd_tbl *cmdtp, int flag, int argc, } if (pxe_setup_ctx(&ctx, cmdtp, sysboot_read_file, &info, true, - filename, false)) { + filename, false, false)) { printf("Out of memory\n"); return CMD_RET_FAILURE; } diff --git a/doc/README.pxe b/doc/README.pxe index 172201093d0..af2e64a5776 100644 --- a/doc/README.pxe +++ b/doc/README.pxe @@ -120,6 +120,11 @@ Unrecognized commands are ignored. default <label> - the label named here is treated as the default and is the first label 'pxe boot' attempts to boot. +fallback <label> - the label named here is treated as a fallback option that + may be attempted should it be detected that booting of + the default has failed to complete, for example via + U-Boot's boot count limit functionality. + menu title <string> - sets a title for the menu of labels being displayed. menu include <path> - use tftp to retrieve the pxe file at <path>, which diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index c6f003851b2..a2913cd47be 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -103,6 +103,12 @@ provide a `read_bootflow()` method which checks whatever bootdevs it likes, then returns the bootflow, if found. Some of these bootmeths may be very slow, if they scan a lot of devices. +The extlinux bootmeth also allows for bootmeth specific configuration to be +set. A bootmeth that wishes to support this provides the `set_property()` +method. This allows string properties and values to be passed to the bootmeth. +It is up to the bootmeth to determine what action to take when this method is +called. + Boot process ------------ @@ -459,8 +465,8 @@ Three commands are available: See :doc:`/usage/cmd/bootflow` `bootmeth` - Allow listing of available bootmethds and setting the order in which they - are tried. See :doc:`/usage/cmd/bootmeth` + Allow listing of available bootmethds, setting the order in which they are + tried and bootmeth specific configuration. See :doc:`/usage/cmd/bootmeth` .. _BootflowStates: diff --git a/doc/usage/cmd/bootmeth.rst b/doc/usage/cmd/bootmeth.rst index c3d2ec1574b..4f899d92b2e 100644 --- a/doc/usage/cmd/bootmeth.rst +++ b/doc/usage/cmd/bootmeth.rst @@ -12,7 +12,8 @@ Synopsis :: bootmeth list [-a] - list selected bootmeths (-a for all) - bootmeth order "[<bm> ...]" - select the order of bootmeths\n" + bootmeth order "[<bm> ...]" - select the order of bootmeths + bootmeth set <bootmeth> <property> <value> - set optional property Description @@ -112,3 +113,38 @@ which are not:: - 4 efi_mgr EFI bootmgr flow ----- --- ------------------ ------------------ (5 bootmeths) + + +bootmeth set +~~~~~~~~~~~~ + +Allows setting of bootmeth specific configuration. This allows finer grain +control over the boot process in specific instances. + + +Supported Configuration Options +------------------------------- + +The following configuration options are currently supported: + +======== =================== ====== =============================== +Property Supported Bootmeths Values Description +======== =================== ====== =============================== +fallback extlinux 0 | 1 Enable or disable fallback path +======== =================== ====== =============================== + + +Bootmeth set Example +-------------------- + +With the bootcount functionality enabled, when the bootlimit is reached, the +`altbootcmd` environment variable lists the command used for booting rather +than `bootcmd`. We can set the fallback configuration to cause the fallback +boot option to be preferred, to revert to a previous known working boot option +after a failed update for example. So if `bootcmd` is set to:: + + bootflow scan -lb + +We would set "altbootcmd" to:: + + bootmeth set extlinux fallback 1; bootflow scan -lb diff --git a/include/bootmeth.h b/include/bootmeth.h index 4d8ca48efd4..a08ebf005ad 100644 --- a/include/bootmeth.h +++ b/include/bootmeth.h @@ -146,6 +146,22 @@ struct bootmeth_ops { * something changes, other -ve on other error */ int (*boot)(struct udevice *dev, struct bootflow *bflow); + + /** + * set_property() - set the bootmeth property + * + * This allows the setting of boot method specific properties to enable + * automated finer grain control of the boot process + * + * @name: String containing the name of the relevant boot method + * @property: String containing the name of the property to set + * @value: String containing the value to be set for the specified + * property + * Return: 0 if OK, -ENODEV if an unknown bootmeth or property is + * provided, -ENOENT if there are no bootmeth devices + */ + int (*set_property)(struct udevice *dev, const char *property, + const char *value); }; #define bootmeth_get_ops(dev) ((struct bootmeth_ops *)(dev)->driver->ops) @@ -291,6 +307,21 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global); int bootmeth_set_order(const char *order_str); /** + * bootmeth_set_property() - Set the bootmeth property + * + * This allows the setting of boot method specific properties to enable + * automated finer grain control of the boot process + * + * @name: String containing the name of the relevant boot method + * @property: String containing the name of the property to set + * @value: String containing the value to be set for the specified property + * Return: 0 if OK, -ENODEV if an unknown bootmeth or property is provided, + * -ENOENT if there are no bootmeth devices + */ +int bootmeth_set_property(const char *name, const char *property, + const char *value); + +/** * bootmeth_setup_fs() - Set up read to read a file * * We must redo the setup before each filesystem operation. This function diff --git a/include/pxe_utils.h b/include/pxe_utils.h index 9f195930487..68ac40b64ad 100644 --- a/include/pxe_utils.h +++ b/include/pxe_utils.h @@ -62,6 +62,7 @@ struct pxe_label { * * title - the name of the menu as given by a 'menu title' line. * default_label - the name of the default label, if any. + * fallback_label - the name of the fallback label, if any. * bmp - the bmp file name which is displayed in background * timeout - time in tenths of a second to wait for a user key-press before * booting the default label. @@ -73,6 +74,7 @@ struct pxe_label { struct pxe_menu { char *title; char *default_label; + char *fallback_label; char *bmp; int timeout; int prompt; @@ -94,6 +96,8 @@ typedef int (*pxe_getfile_func)(struct pxe_context *ctx, const char *file_path, * allocated * @pxe_file_size: Size of the PXE file * @use_ipv6: TRUE : use IPv6 addressing, FALSE : use IPv4 addressing + * @use_fallback: TRUE : use "fallback" option as default, FALSE : use + * "default" option as default */ struct pxe_context { struct cmd_tbl *cmdtp; @@ -114,6 +118,7 @@ struct pxe_context { char *bootdir; ulong pxe_file_size; bool use_ipv6; + bool use_fallback; }; /** @@ -213,12 +218,17 @@ int format_mac_pxe(char *outbuf, size_t outbuf_len); * none * @use_ipv6: TRUE : use IPv6 addressing * FALSE : use IPv4 addressing + * @use_fallback: TRUE : Use "fallback" option instead of "default" should no + * other choice be selected + * FALSE : Use "default" option should no other choice be + * selected * Return: 0 if OK, -ENOMEM if out of memory, -E2BIG if bootfile is larger than * MAX_TFTP_PATH_LEN bytes */ int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp, pxe_getfile_func getfile, void *userdata, - bool allow_abs_path, const char *bootfile, bool use_ipv6); + bool allow_abs_path, const char *bootfile, bool use_ipv6, + bool use_fallback); /** * pxe_destroy_ctx() - Destroy a PXE context diff --git a/test/boot/bootmeth.c b/test/boot/bootmeth.c index 518d99c4a27..18ae6d7fe13 100644 --- a/test/boot/bootmeth.c +++ b/test/boot/bootmeth.c @@ -127,6 +127,53 @@ static int bootmeth_cmd_order_glob(struct unit_test_state *uts) } BOOTSTD_TEST(bootmeth_cmd_order_glob, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); +/* Check 'bootmeth set' command */ +static int bootmeth_cmd_set(struct unit_test_state *uts) +{ + /* Check we can enable extlinux fallback */ + console_record_reset_enable(); + ut_assertok(run_command("bootmeth set extlinux fallback 1", 0)); + ut_assert_console_end(); + + /* Check we can disable extlinux fallback */ + console_record_reset_enable(); + ut_assertok(run_command("bootmeth set extlinux fallback 0", 0)); + ut_assert_console_end(); + + /* Check extlinux fallback unexpected value */ + console_record_reset_enable(); + ut_asserteq(1, run_command("bootmeth set extlinux fallback fred", 0)); + ut_assert_nextline("Unexpected value 'fred'"); + ut_assert_nextline("Failed (err=-22)"); + ut_assert_console_end(); + + /* Check that we need to provide right number of parameters */ + ut_asserteq(1, run_command("bootmeth set extlinux fallback", 0)); + ut_assert_nextline("Required parameters not provided"); + ut_assert_console_end(); + + /* Check that we need to provide a valid bootmethod */ + ut_asserteq(1, run_command("bootmeth set fred fallback 0", 0)); + ut_assert_nextline("Unknown bootmeth 'fred'"); + ut_assert_nextline("Failed (err=-19)"); + ut_assert_console_end(); + + /* Check that we need to provide a valid property */ + ut_asserteq(1, run_command("bootmeth set extlinux fred 0", 0)); + ut_assert_nextline("Invalid option"); + ut_assert_nextline("Failed (err=-22)"); + ut_assert_console_end(); + + /* Check that we need to provide a bootmeth that supports properties */ + ut_asserteq(1, run_command("bootmeth set efi fallback 0", 0)); + ut_assert_nextline("set_property not found"); + ut_assert_nextline("Failed (err=-19)"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootmeth_cmd_set, UTF_DM | UTF_SCAN_FDT); + /* Check 'bootmeths' env var */ static int bootmeth_env(struct unit_test_state *uts) { |