Merge branch 'glob2' into 'master'

boot: Support priority for global bootmeths

See merge request u-boot/u-boot!190
This commit is contained in:
Simon Glass
2025-10-01 16:11:55 +00:00
8 changed files with 419 additions and 96 deletions

View File

@@ -18,6 +18,10 @@
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
/* ensure BOOTMETH_MAX_COUNT fits in method_flags field */
static_assert(BOOTMETH_MAX_COUNT <=
(sizeof(((struct bootflow_iter *)NULL)->method_flags) * 8));
/* error codes used to signal running out of things */
enum {
BF_NO_MORE_PARTS = -ESHUTDOWN,
@@ -56,6 +60,63 @@ const char *bootflow_state_get_name(enum bootflow_state_t state)
return bootflow_state[state];
}
/**
* report_bootflow_err() - Report where a bootflow failed
*
* When a bootflow does not make it to the 'loaded' state, something went wrong.
* Print a helpful message if there is an error
*
* @bflow: Bootflow to process
* @err: Error code (0 if none)
*/
static void report_bootflow_err(struct bootflow *bflow, int err)
{
if (!err)
return;
/* Indent out to 'Method' */
printf(" ** ");
switch (bflow->state) {
case BOOTFLOWST_BASE:
printf("No media/partition found");
break;
case BOOTFLOWST_MEDIA:
printf("No partition found");
break;
case BOOTFLOWST_PART:
printf("No filesystem found");
break;
case BOOTFLOWST_FS:
printf("File not found");
break;
case BOOTFLOWST_FILE:
printf("File cannot be loaded");
break;
case BOOTFLOWST_READY:
printf("Ready");
break;
case BOOTFLOWST_COUNT:
log_err("Unexpected boot value of bootflow error %d",
bflow->state);
break;
}
printf(", err=%dE\n", err);
}
void bootflow_show(int index, struct bootflow *bflow, bool errors)
{
const char *name = bootflow_guess_label(bflow);
printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index,
bflow->method ? bflow->method->name : "(none)",
bootflow_state_get_name(bflow->state), name, bflow->part,
bflow->name, bflow->fname ?: "");
if (errors)
report_bootflow_err(bflow, bflow->err);
}
int bootflow_first_glob(struct bootflow **bflowp)
{
struct bootstd_priv *std;
@@ -112,11 +173,17 @@ int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
iter->method_order[iter->cur_method] != bmeth)
return -EINVAL;
log_debug("Dropping bootmeth '%s'\n", bmeth->name);
memmove(&iter->method_order[iter->cur_method],
&iter->method_order[iter->cur_method + 1],
(iter->num_methods - iter->cur_method - 1) * sizeof(void *));
iter->num_methods--;
if (iter->first_glob_method > 0) {
iter->first_glob_method--;
log_debug("first_glob_method %d\n", iter->first_glob_method);
}
return 0;
}
@@ -150,7 +217,7 @@ static void bootflow_iter_set_dev(struct bootflow_iter *iter,
if (dev)
printf("Scanning bootdev '%s':\n", dev->name);
else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) &&
ucp->flags & BOOTMETHF_GLOBAL)
ucp->flags & BOOTMETHF_GLOBAL)
printf("Scanning global bootmeth '%s':\n",
iter->method->name);
else
@@ -180,6 +247,101 @@ static void scan_next_in_uclass(struct udevice **devp)
*devp = dev;
}
/**
* bootmeth_glob_allowed() - Check if a global bootmeth is usable at this point
*
* @iter: Bootflow iterator being used
* Return: true if the global bootmeth has a suitable priority and has not
* already been used
*/
static bool bootmeth_glob_allowed(struct bootflow_iter *iter, int meth_seq)
{
struct udevice *meth = iter->method_order[meth_seq];
bool done = iter->methods_done & BIT(meth_seq);
struct bootmeth_uc_plat *ucp;
ucp = dev_get_uclass_plat(meth);
log_debug("considering glob '%s': done %d glob_prio %d\n", meth->name,
done, ucp->glob_prio);
/*
* if this one has already been used, or its priority is too low, try
* the next
*/
if (done || ucp->glob_prio > iter->cur_prio)
return false;
return true;
}
/**
* next_glob_bootmeth() - Find the next global bootmeth to use
*
* Scans the global bootmeths to find the first unused one whose priority has
* been reached. If found, iter->cur_method and iter->method are set up and
* doing_global is set to true
*
* @iter: Bootflow iterator being used
* Return 0 if found, -ENOENT if no more global bootmeths are available
*/
static int next_glob_bootmeth(struct bootflow_iter *iter)
{
log_debug("rescan global bootmeths have_global %d\n",
iter->have_global);
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->have_global) {
int i;
/* rescan the global bootmeths */
log_debug("first_glob_method %d num_methods %d methods_done %x\n",
iter->first_glob_method, iter->num_methods,
iter->methods_done);
for (i = iter->first_glob_method; i < iter->num_methods; i++) {
if (bootmeth_glob_allowed(iter, i)) {
iter->cur_method = i;
iter->method = iter->method_order[i];
iter->doing_global = true;
iter->dev = NULL;
return 0;
}
}
}
return -ENOENT;
}
/**
* prepare_bootdev() - Get ready to use a bootdev
*
* @iter: Bootflow iterator being used
* @dev: UCLASS_BOOTDEV device to use
* @method_flags: Method flag for the bootdev
* @check_global: true to check global bootmeths before processing @dev
* Return 0 if OK, -ve if the bootdev failed to probe
*/
static int prepare_bootdev(struct bootflow_iter *iter, struct udevice *dev,
int method_flags, bool check_global)
{
int ret;
if (check_global && !next_glob_bootmeth(iter)) {
iter->pending_bootdev = dev;
iter->pending_method_flags = method_flags;
return 0;
}
/*
* Probe the bootdev. This does not probe any attached block device,
* since they are siblings
*/
ret = device_probe(dev);
log_debug("probe %s %d\n", dev->name, ret);
if (ret)
return log_msg_ret("probe", ret);
bootflow_iter_set_dev(iter, dev, method_flags);
return 0;
}
/**
* iter_incr() - Move to the next item (method, part, bootdev)
*
@@ -195,23 +357,80 @@ static int iter_incr(struct bootflow_iter *iter)
log_debug("entry: err=%d\n", iter->err);
global = iter->doing_global;
if (iter->err == BF_NO_MORE_DEVICES)
if (iter->err == BF_NO_MORE_DEVICES) {
log_debug("-> err: no more devices1\n");
return BF_NO_MORE_DEVICES;
}
/* Get the next boothmethod */
if (++iter->cur_method < iter->num_methods) {
for (iter->cur_method++; iter->cur_method < iter->num_methods;
iter->cur_method++) {
/* loop until we find a global bootmeth we haven't used */
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) {
if (!bootmeth_glob_allowed(iter, iter->cur_method))
continue;
iter->method = iter->method_order[iter->cur_method];
log_debug("-> next global method '%s'\n",
iter->method->name);
return 0;
}
/* at this point we are only considering non-global bootmeths */
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->have_global &&
iter->cur_method >= iter->first_glob_method)
break;
iter->method = iter->method_order[iter->cur_method];
log_debug("-> next method '%s'\n", iter->method->name);
return 0;
}
log_debug("! no more methods: cur_method %d num_methods %d\n",
iter->cur_method, iter->num_methods);
/*
* If we have finished scanning the global bootmeths, start the
* normal bootdev scan
*/
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) {
iter->num_methods = iter->first_glob_method;
iter->doing_global = false;
/*
* we've come to the end, so see if we should use a pending
* bootdev from when we decided to rescan the global bootmeths
*/
if (iter->pending_bootdev) {
int meth_flags = iter->pending_method_flags;
dev = iter->pending_bootdev;
iter->pending_bootdev = NULL;
iter->pending_method_flags = 0;
ret = prepare_bootdev(iter, dev, meth_flags, false);
if (ret)
return log_msg_ret("ipb", ret);
iter->cur_method = 0;
iter->method = iter->method_order[iter->cur_method];
log_debug("-> using pending bootdev '%s' method '%s'\n",
dev->name, iter->method->name);
return 0;
}
/* if this was the final global bootmeth check, we are done */
if (iter->cur_prio == BOOTDEVP_COUNT) {
log_debug("-> done global bootmeths\n");
/* print the same message as bootflow_iter_set_dev() */
if ((iter->flags & (BOOTFLOWIF_SHOW |
BOOTFLOWIF_SINGLE_DEV)) ==
BOOTFLOWIF_SHOW)
printf("No more bootdevs\n");
return BF_NO_MORE_DEVICES;
}
/*
* Don't move to the next dev as we haven't tried this
* one yet!
@@ -219,8 +438,10 @@ static int iter_incr(struct bootflow_iter *iter)
inc_dev = false;
}
if (iter->flags & BOOTFLOWIF_SINGLE_PARTITION)
if (iter->flags & BOOTFLOWIF_SINGLE_PARTITION) {
log_debug("-> single partition: no more devices\n");
return BF_NO_MORE_DEVICES;
}
/* No more bootmeths; start at the first one, and... */
iter->cur_method = 0;
@@ -228,11 +449,15 @@ static int iter_incr(struct bootflow_iter *iter)
if (iter->err != BF_NO_MORE_PARTS) {
/* ...select next partition */
if (++iter->part <= iter->max_part)
if (++iter->part <= iter->max_part) {
log_debug("-> next partition %d max %d\n", iter->part,
iter->max_part);
return 0;
}
}
/* No more partitions; start at the first one and... */
log_debug("! no more partitions\n");
iter->part = 0;
/*
@@ -313,23 +538,31 @@ static int iter_incr(struct bootflow_iter *iter)
}
log_debug("ret=%d, dev=%p %s\n", ret, dev,
dev ? dev->name : "none");
if (ret) {
if (ret)
bootflow_iter_set_dev(iter, NULL, 0);
} else {
/*
* Probe the bootdev. This does not probe any attached
* block device, since they are siblings
*/
ret = device_probe(dev);
log_debug("probe %s %d\n", dev->name, ret);
if (!log_msg_ret("probe", ret))
bootflow_iter_set_dev(iter, dev, method_flags);
else
ret = prepare_bootdev(iter, dev, method_flags, true);
}
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && ret) {
log_debug("no more bootdevs, trying global\n");
/* allow global bootmeths with any priority */
iter->cur_prio = BOOTDEVP_COUNT;
if (!next_glob_bootmeth(iter)) {
log_debug("-> next method '%s'\n", iter->method->name);
return 0;
}
}
/* if there are no more bootdevs, give up */
if (ret)
if (ret) {
log_debug("-> no more bootdevs\n");
return log_msg_ret("incr", BF_NO_MORE_DEVICES);
}
log_debug("-> bootdev '%s' method '%s'\n", dev->name,
iter->method->name);
return 0;
}
@@ -347,6 +580,7 @@ static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow)
struct udevice *dev;
int ret;
/* handle global bootmeths if needed */
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) {
bootflow_iter_set_dev(iter, NULL, 0);
ret = bootmeth_get_bootflow(iter->method, bflow);
@@ -413,6 +647,10 @@ int bootflow_scan_first(struct udevice *dev, const char *label,
bootflow_iter_set_dev(iter, dev, method_flags);
}
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) {
iter->methods_done |= BIT(iter->cur_method);
log_debug("methods_done now %x\n", iter->cur_method);
}
ret = bootflow_check(iter, bflow);
if (ret) {
log_debug("check - ret=%d\n", ret);
@@ -440,6 +678,11 @@ int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow)
return log_msg_ret("done", ret);
if (!ret) {
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) {
iter->methods_done |= BIT(iter->cur_method);
log_debug("methods_done now %x\n",
iter->cur_method);
}
ret = bootflow_check(iter, bflow);
log_debug("check - ret=%d\n", ret);
if (!ret)

