Merge patch series to add a "fallback" keyword to extlinux.conf parsing
This series from Martyn Welch <martyn.welch@collabora.com> adds the ability to have a "fallback" option in extlinux.conf parsing, which can be in turn used in A/B style update mechanisms. Link: https://lore.kernel.org/u-boot/20241009131548.929439-1-martyn.welch@collabora.com/
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+28
-1
@@ -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)
|
||||
|
||||
+23
-2
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -290,6 +306,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
|
||||
*
|
||||
|
||||
+11
-1
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user