bootstd: Allow scanning a single bootdev label

We want to support scanning a single label, like 'mmc' or 'usb0'. Add
this feature by plumbing the label through to the iterator, setting a
flag to indicate that only siblings of the initial device should be used.

This means that scanning a bootdev by its name is not supported anymore.
That feature doesn't seem very useful in practice, so it is no great loss.

Add a test for bootdev_find_by_any() while we are here.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2023-01-17 10:48:15 -07:00 committed by Tom Rini
parent 47aedc29dc
commit 91943ff703
7 changed files with 166 additions and 76 deletions

View file

@ -649,10 +649,10 @@ int bootdev_next_prio(struct bootflow_iter *iter, struct udevice **devp)
return 0;
}
int bootdev_setup_iter(struct bootflow_iter *iter, struct udevice **devp,
int *method_flagsp)
int bootdev_setup_iter(struct bootflow_iter *iter, const char *label,
struct udevice **devp, int *method_flagsp)
{
struct udevice *bootstd, *dev = *devp;
struct udevice *bootstd, *dev = NULL;
bool show = iter->flags & BOOTFLOWF_SHOW;
int method_flags;
int ret;
@ -671,10 +671,24 @@ int bootdev_setup_iter(struct bootflow_iter *iter, struct udevice **devp,
}
/* Handle scanning a single device */
if (dev) {
iter->flags |= BOOTFLOWF_SINGLE_DEV;
log_debug("Selected boodev: %s\n", dev->name);
method_flags = 0;
if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && label) {
if (iter->flags & BOOTFLOWF_HUNT) {
ret = bootdev_hunt(label, show);
if (ret)
return log_msg_ret("hun", ret);
}
ret = bootdev_find_by_any(label, &dev, &method_flags);
if (ret)
return log_msg_ret("lab", ret);
log_debug("method_flags: %x\n", method_flags);
if (method_flags & BOOTFLOW_METHF_SINGLE_UCLASS)
iter->flags |= BOOTFLOWF_SINGLE_UCLASS;
else if (method_flags & BOOTFLOW_METHF_SINGLE_DEV)
iter->flags |= BOOTFLOWF_SINGLE_DEV;
else
iter->flags |= BOOTFLOWF_SINGLE_MEDIA;
log_debug("Selected label: %s, flags %x\n", label, iter->flags);
} else {
bool ok;

View file

@ -215,7 +215,37 @@ static int iter_incr(struct bootflow_iter *iter)
dev = iter->dev;
log_debug("inc_dev=%d\n", inc_dev);
if (!inc_dev) {
ret = bootdev_setup_iter(iter, &dev, &method_flags);
ret = bootdev_setup_iter(iter, NULL, &dev,
&method_flags);
} else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) &&
(iter->flags & BOOTFLOWF_SINGLE_UCLASS)) {
/* Move to the next bootdev in this uclass */
uclass_find_next_device(&dev);
if (!dev) {
log_debug("finished uclass %s\n",
dev_get_uclass_name(dev));
ret = -ENODEV;
}
} else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) &&
iter->flags & BOOTFLOWF_SINGLE_MEDIA) {
log_debug("next in single\n");
method_flags = 0;
do {
/*
* Move to the next bootdev child of this media
* device. This ensures that we cover all the
* available SCSI IDs and LUNs.
*/
device_find_next_child(&dev);
log_debug("- next %s\n",
dev ? dev->name : "(none)");
} while (dev && device_get_uclass_id(dev) !=
UCLASS_BOOTDEV);
if (!dev) {
log_debug("finished uclass %s\n",
dev_get_uclass_name(dev));
ret = -ENODEV;
}
} else {
log_debug("labels %p\n", iter->labels);
if (iter->labels) {
@ -294,12 +324,13 @@ static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow)
return 0;
}
int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter,
int flags, struct bootflow *bflow)
int bootflow_scan_bootdev(struct udevice *dev, const char *label,
struct bootflow_iter *iter, int flags,
struct bootflow *bflow)
{
int ret;
if (dev)
if (dev || label)
flags |= BOOTFLOWF_SKIP_GLOBAL;
bootflow_iter_init(iter, flags);
@ -318,7 +349,7 @@ int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter,
struct udevice *dev = NULL;
int method_flags;
ret = bootdev_setup_iter(iter, &dev, &method_flags);
ret = bootdev_setup_iter(iter, label, &dev, &method_flags);
if (ret)
return log_msg_ret("obdev", -ENODEV);
@ -345,7 +376,7 @@ int bootflow_scan_first(struct bootflow_iter *iter, int flags,
{
int ret;
ret = bootflow_scan_bootdev(NULL, iter, flags, bflow);
ret = bootflow_scan_bootdev(NULL, NULL, iter, flags, bflow);
if (ret)
return log_msg_ret("start", ret);

View file

@ -93,11 +93,12 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
{
struct bootstd_priv *std;
struct bootflow_iter iter;
struct udevice *dev;
struct udevice *dev = NULL;
struct bootflow bflow;
bool all = false, boot = false, errors = false, no_global = false;
bool list = false, no_hunter = false;
int num_valid = 0;
const char *label = NULL;
bool has_args;
int ret, i;
int flags;
@ -105,7 +106,6 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
ret = bootstd_get_priv(&std);
if (ret)
return CMD_RET_FAILURE;
dev = std->cur_bootdev;
has_args = argc > 1 && *argv[1] == '-';
if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) {
@ -119,12 +119,10 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
argc--;
argv++;
}
if (argc > 1) {
const char *label = argv[1];
if (bootdev_find_by_any(label, &dev, NULL))
return CMD_RET_FAILURE;
}
if (argc > 1)
label = argv[1];
if (!label)
dev = std->cur_bootdev;
} else {
if (has_args) {
printf("Flags not supported: enable CONFIG_BOOTFLOW_FULL\n");
@ -148,54 +146,36 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
/*
* If we have a device, just scan for bootflows attached to that device
*/
if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && dev) {
if (list) {
printf("Scanning for bootflows in bootdev '%s'\n",
dev->name);
show_header();
}
if (list) {
printf("Scanning for bootflows ");
if (dev)
printf("in bootdev '%s'\n", dev->name);
else if (label)
printf("with label '%s'\n", label);
else
printf("in all bootdevs\n");
show_header();
}
if (dev)
bootdev_clear_bootflows(dev);
for (i = 0,
ret = bootflow_scan_bootdev(dev, &iter, flags, &bflow);
i < 1000 && ret != -ENODEV;
i++, ret = bootflow_scan_next(&iter, &bflow)) {
bflow.err = ret;
if (!ret)
num_valid++;
ret = bootdev_add_bootflow(&bflow);
if (ret) {
printf("Out of memory\n");
return CMD_RET_FAILURE;
}
if (list)
show_bootflow(i, &bflow, errors);
if (boot && !bflow.err)
bootflow_run_boot(&iter, &bflow);
}
} else {
if (list) {
printf("Scanning for bootflows in all bootdevs\n");
show_header();
}
else
bootstd_clear_glob();
for (i = 0,
ret = bootflow_scan_first(&iter, flags, &bflow);
i < 1000 && ret != -ENODEV;
i++, ret = bootflow_scan_next(&iter, &bflow)) {
bflow.err = ret;
if (!ret)
num_valid++;
ret = bootdev_add_bootflow(&bflow);
if (ret) {
printf("Out of memory\n");
return CMD_RET_FAILURE;
}
if (list)
show_bootflow(i, &bflow, errors);
if (boot && !bflow.err)
bootflow_run_boot(&iter, &bflow);
for (i = 0,
ret = bootflow_scan_bootdev(dev, label, &iter, flags, &bflow);
i < 1000 && ret != -ENODEV;
i++, ret = bootflow_scan_next(&iter, &bflow)) {
bflow.err = ret;
if (!ret)
num_valid++;
ret = bootdev_add_bootflow(&bflow);
if (ret) {
printf("Out of memory\n");
return CMD_RET_FAILURE;
}
if (list)
show_bootflow(i, &bflow, errors);
if (boot && !bflow.err)
bootflow_run_boot(&iter, &bflow);
}
bootflow_iter_uninit(&iter);
if (list)

View file

@ -267,10 +267,13 @@ int bootdev_find_by_any(const char *name, struct udevice **devp,
/**
* bootdev_setup_iter() - Set up iteration through bootdevs
*
* This sets up the an interation, based on the priority of each bootdev, the
* bootdev-order property in the bootstd node (or the boot_targets env var).
* This sets up the an interation, based on the provided device or label. If
* neither is provided, the iteration is based on the priority of each bootdev,
* the * bootdev-order property in the bootstd node (or the boot_targets env
* var).
*
* @iter: Iterator to update with the order
* @label: label to scan, or NULL to scan all
* @devp: On entry, *devp is NULL to scan all, otherwise this is the (single)
* device to scan. Returns the first device to use, which is the passed-in
* @devp if it was non-NULL
@ -279,8 +282,8 @@ int bootdev_find_by_any(const char *name, struct udevice **devp,
* Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve
* on other error
*/
int bootdev_setup_iter(struct bootflow_iter *iter, struct udevice **devp,
int *method_flagsp);
int bootdev_setup_iter(struct bootflow_iter *iter, const char *label,
struct udevice **devp, int *method_flagsp);
/**
* bootdev_list_hunters() - List the available bootdev hunters

View file

@ -255,14 +255,18 @@ int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
*
* @dev: Boot device to scan, NULL to work through all of them until it
* finds one that can supply a bootflow
* @label: Label to control the scan, NULL to work through all devices
* until it finds one that can supply a bootflow
* @iter: Place to store private info (inited by this call)
* @flags: Flags for iterator (enum bootflow_flags_t)
* @flags: Flags for iterator (enum bootflow_flags_t). Note that if @dev
* is NULL, then BOOTFLOWF_SKIP_GLOBAL is set automatically by this function
* @bflow: Place to put the bootflow if found
* Return: 0 if found, -ENODEV if no device, other -ve on other error
* (iteration can continue)
*/
int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter,
int flags, struct bootflow *bflow);
int bootflow_scan_bootdev(struct udevice *dev, const char *label,
struct bootflow_iter *iter, int flags,
struct bootflow *bflow);
/**
* bootflow_scan_first() - find the first bootflow

View file

@ -626,7 +626,7 @@ static int bootdev_test_next_prio(struct unit_test_state *uts)
struct udevice *dev;
int ret;
sandbox_set_eth_enable(false);
test_set_eth_enable(false);
test_set_skip_delays(true);
/* get access to the used hunters */

View file

@ -76,7 +76,6 @@ static int bootflow_cmd(struct unit_test_state *uts)
}
BOOTSTD_TEST(bootflow_cmd, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
#if 0 /* disable for now */
/* Check 'bootflow scan' with a label / seq */
static int bootflow_cmd_label(struct unit_test_state *uts)
{
@ -124,7 +123,6 @@ static int bootflow_cmd_label(struct unit_test_state *uts)
}
BOOTSTD_TEST(bootflow_cmd_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
UT_TESTF_ETH_BOOTDEV);
#endif
/* Check 'bootflow scan/list' commands using all bootdevs */
static int bootflow_cmd_glob(struct unit_test_state *uts)
@ -564,6 +562,66 @@ static int bootflow_cmd_menu(struct unit_test_state *uts)
}
BOOTSTD_TEST(bootflow_cmd_menu, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
/* Check searching for a single bootdev using the hunters */
static int bootflow_cmd_hunt_single(struct unit_test_state *uts)
{
struct bootstd_priv *std;
/* get access to the used hunters */
ut_assertok(bootstd_get_priv(&std));
ut_assertok(bootstd_test_drop_bootdev_order(uts));
console_record_reset_enable();
ut_assertok(run_command("bootflow scan -l mmc1", 0));
ut_assert_nextline("Scanning for bootflows with label 'mmc1'");
ut_assert_skip_to_line("(1 bootflow, 1 valid)");
ut_assert_console_end();
/* check that the hunter was used */
ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
return 0;
}
BOOTSTD_TEST(bootflow_cmd_hunt_single, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
/* Check searching for a uclass label using the hunters */
static int bootflow_cmd_hunt_label(struct unit_test_state *uts)
{
struct bootstd_priv *std;
/* get access to the used hunters */
ut_assertok(bootstd_get_priv(&std));
test_set_skip_delays(true);
test_set_eth_enable(false);
ut_assertok(bootstd_test_drop_bootdev_order(uts));
console_record_reset_enable();
ut_assertok(run_command("bootflow scan -l mmc", 0));
/* check that the hunter was used */
ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
/* check that we got the mmc1 bootflow */
ut_assert_nextline("Scanning for bootflows with label 'mmc'");
ut_assert_nextlinen("Seq");
ut_assert_nextlinen("---");
ut_assert_nextline("Hunting with: simple_bus");
ut_assert_nextline("Found 2 extension board(s).");
ut_assert_nextline("Hunting with: mmc");
ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
ut_assert_nextline(
" 0 syslinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
ut_assert_skip_to_line("(1 bootflow, 1 valid)");
ut_assert_console_end();
return 0;
}
BOOTSTD_TEST(bootflow_cmd_hunt_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
/**
* check_font() - Check that the font size for an item matches expectations
*