View File

@@ -147,8 +147,7 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global)
bool is_global;
ucp = dev_get_uclass_plat(dev);
is_global = ucp->flags &
BOOTMETHF_GLOBAL;
is_global = ucp->flags & BOOTMETHF_GLOBAL;
if (is_global) {
iter->first_glob_method = i;
break;
@@ -193,11 +192,23 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global)
if (!count)
return log_msg_ret("count2", -ENOENT);
/* start with the global bootmeths */
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && include_global &&
iter->first_glob_method != -1 && iter->first_glob_method != count) {
iter->cur_method = iter->first_glob_method;
iter->doing_global = true;
iter->have_global = true;
}
/*
* check we don't exceed the maximum bits in methods_done when tracking
* which global bootmeths have run
*/
if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && count > BOOTMETH_MAX_COUNT) {
free(order);
return log_msg_ret("tmb", -ENOSPC);
}
iter->method_order = order;
iter->num_methods = count;

View File

@@ -99,6 +99,15 @@ static int bootmeth_efi_mgr_bind(struct udevice *dev)
plat->desc = "EFI bootmgr flow";
plat->flags = BOOTMETHF_GLOBAL;
/*
* bootmgr scans all available devices which can take a while,
* especially for network devices. So choose the priority so that it
* comes just before the 'very slow' devices. This allows systems which
* don't rely on bootmgr to boot quickly, while allowing bootmgr to run
* on systems which need it.
*/
plat->glob_prio = BOOTDEVP_6_NET_BASE;
return 0;
}

