Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10ba4553b2 | ||
|
|
86701ac1e1 | ||
|
|
8ab3523505 | ||
|
|
e164e96b47 | ||
|
|
f05d8097ad | ||
|
|
15bb4b3074 | ||
|
|
e4c50815ab | ||
|
|
b6db0a6f48 | ||
|
|
cbf290a635 | ||
|
|
8d421a3214 | ||
|
|
f15b67db40 | ||
|
|
9c4a64b260 | ||
|
|
ed6cf4ee9a | ||
|
|
7d169c5625 | ||
|
|
305eefbcf0 |
275
boot/bootflow.c
275
boot/bootflow.c
@@ -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,61 @@ 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:
|
||||
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 +171,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 +215,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 +245,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 +355,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 +436,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 +447,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 +536,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 +578,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 +645,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 +676,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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ static int bootmeth_efi_mgr_bind(struct udevice *dev)
|
||||
|
||||
plat->desc = "EFI bootmgr flow";
|
||||
plat->flags = BOOTMETHF_GLOBAL;
|
||||
plat->glob_prio = BOOTDEVP_6_NET_BASE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -719,6 +720,23 @@ 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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
checks the result of each iteration generated, to determine whether it can
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user