summaryrefslogtreecommitdiff
path: root/boot/bootdev-uclass.c
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-01-24 14:04:14 -0500
committerTom Rini <trini@konsulko.com>2023-01-24 14:04:14 -0500
commit4e1ab2065e21e48a3087144ab826f12cfb797a65 (patch)
tree1dc9e793258c5a4a1be4d5e6d554f7f1a82450f3 /boot/bootdev-uclass.c
parentdd31cd58b02729807934cb699b164b1f8736620f (diff)
parent3891c68ef50eda38d78c95ecd03aed030aa6bb53 (diff)
Merge branch '2023-01-24-bootstd-allow-migration-from-distro_bootcmd-script'
To quote the author: So far, standard boot does not replicate all the of the functionality of the distro_bootcmd scripts. In particular it lacks some bootdevs and some of the bootmeths are incomplete. Also there is currently no internal mechanism to enumerate buses in order to discover bootdevs, e.g. with USB. This series addresses these shortcomings: - Adds the concept of a 'bootdev hunter' to enumerate buses, etc. in an effort to find bootdevs of a certain priority - Adds bootdevs for SCSI, IDE, NVMe, virtio, SPI flash - Handles PXE and DHCP properly - Supports reading the device tree with EFI and reading scripts from the network It also tidies up label processing, so it is possible to use: bootflow scan mmc2 to scan just one MMC device (with BOOTSTD_FULL). As before this implementation still relies on CONFIG_CMDLINE being enabled, mostly for the network stack. Further work would be required to disentangle that. Quite a few tests are added but there are some gaps: - SPI flash bootdev - EFI FDT loading Note that SATA works via SCSI (CONFIG_SCSI_AHCI) and does not use driver model. Only pogo_v4 seems to be affected. Probably all thats is needed is to call bootdev_setup_sibling_blk() in the Marvell SATA driver. Also, while it would be possible to init MMC in a bootdev hunter, there is no point since U-Boot always inits MMC on startup, if present. With this series it should be possible to migrate boards to standard boot by removing the inclusion of config_distro_bootcmd.h and instead adding a suitable value for boot_targets to the environment, e.g.: boot_targets=mmc1 mmc0 nvme scsi usb pxe dhcp spi Thus it is possible to boot automatically without scripts and boards can use a text-based environment instead of the config.h files. To demonstrate this, rockpro64-rk3399 is migrated to standard boot in this series. Full migration could probably be automated using a script, similar in concept to moveconfig: - obtain the board environment via 'make u-boot-initial-env' - get the value of "boot_targets" - drop config_distro_bootcmd.h from the config.h file - rebuild again to get the environment without distro scripts - write the environment (adding boot_targets) to board.env - remove CONFIG_EXTRA_ENV_SETTINGS from the config.h file
Diffstat (limited to 'boot/bootdev-uclass.c')
-rw-r--r--boot/bootdev-uclass.c551
1 files changed, 384 insertions, 167 deletions
diff --git a/boot/bootdev-uclass.c b/boot/bootdev-uclass.c
index affe0d3e04e..99ee08e3353 100644
--- a/boot/bootdev-uclass.c
+++ b/boot/bootdev-uclass.c
@@ -12,7 +12,6 @@
#include <bootflow.h>
#include <bootmeth.h>
#include <bootstd.h>
-#include <env.h>
#include <fs.h>
#include <log.h>
#include <malloc.h>
@@ -164,7 +163,15 @@ int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
*/
iter->max_part = MAX_PART_PER_BOOTDEV;
- if (iter->part) {
+ /* If this is the whole disk, check if we have bootable partitions */
+ if (!iter->part) {
+ iter->first_bootable = part_get_bootable(desc);
+ log_debug("checking bootable=%d\n", iter->first_bootable);
+
+ /* if there are bootable partitions, scan only those */
+ } else if (iter->first_bootable ? !info.bootable : iter->part != 1) {
+ return log_msg_ret("boot", -EINVAL);
+ } else {
ret = fs_set_blk_dev_with_part(desc, bflow->part);
bflow->state = BOOTFLOWST_PART;
@@ -235,13 +242,27 @@ int bootdev_setup_for_dev(struct udevice *parent, const char *drv_name)
return 0;
}
+static int bootdev_get_suffix_start(struct udevice *dev, const char *suffix)
+{
+ int len, slen;
+
+ len = strlen(dev->name);
+ slen = strlen(suffix);
+ if (len > slen && !strcmp(suffix, dev->name + len - slen))
+ return len - slen;
+
+ return len;
+}
+
int bootdev_setup_sibling_blk(struct udevice *blk, const char *drv_name)
{
struct udevice *parent, *dev;
char dev_name[50];
- int ret;
+ int ret, len;
- snprintf(dev_name, sizeof(dev_name), "%s.%s", blk->name, "bootdev");
+ len = bootdev_get_suffix_start(blk, ".blk");
+ snprintf(dev_name, sizeof(dev_name), "%.*s.%s", len, blk->name,
+ "bootdev");
parent = dev_get_parent(blk);
ret = device_find_child_by_name(parent, dev_name, &dev);
@@ -272,20 +293,22 @@ int bootdev_get_sibling_blk(struct udevice *dev, struct udevice **blkp)
struct udevice *parent = dev_get_parent(dev);
struct udevice *blk;
int ret, len;
- char *p;
if (device_get_uclass_id(dev) != UCLASS_BOOTDEV)
return -EINVAL;
/* This should always work if bootdev_setup_sibling_blk() was used */
- p = strstr(dev->name, ".bootdev");
- if (!p)
- return log_msg_ret("str", -EINVAL);
-
- len = p - dev->name;
+ len = bootdev_get_suffix_start(dev, ".bootdev");
ret = device_find_child_by_namelen(parent, dev->name, len, &blk);
- if (ret)
- return log_msg_ret("find", ret);
+ if (ret) {
+ char dev_name[50];
+
+ snprintf(dev_name, sizeof(dev_name), "%.*s.blk", len,
+ dev->name);
+ ret = device_find_child_by_name(parent, dev_name, &blk);
+ if (ret)
+ return log_msg_ret("find", ret);
+ }
*blkp = blk;
return 0;
@@ -296,13 +319,15 @@ static int bootdev_get_from_blk(struct udevice *blk, struct udevice **bootdevp)
struct udevice *parent = dev_get_parent(blk);
struct udevice *bootdev;
char dev_name[50];
- int ret;
+ int ret, len;
if (device_get_uclass_id(blk) != UCLASS_BLK)
return -EINVAL;
/* This should always work if bootdev_setup_sibling_blk() was used */
- snprintf(dev_name, sizeof(dev_name), "%s.%s", blk->name, "bootdev");
+ len = bootdev_get_suffix_start(blk, ".blk");
+ snprintf(dev_name, sizeof(dev_name), "%.*s.%s", len, blk->name,
+ "bootdev");
ret = device_find_child_by_name(parent, dev_name, &bootdev);
if (ret)
return log_msg_ret("find", ret);
@@ -330,36 +355,67 @@ int bootdev_unbind_dev(struct udevice *parent)
}
/**
- * bootdev_find_by_label() - Convert a label string to a bootdev device
- *
- * Looks up a label name to find the associated bootdev. For example, if the
- * label name is "mmc2", this will find a bootdev for an mmc device whose
- * sequence number is 2.
+ * label_to_uclass() - Convert a label to a uclass and sequence number
*
- * @label: Label string to convert, e.g. "mmc2"
- * @devp: Returns bootdev device corresponding to that boot label
- * Return: 0 if OK, -EINVAL if the label name (e.g. "mmc") does not refer to a
- * uclass, -ENOENT if no bootdev for that media has the sequence number
- * (e.g. 2)
+ * @label: Label to look up (e.g. "mmc1" or "mmc0")
+ * @seqp: Returns the sequence number, or -1 if none
+ * @method_flagsp: If non-NULL, returns any flags implied by the label
+ * (enum bootflow_meth_flags_t), 0 if none
+ * Returns: sequence number on success, else -ve error code
*/
-int bootdev_find_by_label(const char *label, struct udevice **devp)
+static int label_to_uclass(const char *label, int *seqp, int *method_flagsp)
{
- struct udevice *media;
- struct uclass *uc;
+ int seq, len, method_flags;
enum uclass_id id;
const char *end;
- int seq;
+ method_flags = 0;
seq = trailing_strtoln_end(label, NULL, &end);
- id = uclass_get_by_namelen(label, end - label);
+ len = end - label;
+ if (!len)
+ return -EINVAL;
+ id = uclass_get_by_namelen(label, len);
log_debug("find %s: seq=%d, id=%d/%s\n", label, seq, id,
uclass_get_name(id));
if (id == UCLASS_INVALID) {
- log_warning("Unknown uclass '%s' in label\n", label);
- return -EINVAL;
+ /* try some special cases */
+ if (IS_ENABLED(CONFIG_BOOTDEV_SPI_FLASH) &&
+ !strncmp("spi", label, len)) {
+ id = UCLASS_SPI_FLASH;
+ } else if (IS_ENABLED(CONFIG_BOOTDEV_ETH) &&
+ !strncmp("pxe", label, len)) {
+ id = UCLASS_ETH;
+ method_flags |= BOOTFLOW_METHF_PXE_ONLY;
+ } else if (IS_ENABLED(CONFIG_BOOTDEV_ETH) &&
+ !strncmp("dhcp", label, len)) {
+ id = UCLASS_ETH;
+ method_flags |= BOOTFLOW_METHF_DHCP_ONLY;
+ } else {
+ log_warning("Unknown uclass '%s' in label\n", label);
+ return -EINVAL;
+ }
}
if (id == UCLASS_USB)
id = UCLASS_MASS_STORAGE;
+ *seqp = seq;
+ if (method_flagsp)
+ *method_flagsp = method_flags;
+
+ return id;
+}
+
+int bootdev_find_by_label(const char *label, struct udevice **devp,
+ int *method_flagsp)
+{
+ int seq, ret, method_flags = 0;
+ struct udevice *media;
+ struct uclass *uc;
+ enum uclass_id id;
+
+ ret = label_to_uclass(label, &seq, &method_flags);
+ if (ret < 0)
+ return log_msg_ret("uc", ret);
+ id = ret;
/* Iterate through devices in the media uclass (e.g. UCLASS_MMC) */
uclass_id_foreach_dev(id, media, uc) {
@@ -386,6 +442,15 @@ int bootdev_find_by_label(const char *label, struct udevice **devp)
if (!ret) {
log_debug("- found %s\n", bdev->name);
*devp = bdev;
+
+ /*
+ * if no sequence number was provided, we must scan all
+ * bootdevs for this media uclass
+ */
+ if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && seq == -1)
+ method_flags |= BOOTFLOW_METHF_SINGLE_UCLASS;
+ if (method_flagsp)
+ *method_flagsp = method_flags;
return 0;
}
log_debug("- no device in %s\n", media->name);
@@ -395,10 +460,12 @@ int bootdev_find_by_label(const char *label, struct udevice **devp)
return -ENOENT;
}
-int bootdev_find_by_any(const char *name, struct udevice **devp)
+int bootdev_find_by_any(const char *name, struct udevice **devp,
+ int *method_flagsp)
{
struct udevice *dev;
- int ret, seq;
+ int method_flags = 0;
+ int ret = -ENODEV, seq;
char *endp;
seq = simple_strtol(name, &endp, 16);
@@ -407,21 +474,22 @@ int bootdev_find_by_any(const char *name, struct udevice **devp)
if (*endp) {
ret = uclass_get_device_by_name(UCLASS_BOOTDEV, name, &dev);
if (ret == -ENODEV) {
- ret = bootdev_find_by_label(name, &dev);
+ ret = bootdev_find_by_label(name, &dev, &method_flags);
if (ret) {
printf("Cannot find bootdev '%s' (err=%d)\n",
name, ret);
- return ret;
+ return log_msg_ret("lab", ret);
}
ret = device_probe(dev);
}
if (ret) {
printf("Cannot probe bootdev '%s' (err=%d)\n", name,
ret);
- return ret;
+ return log_msg_ret("pro", ret);
}
- } else {
+ } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
ret = uclass_get_device_by_seq(UCLASS_BOOTDEV, seq, &dev);
+ method_flags |= BOOTFLOW_METHF_SINGLE_DEV;
}
if (ret) {
printf("Cannot find '%s' (err=%d)\n", name, ret);
@@ -429,6 +497,46 @@ int bootdev_find_by_any(const char *name, struct udevice **devp)
}
*devp = dev;
+ if (method_flagsp)
+ *method_flagsp = method_flags;
+
+ return 0;
+}
+
+int bootdev_hunt_and_find_by_label(const char *label, struct udevice **devp,
+ int *method_flagsp)
+{
+ int ret;
+
+ ret = bootdev_hunt(label, false);
+ if (ret)
+ return log_msg_ret("scn", ret);
+ ret = bootdev_find_by_label(label, devp, method_flagsp);
+ if (ret)
+ return log_msg_ret("fnd", ret);
+
+ return 0;
+}
+
+static int default_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+ struct bootflow *bflow)
+{
+ struct udevice *blk;
+ int ret;
+
+ ret = bootdev_get_sibling_blk(dev, &blk);
+ /*
+ * If there is no media, indicate that no more partitions should be
+ * checked
+ */
+ if (ret == -EOPNOTSUPP)
+ ret = -ESHUTDOWN;
+ if (ret)
+ return log_msg_ret("blk", ret);
+ assert(blk);
+ ret = bootdev_find_in_blk(dev, blk, iter, bflow);
+ if (ret)
+ return log_msg_ret("find", ret);
return 0;
}
@@ -438,9 +546,10 @@ int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
{
const struct bootdev_ops *ops = bootdev_get_ops(dev);
- if (!ops->get_bootflow)
- return -ENOSYS;
+ log_debug("->get_bootflow %s=%p\n", dev->name, ops->get_bootflow);
bootflow_init(bflow, dev, iter->method);
+ if (!ops->get_bootflow)
+ return default_get_bootflow(dev, iter, bflow);
return ops->get_bootflow(dev, iter, bflow);
}
@@ -458,116 +567,95 @@ void bootdev_clear_bootflows(struct udevice *dev)
}
}
-/**
- * h_cmp_bootdev() - Compare two bootdevs to find out which should go first
- *
- * @v1: struct udevice * of first bootdev device
- * @v2: struct udevice * of second bootdev device
- * Return: sort order (<0 if dev1 < dev2, ==0 if equal, >0 if dev1 > dev2)
- */
-static int h_cmp_bootdev(const void *v1, const void *v2)
+int bootdev_next_label(struct bootflow_iter *iter, struct udevice **devp,
+ int *method_flagsp)
{
- const struct udevice *dev1 = *(struct udevice **)v1;
- const struct udevice *dev2 = *(struct udevice **)v2;
- const struct bootdev_uc_plat *ucp1 = dev_get_uclass_plat(dev1);
- const struct bootdev_uc_plat *ucp2 = dev_get_uclass_plat(dev2);
- int diff;
+ struct udevice *dev;
- /* Use priority first */
- diff = ucp1->prio - ucp2->prio;
- if (diff)
- return diff;
+ log_debug("next\n");
+ for (dev = NULL; !dev && iter->labels[++iter->cur_label];) {
+ log_debug("Scanning: %s\n", iter->labels[iter->cur_label]);
+ bootdev_hunt_and_find_by_label(iter->labels[iter->cur_label],
+ &dev, method_flagsp);
+ }
- /* Fall back to seq for devices of the same priority */
- diff = dev_seq(dev1) - dev_seq(dev2);
+ if (!dev)
+ return log_msg_ret("fin", -ENODEV);
+ *devp = dev;
- return diff;
+ return 0;
}
-/**
- * build_order() - Build the ordered list of bootdevs to use
- *
- * This builds an ordered list of devices by one of three methods:
- * - using the boot_targets environment variable, if non-empty
- * - using the bootdev-order devicetree property, if present
- * - sorted by priority and sequence number
- *
- * @bootstd: BOOTSTD device to use
- * @order: Bootdevs listed in default order
- * @max_count: Number of entries in @order
- * Return: number of bootdevs found in the ordering, or -E2BIG if the
- * boot_targets string is too long, or -EXDEV if the ordering produced 0 results
- */
-static int build_order(struct udevice *bootstd, struct udevice **order,
- int max_count)
+int bootdev_next_prio(struct bootflow_iter *iter, struct udevice **devp)
{
- const char *overflow_target = NULL;
- const char *const *labels;
- struct udevice *dev;
- const char *targets;
- int i, ret, count;
-
- targets = env_get("boot_targets");
- labels = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
- bootstd_get_bootdev_order(bootstd) : NULL;
- if (targets) {
- char str[BOOT_TARGETS_MAX_LEN];
- char *target;
-
- if (strlen(targets) >= BOOT_TARGETS_MAX_LEN)
- return log_msg_ret("len", -E2BIG);
-
- /* make a copy of the string, since strok() will change it */
- strcpy(str, targets);
- for (i = 0, target = strtok(str, " "); target;
- target = strtok(NULL, " ")) {
- ret = bootdev_find_by_label(target, &dev);
- if (!ret) {
- if (i == max_count) {
- overflow_target = target;
- break;
- }
- order[i++] = dev;
- }
+ struct udevice *dev = *devp;
+ bool found;
+ int ret;
+
+ /* find the next device with this priority */
+ *devp = NULL;
+ log_debug("next prio %d: dev=%p/%s\n", iter->cur_prio, dev,
+ dev ? dev->name : "none");
+ do {
+ /*
+ * Don't probe devices here since they may not be of the
+ * required priority
+ */
+ if (!dev)
+ uclass_find_first_device(UCLASS_BOOTDEV, &dev);
+ else
+ uclass_find_next_device(&dev);
+ found = false;
+
+ /* scan for the next device with the correct priority */
+ while (dev) {
+ struct bootdev_uc_plat *plat;
+
+ plat = dev_get_uclass_plat(dev);
+ log_debug("- %s: %d, want %d\n", dev->name, plat->prio,
+ iter->cur_prio);
+ if (plat->prio == iter->cur_prio)
+ break;
+ uclass_find_next_device(&dev);
}
- count = i;
- } else if (labels) {
- int upto;
- upto = 0;
- for (i = 0; labels[i]; i++) {
- ret = bootdev_find_by_label(labels[i], &dev);
- if (!ret) {
- if (upto == max_count) {
- overflow_target = labels[i];
- break;
- }
- order[upto++] = dev;
+ /* none found for this priority, so move to the next */
+ if (!dev) {
+ log_debug("None found at prio %d, moving to %d\n",
+ iter->cur_prio, iter->cur_prio + 1);
+ if (++iter->cur_prio == BOOTDEVP_COUNT)
+ return log_msg_ret("fin", -ENODEV);
+
+ if (iter->flags & BOOTFLOWF_HUNT) {
+ /* hunt to find new bootdevs */
+ ret = bootdev_hunt_prio(iter->cur_prio,
+ iter->flags &
+ BOOTFLOWF_SHOW);
+ log_debug("- hunt ret %d\n", ret);
+ if (ret)
+ return log_msg_ret("hun", ret);
+ }
+ } else {
+ ret = device_probe(dev);
+ if (ret) {
+ log_debug("Device '%s' failed to probe\n",
+ dev->name);
+ dev = NULL;
}
}
- count = upto;
- } else {
- /* sort them into priority order */
- count = max_count;
- qsort(order, count, sizeof(struct udevice *), h_cmp_bootdev);
- }
-
- if (overflow_target) {
- log_warning("Expected at most %d bootdevs, but overflowed with boot_target '%s'\n",
- max_count, overflow_target);
- }
+ } while (!dev);
- if (!count)
- return log_msg_ret("targ", -EXDEV);
+ *devp = dev;
- return count;
+ return 0;
}
-int bootdev_setup_iter_order(struct bootflow_iter *iter, struct udevice **devp)
+int bootdev_setup_iter(struct bootflow_iter *iter, const char *label,
+ struct udevice **devp, int *method_flagsp)
{
- struct udevice *bootstd, *dev = *devp, **order;
- int upto, i;
- int count;
+ struct udevice *bootstd, *dev = NULL;
+ bool show = iter->flags & BOOTFLOWF_SHOW;
+ int method_flags;
int ret;
ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
@@ -576,53 +664,182 @@ int bootdev_setup_iter_order(struct bootflow_iter *iter, struct udevice **devp)
return log_msg_ret("std", ret);
}
- /* Handle scanning a single device */
- if (dev) {
- iter->flags |= BOOTFLOWF_SINGLE_DEV;
- return 0;
+ /* hunt for any pre-scan devices */
+ if (iter->flags & BOOTFLOWF_HUNT) {
+ ret = bootdev_hunt_prio(BOOTDEVP_1_PRE_SCAN, show);
+ if (ret)
+ return log_msg_ret("pre", ret);
}
- count = uclass_id_count(UCLASS_BOOTDEV);
- if (!count)
- return log_msg_ret("count", -ENOENT);
-
- order = calloc(count, sizeof(struct udevice *));
- if (!order)
- return log_msg_ret("order", -ENOMEM);
+ /* Handle scanning a single device */
+ 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);
- /*
- * Get a list of bootdevs, in seq order (i.e. using aliases). There may
- * be gaps so try to count up high enough to find them all.
- */
- for (i = 0, upto = 0; upto < count && i < 20 + count * 2; i++) {
- ret = uclass_find_device_by_seq(UCLASS_BOOTDEV, i, &dev);
- if (!ret)
- order[upto++] = dev;
- }
- log_debug("Found %d bootdevs\n", count);
- if (upto != count)
- log_debug("Expected %d bootdevs, found %d using aliases\n",
- count, upto);
-
- ret = build_order(bootstd, order, upto);
- if (ret < 0) {
- free(order);
- return log_msg_ret("build", 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;
+
+ /* This either returns a non-empty list or NULL */
+ iter->labels = bootstd_get_bootdev_order(bootstd, &ok);
+ if (!ok)
+ return log_msg_ret("ord", -ENOMEM);
+ log_debug("setup labels %p\n", iter->labels);
+ if (iter->labels) {
+ iter->cur_label = -1;
+ ret = bootdev_next_label(iter, &dev, &method_flags);
+ } else {
+ ret = bootdev_next_prio(iter, &dev);
+ method_flags = 0;
+ }
+ if (!dev)
+ return log_msg_ret("fin", -ENOENT);
+ log_debug("Selected bootdev: %s\n", dev->name);
}
- iter->num_devs = ret;
- iter->dev_order = order;
- iter->cur_dev = 0;
-
- dev = *order;
ret = device_probe(dev);
if (ret)
return log_msg_ret("probe", ret);
+ if (method_flagsp)
+ *method_flagsp = method_flags;
*devp = dev;
return 0;
}
+static int bootdev_hunt_drv(struct bootdev_hunter *info, uint seq, bool show)
+{
+ const char *name = uclass_get_name(info->uclass);
+ struct bootstd_priv *std;
+ int ret;
+
+ ret = bootstd_get_priv(&std);
+ if (ret)
+ return log_msg_ret("std", ret);
+
+ if (!(std->hunters_used & BIT(seq))) {
+ if (show)
+ printf("Hunting with: %s\n",
+ uclass_get_name(info->uclass));
+ log_debug("Hunting with: %s\n", name);
+ if (info->hunt) {
+ ret = info->hunt(info, show);
+ if (ret)
+ return ret;
+ }
+ std->hunters_used |= BIT(seq);
+ }
+
+ return 0;
+}
+
+int bootdev_hunt(const char *spec, bool show)
+{
+ struct bootdev_hunter *start;
+ const char *end;
+ int n_ent, i;
+ int result;
+ size_t len;
+
+ start = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
+ n_ent = ll_entry_count(struct bootdev_hunter, bootdev_hunter);
+ result = 0;
+
+ len = SIZE_MAX;
+ if (spec) {
+ trailing_strtoln_end(spec, NULL, &end);
+ len = end - spec;
+ }
+
+ for (i = 0; i < n_ent; i++) {
+ struct bootdev_hunter *info = start + i;
+ const char *name = uclass_get_name(info->uclass);
+ int ret;
+
+ log_debug("looking at %.*s for %s\n",
+ (int)max(strlen(name), len), spec, name);
+ if (spec && strncmp(spec, name, max(strlen(name), len))) {
+ if (info->uclass != UCLASS_ETH ||
+ (strcmp("dhcp", spec) && strcmp("pxe", spec)))
+ continue;
+ }
+ ret = bootdev_hunt_drv(info, i, show);
+ if (ret)
+ result = ret;
+ }
+
+ return result;
+}
+
+int bootdev_hunt_prio(enum bootdev_prio_t prio, bool show)
+{
+ struct bootdev_hunter *start;
+ int n_ent, i;
+ int result;
+
+ start = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
+ n_ent = ll_entry_count(struct bootdev_hunter, bootdev_hunter);
+ result = 0;
+
+ log_debug("Hunting for priority %d\n", prio);
+ for (i = 0; i < n_ent; i++) {
+ struct bootdev_hunter *info = start + i;
+ int ret;
+
+ if (prio != info->prio)
+ continue;
+ ret = bootdev_hunt_drv(info, i, show);
+ if (ret && ret != -ENOENT)
+ result = ret;
+ }
+
+ return result;
+}
+
+void bootdev_list_hunters(struct bootstd_priv *std)
+{
+ struct bootdev_hunter *orig, *start;
+ int n_ent, i;
+
+ orig = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
+ n_ent = ll_entry_count(struct bootdev_hunter, bootdev_hunter);
+
+ /*
+ * workaround for strange bug in clang-12 which sees all the below data
+ * as zeroes. Any access of start seems to fix it, such as
+ *
+ * printf("%p", start);
+ *
+ * Use memcpy() to force the correct behaviour.
+ */
+ memcpy(&start, &orig, sizeof(orig));
+ printf("%4s %4s %-15s %s\n", "Prio", "Used", "Uclass", "Hunter");
+ printf("%4s %4s %-15s %s\n", "----", "----", "---------------", "---------------");
+ for (i = 0; i < n_ent; i++) {
+ struct bootdev_hunter *info = start + i;
+
+ printf("%4d %4s %-15s %s\n", info->prio,
+ std->hunters_used & BIT(i) ? "*" : "",
+ uclass_get_name(info->uclass),
+ info->drv ? info->drv->name : "(none)");
+ }
+
+ printf("(total hunters: %d)\n", n_ent);
+}
+
static int bootdev_post_bind(struct udevice *dev)
{
struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);