View File

@@ -18,68 +18,6 @@
#include <log.h>
#include <mapmem.h>
/**
* report_bootflow_err() - Report where a bootflow failed
*
* When a bootflow does not make it to the 'loaded' state, something went wrong.
* Print a helpful message if there is an error
*
* @bflow: Bootflow to process
* @err: Error code (0 if none)
*/
static void report_bootflow_err(struct bootflow *bflow, int err)
{
if (!err)
return;
/* Indent out to 'Method' */
printf(" ** ");
switch (bflow->state) {
case BOOTFLOWST_BASE:
printf("No media/partition found");
break;
case BOOTFLOWST_MEDIA:
printf("No partition found");
break;
case BOOTFLOWST_PART:
printf("No filesystem found");
break;
case BOOTFLOWST_FS:
printf("File not found");
break;
case BOOTFLOWST_FILE:
printf("File cannot be loaded");
break;
case BOOTFLOWST_READY:
printf("Ready");
break;
case BOOTFLOWST_COUNT:
break;
}
printf(", err=%dE\n", err);
}
/**
* show_bootflow() - Show the status of a bootflow
*
* @seq: Bootflow index
* @bflow: Bootflow to show
* @errors: True to show the error received, if any
*/
static void show_bootflow(int index, struct bootflow *bflow, bool errors)
{
const char *name = bootflow_guess_label(bflow);
printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index,
bflow->method ? bflow->method->name : "(none)",
bootflow_state_get_name(bflow->state), name, bflow->part,
bflow->name, bflow->fname ?: "");
if (errors)
report_bootflow_err(bflow, bflow->err);
}
static void show_header(void)
{
printf("Seq Method State Uclass Part Name Filename\n");
@@ -240,7 +178,7 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
return CMD_RET_FAILURE;
}
if (list)
show_bootflow(i, &bflow, errors);
bootflow_show(i, &bflow, errors);
if (!menu && boot && !bflow.err)
bootflow_run_boot(&iter, &bflow);
}
@@ -298,7 +236,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
!ret;
ret = bootdev_next_bootflow(&bflow), i++) {
num_valid += bflow->state == BOOTFLOWST_READY;
show_bootflow(i, bflow, errors);
bootflow_show(i, bflow, errors);
}
} else {
printf("Showing all bootflows\n");
@@ -307,7 +245,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
!ret;
ret = bootflow_next_glob(&bflow), i++) {
num_valid += bflow->state == BOOTFLOWST_READY;
show_bootflow(i, bflow, errors);
bootflow_show(i, bflow, errors);
}
}
show_footer(i, num_valid);

View File

@@ -133,7 +133,8 @@ which scans for available bootflows, optionally listing each find it finds (-l)
and trying to boot it (-b).
When global bootmeths are available, these are typically checked before the
above bootdev scanning.
above bootdev scanning, but it is possible provide a priority to make them
run later, by setting the glob_prio field in the driver's bind() method.
Controlling ordering
@@ -614,9 +615,9 @@ simply copied into the iterator. Either way, the `method_order` array it set up,
along with `num_methods`.
Note that global bootmeths are always put at the end of the ordering. If any are
present, `cur_method` is set to the first one, so that global bootmeths are done
first. Once all have been used, these bootmeths are dropped from the iteration.
When there are no global bootmeths, `cur_method` is set to 0.
present, `cur_method` is set to the first one, so that global bootmeths are
processed first, so long as their priority allows it. Bootstd keeps track of
which global bootmeths have been used, to make sure they are only used once.
At this point the iterator is ready to use, with the first bootmeth selected.
Most of the other fields are 0. This means that the current partition
@@ -716,8 +717,35 @@ to the next partition, or bootdev, for example. The special values
`BF_NO_MORE_PARTS` and `BF_NO_MORE_DEVICES` handle this. When `iter_incr` sees
`BF_NO_MORE_PARTS` it knows that it should immediately move to the next bootdev.
When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do
so it should immediately return. The caller of `iter_incr()` is responsible for
updating the `err` field, based on the return value it sees.
so it should immediately run any unused global bootmeths and then return. The
caller of `iter_incr()` is responsible for updating the `err` field, based on
the return value it sees.
Global bootmeths can have a non-zero priority, which indicates where in the
iteration sequence they should run. Each time a new bootdev is produced by a
hunter, all of the global bootmeths are first checked to see if they should run
before this new bootdev. For example, if the bootdev was produced by a hunter
with priority BOOTDEVP_6_NET_BASE, then a quick check is made for global
bootmeths with that priority or less. If there are any, they run before the new
bootdev is processed.
Assuming they are enabled and the iteration sequence runs right to the end, all
global bootmeths will be used. This is handled by a special case at the end of
iter_incr(), where it processes amy so-far-unused global bootmeths.
It is important to note the special nature of global bootmeths, with respect to
priority. If there are two normal bootmeths and a global one, the normal ones
are run for each bootdev, but the global one is independent of bootdevs. The
order might be:
bootdev priority 3: normal-1, normal-3
global-2, prio 4
bootdev priority 5: normal-1, normal-3
Of course if a specific bootmeth ordering is provided, then this overrides the
default ordering. Global bootmeths must be listed at the end, reflecting their
hybrid nature (they are bootmeths but operate on the system as a whole, not on
a particular bootdev).
The above describes the iteration process at a high level. It is basically a
very simple increment function with a checker called `bootflow_check()` that

View File

@@ -12,6 +12,7 @@
#include <image.h>
#include <dm/ofnode_decl.h>
#include <linux/list.h>
#include <linux/build_bug.h>
struct bootstd_priv;
struct expo;
@@ -226,6 +227,10 @@ enum bootflow_meth_flags_t {
BOOTFLOW_METHF_SINGLE_UCLASS = 1 << 3,
};
enum {
BOOTMETH_MAX_COUNT = 32,
};
/**
* struct bootflow_iter - state for iterating through bootflows
*
@@ -263,14 +268,21 @@ enum bootflow_meth_flags_t {
* @cur_label: Current label being processed
* @num_methods: Number of bootmeth devices in @method_order
* @cur_method: Current method number, an index into @method_order
* @first_glob_method: First global method, if any, else -1
* @first_glob_method: Index of first global method within @method_order[], if
* any, else -1
* @cur_prio: Current priority being scanned
* @method_order: List of bootmeth devices to use, in order. The normal methods
* appear first, then the global ones, if any
* @have_global: true if we have global bootmeths in @method_order[]
* @doing_global: true if we are iterating through the global bootmeths (which
* happens before the normal ones)
* generally happens before the normal ones)
* @method_flags: flags controlling which methods should be used for this @dev
* (enum bootflow_meth_flags_t)
* @methods_done: indicates which methods have been processed, one bit for
* each method in @method_order[]
* @pending_bootdev: if non-NULL, bootdev which will be used when the global
* bootmeths are done
* @pending_method_flags: method flags which will be used with @pending_bootdev
*/
struct bootflow_iter {
int flags;
@@ -290,8 +302,12 @@ struct bootflow_iter {
int first_glob_method;
enum bootdev_prio_t cur_prio;
struct udevice **method_order;
bool have_global;
bool doing_global;
int method_flags;
uint methods_done;
struct udevice *pending_bootdev;
int pending_method_flags;
};
/**
@@ -739,4 +755,13 @@ int bootflow_menu_poll(struct expo *exp, int *seqp);
*/
const char *bootflow_guess_label(const struct bootflow *bflow);
/**
* bootflow_show() - Show the status of a bootflow
*
* @seq: Bootflow index
* @bflow: Bootflow to show
* @errors: True to show the error received, if any
*/
void bootflow_show(int index, struct bootflow *bflow, bool errors);
#endif

View File

@@ -30,10 +30,14 @@ enum bootmeth_flags {
*
* @desc: A long description of the bootmeth
* @flags: Flags for this bootmeth (enum bootmeth_flags)
* @glob_prio: Priority for this bootmeth. If unset (0) the bootmeth is started
* before all other bootmeths. Otherwise it is started before the iteration
* reaches the given priority.
*/
struct bootmeth_uc_plat {
const char *desc;
int flags;
enum bootdev_prio_t glob_prio;
};
/** struct bootmeth_ops - Operations for boot methods */

View File

@@ -313,6 +313,10 @@ static int bootflow_iter(struct unit_test_state *uts)
ut_asserteq(0, iter.max_part);
ut_asserteq_str("extlinux", iter.method->name);
ut_asserteq(0, bflow.err);
ut_assert(!iter.doing_global);
ut_assert(!iter.have_global);
ut_asserteq(-1, iter.first_glob_method);
ut_asserteq(BIT(0), iter.methods_done);
/*
* This shows MEDIA even though there is none, since in
@@ -321,6 +325,7 @@ static int bootflow_iter(struct unit_test_state *uts)
* know.
*/
ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
bootflow_free(&bflow);
ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow));
ut_asserteq(3, iter.num_methods);
@@ -330,6 +335,7 @@ static int bootflow_iter(struct unit_test_state *uts)
ut_asserteq_str("efi", iter.method->name);
ut_asserteq(0, bflow.err);
ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
ut_asserteq(BIT(0) | BIT(1), iter.methods_done);
bootflow_free(&bflow);
/* now the VBE boothmeth */
@@ -341,6 +347,7 @@ static int bootflow_iter(struct unit_test_state *uts)
ut_asserteq_str("vbe", iter.method->name);
ut_asserteq(0, bflow.err);
ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
bootflow_free(&bflow);
/* The next device is mmc1.bootdev - at first we use the whole device */
@@ -352,6 +359,7 @@ static int bootflow_iter(struct unit_test_state *uts)
ut_asserteq_str("extlinux", iter.method->name);
ut_asserteq(0, bflow.err);
ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
bootflow_free(&bflow);
ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
@@ -362,6 +370,7 @@ static int bootflow_iter(struct unit_test_state *uts)
ut_asserteq_str("efi", iter.method->name);
ut_asserteq(0, bflow.err);
ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
bootflow_free(&bflow);
/* now the VBE boothmeth */
@@ -373,6 +382,7 @@ static int bootflow_iter(struct unit_test_state *uts)
ut_asserteq_str("vbe", iter.method->name);
ut_asserteq(0, bflow.err);
ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
bootflow_free(&bflow);
/* Then move to partition 1 where we find something */
@@ -415,6 +425,7 @@ static int bootflow_iter(struct unit_test_state *uts)
ut_asserteq_str("extlinux", iter.method->name);
ut_asserteq(0, bflow.err);
ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
bootflow_free(&bflow);
bootflow_iter_uninit(&iter);
@@ -426,6 +437,51 @@ static int bootflow_iter(struct unit_test_state *uts)
BOOTSTD_TEST(bootflow_iter, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
#if defined(CONFIG_SANDBOX) && defined(CONFIG_BOOTMETH_GLOBAL)
/* Check iterating through available bootflows to test global bootmeths */
static int bootflow_iter_glob(struct unit_test_state *uts)
{
struct bootflow_iter iter;
struct bootflow bflow;
bootstd_clear_glob();
/* we should get the global bootmeth initially */
ut_asserteq(-EINVAL,
bootflow_scan_first(NULL, NULL, &iter, BOOTFLOWIF_ALL |
BOOTFLOWIF_SHOW, &bflow));
bootflow_show(0, &bflow, true);
ut_asserteq(4, iter.num_methods);
ut_assert(iter.doing_global);
ut_assert(iter.have_global);
ut_asserteq(3, iter.first_glob_method);
ut_asserteq(3, iter.cur_method);
ut_asserteq(0, iter.part);
ut_asserteq(0, iter.max_part);
ut_asserteq_str("firmware0", iter.method->name);
ut_asserteq(0, bflow.err);
bootflow_free(&bflow);
/* next we should get the first non-global bootmeth */
ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow));
/* at this point the global bootmeths are stranded above num_methods */
ut_asserteq(4, iter.num_methods);
ut_asserteq(3, iter.first_glob_method);
ut_assert(!iter.doing_global);
ut_assert(iter.have_global);
ut_asserteq(0, iter.cur_method);
ut_asserteq(0, iter.part);
ut_asserteq(0, iter.max_part);
ut_asserteq_str("extlinux", iter.method->name);
ut_asserteq(0, bflow.err);
return 0;
}
BOOTSTD_TEST(bootflow_iter_glob, UTF_DM | UTF_SCAN_FDT);
/* Check using the system bootdev */
static int bootflow_system(struct unit_test_state *uts)
{
@@ -439,11 +495,11 @@ static int bootflow_system(struct unit_test_state *uts)
ut_assertok(device_probe(dev));
sandbox_set_fake_efi_mgr_dev(dev, true);
/* We should get a single 'bootmgr' method right at the end */
/* We should get a single 'bootmgr' method at the end */
bootstd_clear_glob();
ut_assertok(run_command("bootflow scan -lH", 0));
ut_assert_skip_to_line(
" 0 efi_mgr ready (none) 0 <NULL> ");
" 1 efi_mgr ready (none) 0 <NULL> ");
ut_assert_skip_to_line("No more bootdevs");
ut_assert_skip_to_line("(2 bootflows, 2 valid)");
ut_assert_console_end();
@@ -476,7 +532,12 @@ static int bootflow_iter_disable(struct unit_test_state *uts)
/* Try to boot the bootmgr flow, which will fail */
console_record_reset_enable();
ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
ut_asserteq(4, iter.num_methods);
/* at this point the global bootmeths are stranded above num_methods */
ut_asserteq(5, iter.num_methods);
ut_assert(!iter.doing_global);
ut_assert(iter.have_global);
ut_asserteq(4, iter.first_glob_method);
ut_asserteq_str("sandbox", iter.method->name);
ut_assertok(inject_response(uts));
ut_asserteq(-ENOTSUPP, bootflow_run_boot(&iter, &bflow));
@@ -485,10 +546,14 @@ static int bootflow_iter_disable(struct unit_test_state *uts)
ut_assert_console_end();
/* Check that the sandbox bootmeth has been removed */
ut_asserteq(3, iter.num_methods);
ut_asserteq(4, iter.num_methods);
for (i = 0; i < iter.num_methods; i++)
ut_assert(strcmp("sandbox", iter.method_order[i]->name));
/* the first global bootmeth is now down one place in the list */
ut_asserteq(3, iter.first_glob_method);
return 0;
}
BOOTSTD_TEST(bootflow_iter_disable, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);