diff options
author | Tom Rini | 2023-01-24 14:04:14 -0500 |
---|---|---|
committer | Tom Rini | 2023-01-24 14:04:14 -0500 |
commit | 4e1ab2065e21e48a3087144ab826f12cfb797a65 (patch) | |
tree | 1dc9e793258c5a4a1be4d5e6d554f7f1a82450f3 | |
parent | dd31cd58b02729807934cb699b164b1f8736620f (diff) | |
parent | 3891c68ef50eda38d78c95ecd03aed030aa6bb53 (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
79 files changed, 3225 insertions, 715 deletions
diff --git a/arch/sandbox/cpu/state.c b/arch/sandbox/cpu/state.c index dd7978cfced..69da378ab59 100644 --- a/arch/sandbox/cpu/state.c +++ b/arch/sandbox/cpu/state.c @@ -12,6 +12,7 @@ #include <os.h> #include <asm/malloc.h> #include <asm/state.h> +#include <asm/test.h> /* Main state record for the sandbox */ static struct sandbox_state main_state; @@ -366,6 +367,7 @@ void state_reset_for_test(struct sandbox_state *state) state->sysreset_allowed[SYSRESET_POWER_OFF] = true; state->sysreset_allowed[SYSRESET_COLD] = true; state->allow_memio = false; + sandbox_set_eth_enable(true); memset(&state->wdt, '\0', sizeof(state->wdt)); memset(state->spi, '\0', sizeof(state->spi)); @@ -444,6 +446,34 @@ int state_load_other_fdt(const char **bufp, int *sizep) return 0; } +void sandbox_set_eth_enable(bool enable) +{ + struct sandbox_state *state = state_get_current(); + + state->disable_eth = !enable; +} + +bool sandbox_eth_enabled(void) +{ + struct sandbox_state *state = state_get_current(); + + return !state->disable_eth; +} + +void sandbox_sf_set_enable_bootdevs(bool enable) +{ + struct sandbox_state *state = state_get_current(); + + state->disable_sf_bootdevs = !enable; +} + +bool sandbox_sf_bootdev_enabled(void) +{ + struct sandbox_state *state = state_get_current(); + + return !state->disable_sf_bootdevs; +} + int state_init(void) { state = &main_state; diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index afe598a4f56..18bf1cb5b69 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -12,19 +12,6 @@ chosen { stdout-path = "/serial"; - - fwupd { - compatible = "simple-bus"; - firmware { - compatible = "fwupd,vbe-simple"; - cur-version = "1.2.3"; - bootloader-version = "2022.01"; - storage = "mmc1"; - area-start = <0x0>; - area-size = <0x1000000>; - skip-offset = <0x8000>; - }; - }; }; alarm_wdt: alarm-wdt { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 2e580f980fc..9d96e479ca8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1527,12 +1527,18 @@ sandbox_virtio1 { compatible = "sandbox,virtio1"; + virtio-type = <4>; /* rng */ }; sandbox_virtio2 { compatible = "sandbox,virtio2"; }; + sandbox-virtio-blk { + compatible = "sandbox,virtio1"; + virtio-type = <2>; /* block */ + }; + sandbox_scmi { compatible = "sandbox,scmi-devices"; clocks = <&clk_scmi 2>, <&clk_scmi 0>; diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index 49ea483d332..59a20595f51 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -96,6 +96,8 @@ struct sandbox_state { const char *select_unittests; /* Unit test to run */ bool handle_signals; /* Handle signals within sandbox */ bool autoboot_keyed; /* Use keyed-autoboot feature */ + bool disable_eth; /* Disable Ethernet devices */ + bool disable_sf_bootdevs; /* Don't bind SPI flash bootdevs */ /* Pointer to information for each SPI bus/cs */ struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS] diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index 568738c16d5..4853dc948f3 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -344,4 +344,34 @@ void sandbox_set_fake_efi_mgr_dev(struct udevice *dev, bool fake_dev); */ int sandbox_load_other_fdt(void **fdtp, int *sizep); +/** + * sandbox_set_eth_enable() - Enable / disable Ethernet + * + * Allows control of whether Ethernet packets are actually send/received + * + * @enable: true to enable Ethernet, false to disable + */ +void sandbox_set_eth_enable(bool enable); + +/** + * sandbox_eth_enabled() - Check if Ethernet is enabled + * + * Returns: true if Ethernet is enabled on sandbox, False if not + */ +bool sandbox_eth_enabled(void); + +/** + * sandbox_sf_bootdev_enabled() - Check if SPI flash bootdevs should be bound + * + * Returns: true if sandbox should bind bootdevs for SPI flash, false if not + */ +bool sandbox_sf_bootdev_enabled(void); + +/** + * sandbox_sf_set_enable_bootdevs() - Enable / disable the SPI flash bootdevs + * + * @enable: true to bind the SPI flash bootdevs, false to skip + */ +void sandbox_sf_set_enable_bootdevs(bool enable); + #endif 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); diff --git a/boot/bootflow.c b/boot/bootflow.c index 163cd4953dd..dc3f1f0c731 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -88,11 +88,13 @@ void bootflow_iter_init(struct bootflow_iter *iter, int flags) memset(iter, '\0', sizeof(*iter)); iter->first_glob_method = -1; iter->flags = flags; + + /* remember the first bootdevs we see */ + iter->max_devs = BOOTFLOW_MAX_USED_DEVS; } void bootflow_iter_uninit(struct bootflow_iter *iter) { - free(iter->dev_order); free(iter->method_order); } @@ -113,22 +115,41 @@ int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter, return 0; } +/** + * bootflow_iter_set_dev() - switch to the next bootdev when iterating + * + * This sets iter->dev, records the device in the dev_used[] list and shows a + * message if required + * + * @iter: Iterator to update + * @dev: Bootdev to use, or NULL if there are no more + */ static void bootflow_iter_set_dev(struct bootflow_iter *iter, - struct udevice *dev) + struct udevice *dev, int method_flags) { struct bootmeth_uc_plat *ucp = dev_get_uclass_plat(iter->method); + log_debug("iter: Setting dev to %s, flags %x\n", + dev ? dev->name : "(none)", method_flags); iter->dev = dev; - if ((iter->flags & (BOOTFLOWF_SHOW | BOOTFLOWF_SINGLE_DEV)) == - BOOTFLOWF_SHOW) { - if (dev) - printf("Scanning bootdev '%s':\n", dev->name); - else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && - ucp->flags & BOOTMETHF_GLOBAL) - printf("Scanning global bootmeth '%s':\n", - iter->method->name); - else - printf("No more bootdevs\n"); + iter->method_flags = method_flags; + + if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) { + /* record the device for later */ + if (dev && iter->num_devs < iter->max_devs) + iter->dev_used[iter->num_devs++] = dev; + + if ((iter->flags & (BOOTFLOWF_SHOW | BOOTFLOWF_SINGLE_DEV)) == + BOOTFLOWF_SHOW) { + if (dev) + printf("Scanning bootdev '%s':\n", dev->name); + else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && + ucp->flags & BOOTMETHF_GLOBAL) + printf("Scanning global bootmeth '%s':\n", + iter->method->name); + else + printf("No more bootdevs\n"); + } } } @@ -144,6 +165,7 @@ static int iter_incr(struct bootflow_iter *iter) bool global; int ret; + log_debug("entry: err=%d\n", iter->err); global = iter->doing_global; if (iter->err == BF_NO_MORE_DEVICES) @@ -182,7 +204,7 @@ static int iter_incr(struct bootflow_iter *iter) return 0; } - /* No more partitions; start at the first one and...*/ + /* No more partitions; start at the first one and... */ iter->part = 0; /* @@ -196,16 +218,62 @@ static int iter_incr(struct bootflow_iter *iter) if (iter->flags & BOOTFLOWF_SINGLE_DEV) { ret = -ENOENT; } else { - if (inc_dev) - iter->cur_dev++; - if (iter->cur_dev == iter->num_devs) { - ret = -ENOENT; - bootflow_iter_set_dev(iter, NULL); + int method_flags; + + ret = 0; + dev = iter->dev; + log_debug("inc_dev=%d\n", inc_dev); + if (!inc_dev) { + 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) { + ret = bootdev_next_label(iter, &dev, + &method_flags); + } else { + ret = bootdev_next_prio(iter, &dev); + method_flags = 0; + } + } + log_debug("ret=%d, dev=%p %s\n", ret, dev, + dev ? dev->name : "none"); + if (ret) { + bootflow_iter_set_dev(iter, NULL, 0); } else { - dev = iter->dev_order[iter->cur_dev]; 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); + bootflow_iter_set_dev(iter, dev, method_flags); } } @@ -230,7 +298,7 @@ static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow) int ret; if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) { - bootflow_iter_set_dev(iter, NULL); + bootflow_iter_set_dev(iter, NULL, 0); ret = bootmeth_get_bootflow(iter->method, bflow); if (ret) return log_msg_ret("glob", ret); @@ -265,30 +333,41 @@ 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_first(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); - ret = bootdev_setup_iter_order(iter, &dev); - if (ret) - return log_msg_ret("obdev", -ENODEV); - + /* + * Set up the ordering of bootmeths. This sets iter->doing_global and + * iter->first_glob_method if we are starting with the global bootmeths + */ ret = bootmeth_setup_iter_order(iter, !(flags & BOOTFLOWF_SKIP_GLOBAL)); if (ret) return log_msg_ret("obmeth", -ENODEV); /* Find the first bootmeth (there must be at least one!) */ iter->method = iter->method_order[iter->cur_method]; - if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) || !iter->doing_global) - bootflow_iter_set_dev(iter, dev); + + if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) || !iter->doing_global) { + struct udevice *dev = NULL; + int method_flags; + + ret = bootdev_setup_iter(iter, label, &dev, &method_flags); + if (ret) + return log_msg_ret("obdev", -ENODEV); + + bootflow_iter_set_dev(iter, dev, method_flags); + } ret = bootflow_check(iter, bflow); if (ret) { + log_debug("check - ret=%d\n", ret); if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) { if (iter->flags & BOOTFLOWF_ALL) return log_msg_ret("all", ret); @@ -302,29 +381,19 @@ int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter, return 0; } -int bootflow_scan_first(struct bootflow_iter *iter, int flags, - struct bootflow *bflow) -{ - int ret; - - ret = bootflow_scan_bootdev(NULL, iter, flags, bflow); - if (ret) - return log_msg_ret("start", ret); - - return 0; -} - int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow) { int ret; do { ret = iter_incr(iter); + log_debug("iter_incr: ret=%d\n", ret); if (ret == BF_NO_MORE_DEVICES) return log_msg_ret("done", ret); if (!ret) { ret = bootflow_check(iter, bflow); + log_debug("check - ret=%d\n", ret); if (!ret) return 0; iter->err = ret; @@ -333,6 +402,7 @@ int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow) return log_msg_ret("all", ret); } } else { + log_debug("incr failed, err=%d\n", ret); iter->err = ret; } @@ -355,6 +425,7 @@ void bootflow_free(struct bootflow *bflow) free(bflow->fname); free(bflow->buf); free(bflow->os_name); + free(bflow->fdt_fname); } void bootflow_remove(struct bootflow *bflow) @@ -427,7 +498,7 @@ int bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow) return ret; } -int bootflow_iter_uses_blk_dev(const struct bootflow_iter *iter) +int bootflow_iter_check_blk(const struct bootflow_iter *iter) { const struct udevice *media = dev_get_parent(iter->dev); enum uclass_id id = device_get_uclass_id(media); @@ -439,7 +510,19 @@ int bootflow_iter_uses_blk_dev(const struct bootflow_iter *iter) return -ENOTSUPP; } -int bootflow_iter_uses_network(const struct bootflow_iter *iter) +int bootflow_iter_check_sf(const struct bootflow_iter *iter) +{ + const struct udevice *media = dev_get_parent(iter->dev); + enum uclass_id id = device_get_uclass_id(media); + + log_debug("uclass %d: %s\n", id, uclass_get_name(id)); + if (id == UCLASS_SPI_FLASH) + return 0; + + return -ENOTSUPP; +} + +int bootflow_iter_check_net(const struct bootflow_iter *iter) { const struct udevice *media = dev_get_parent(iter->dev); enum uclass_id id = device_get_uclass_id(media); @@ -451,7 +534,7 @@ int bootflow_iter_uses_network(const struct bootflow_iter *iter) return -ENOTSUPP; } -int bootflow_iter_uses_system(const struct bootflow_iter *iter) +int bootflow_iter_check_system(const struct bootflow_iter *iter) { const struct udevice *media = dev_get_parent(iter->dev); enum uclass_id id = device_get_uclass_id(media); diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 4c3529d1555..2aee1e0f0c5 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -50,6 +50,17 @@ int bootmeth_read_bootflow(struct udevice *dev, struct bootflow *bflow) return ops->read_bootflow(dev, bflow); } +int bootmeth_set_bootflow(struct udevice *dev, struct bootflow *bflow, + char *buf, int size) +{ + const struct bootmeth_ops *ops = bootmeth_get_ops(dev); + + if (!ops->set_bootflow) + return -ENOSYS; + + return ops->set_bootflow(dev, bflow, buf, size); +} + int bootmeth_boot(struct udevice *dev, struct bootflow *bflow) { const struct bootmeth_ops *ops = bootmeth_get_ops(dev); diff --git a/boot/bootmeth_distro.c b/boot/bootmeth_distro.c index 6ef0fa1f2c9..356929828b9 100644 --- a/boot/bootmeth_distro.c +++ b/boot/bootmeth_distro.c @@ -59,7 +59,7 @@ static int distro_check(struct udevice *dev, struct bootflow_iter *iter) int ret; /* This only works on block devices */ - ret = bootflow_iter_uses_blk_dev(iter); + ret = bootflow_iter_check_blk(iter); if (ret) return log_msg_ret("blk", ret); diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c index d5438eb67b9..67c972e3fe4 100644 --- a/boot/bootmeth_efi.c +++ b/boot/bootmeth_efi.c @@ -19,6 +19,7 @@ #include <malloc.h> #include <mapmem.h> #include <mmc.h> +#include <net.h> #include <pxe_utils.h> #define EFI_DIRNAME "efi/boot/" @@ -59,6 +60,40 @@ static int get_efi_leafname(char *str, int max_len) return 0; } +static int get_efi_pxe_arch(void) +{ + /* http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml */ + if (IS_ENABLED(CONFIG_ARM64)) + return 0xb; + else if (IS_ENABLED(CONFIG_ARM)) + return 0xa; + else if (IS_ENABLED(CONFIG_X86_64)) + return 0x6; + else if (IS_ENABLED(CONFIG_X86)) + return 0x7; + else if (IS_ENABLED(CONFIG_ARCH_RV32I)) + return 0x19; + else if (IS_ENABLED(CONFIG_ARCH_RV64I)) + return 0x1b; + else if (IS_ENABLED(CONFIG_SANDBOX)) + return 0; /* not used */ + + return -EINVAL; +} + +static int get_efi_pxe_vci(char *str, int max_len) +{ + int ret; + + ret = get_efi_pxe_arch(); + if (ret < 0) + return ret; + + snprintf(str, max_len, "PXEClient:Arch:%05x:UNDI:003000", ret); + + return 0; +} + static int efiload_read_file(struct blk_desc *desc, struct bootflow *bflow) { const struct udevice *media_dev; @@ -101,20 +136,44 @@ static int efiload_read_file(struct blk_desc *desc, struct bootflow *bflow) static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter) { - int ret; + /* This only works on block and network devices */ + if (bootflow_iter_check_blk(iter) && bootflow_iter_check_net(iter)) + return log_msg_ret("blk", -ENOTSUPP); - /* This only works on block devices */ - ret = bootflow_iter_uses_blk_dev(iter); - if (ret) - return log_msg_ret("blk", ret); + /* This works on block devices and network devices */ + if (iter->method_flags & BOOTFLOW_METHF_PXE_ONLY) + return log_msg_ret("pxe", -ENOTSUPP); return 0; } -static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow) +static void distro_efi_get_fdt_name(char *fname, int size) +{ + const char *fdt_fname; + + fdt_fname = env_get("fdtfile"); + if (fdt_fname) { + snprintf(fname, size, "dtb/%s", fdt_fname); + log_debug("Using device tree: %s\n", fname); + } else { + const char *soc = env_get("soc"); + const char *board = env_get("board"); + const char *boardver = env_get("boardver"); + + /* cf the code in label_boot() which seems very complex */ + snprintf(fname, size, "dtb/%s%s%s%s.dtb", + soc ? soc : "", soc ? "-" : "", board ? board : "", + boardver ? boardver : ""); + log_debug("Using default device tree: %s\n", fname); + } +} + +static int distro_efi_read_bootflow_file(struct udevice *dev, + struct bootflow *bflow) { struct blk_desc *desc = NULL; - char fname[sizeof(EFI_DIRNAME) + 16]; + ulong fdt_addr, size; + char fname[256]; int ret; /* We require a partition table */ @@ -137,20 +196,159 @@ static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow) if (ret) return log_msg_ret("read", -EINVAL); + distro_efi_get_fdt_name(fname, sizeof(fname)); + bflow->fdt_fname = strdup(fname); + if (!bflow->fdt_fname) + return log_msg_ret("fil", -ENOMEM); + + fdt_addr = env_get_hex("fdt_addr_r", 0); + ret = bootmeth_common_read_file(dev, bflow, fname, fdt_addr, &size); + if (!ret) { + bflow->fdt_size = size; + bflow->fdt_addr = fdt_addr; + + /* + * TODO: Apply extension overlay + * + * Here we need to load and apply the extension overlay. This is + * not implemented. See do_extension_apply(). The extension + * stuff needs an implementation in boot/extension.c so it is + * separate from the command code. Really the extension stuff + * should use the device tree and a uclass / driver interface + * rather than implementing its own list + */ + } else { + log_debug("No device tree available\n"); + } + + return 0; +} + +static int distro_efi_read_bootflow_net(struct bootflow *bflow) +{ + char file_addr[17], fname[256]; + char *tftp_argv[] = {"tftp", file_addr, fname, NULL}; + struct cmd_tbl cmdtp = {}; /* dummy */ + const char *addr_str, *fdt_addr_str; + int ret, arch, size; + ulong addr, fdt_addr; + char str[36]; + + ret = get_efi_pxe_vci(str, sizeof(str)); + if (ret) + return log_msg_ret("vci", ret); + ret = get_efi_pxe_arch(); + if (ret < 0) + return log_msg_ret("arc", ret); + arch = ret; + + ret = env_set("bootp_vci", str); + if (ret) + return log_msg_ret("vcs", ret); + ret = env_set_ulong("bootp_arch", arch); + if (ret) + return log_msg_ret("ars", ret); + + /* figure out the load address */ + addr_str = env_get("kernel_addr_r"); + addr = addr_str ? hextoul(addr_str, NULL) : image_load_addr; + + /* clear any previous bootfile */ + env_set("bootfile", NULL); + + /* read the kernel */ + ret = dhcp_run(addr, NULL, true); + if (ret) + return log_msg_ret("dhc", ret); + + size = env_get_hex("filesize", -1); + if (size <= 0) + return log_msg_ret("sz", -EINVAL); + bflow->size = size; + + /* do the hideous EFI hack */ + efi_set_bootdev("Net", "", bflow->fname, map_sysmem(addr, 0), + bflow->size); + + /* read the DT file also */ + fdt_addr_str = env_get("fdt_addr_r"); + if (!fdt_addr_str) + return log_msg_ret("fdt", -EINVAL); + fdt_addr = hextoul(fdt_addr_str, NULL); + sprintf(file_addr, "%lx", fdt_addr); + + distro_efi_get_fdt_name(fname, sizeof(fname)); + bflow->fdt_fname = strdup(fname); + if (!bflow->fdt_fname) + return log_msg_ret("fil", -ENOMEM); + + if (!do_tftpb(&cmdtp, 0, 3, tftp_argv)) { + bflow->fdt_size = env_get_hex("filesize", 0); + bflow->fdt_addr = fdt_addr; + } else { + log_debug("No device tree available\n"); + } + + bflow->state = BOOTFLOWST_READY; + + return 0; +} + +static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + const struct udevice *media = dev_get_parent(bflow->dev); + int ret; + + if (IS_ENABLED(CONFIG_CMD_DHCP) && + device_get_uclass_id(media) == UCLASS_ETH) { + /* we only support reading from one device, so ignore 'dev' */ + ret = distro_efi_read_bootflow_net(bflow); + if (ret) + return log_msg_ret("net", ret); + } else { + ret = distro_efi_read_bootflow_file(dev, bflow); + if (ret) + return log_msg_ret("blk", ret); + } + return 0; } int distro_efi_boot(struct udevice *dev, struct bootflow *bflow) { + ulong kernel, fdt; char cmd[50]; + /* A non-zero buffer indicates the kernel is there */ + if (bflow->buf) { + kernel = (ulong)map_to_sysmem(bflow->buf); + + /* + * use the provided device tree if available, else fall back to + * the control FDT + */ + if (bflow->fdt_fname) + fdt = bflow->fdt_addr; + else + fdt = (ulong)map_to_sysmem(gd->fdt_blob); + } else { + /* + * This doesn't actually work for network devices: + * + * do_bootefi_image() No UEFI binary known at 0x02080000 + * + * But this is the same behaviour for distro boot, so it can be + * fixed here. + */ + kernel = env_get_hex("kernel_addr_r", 0); + fdt = env_get_hex("fdt_addr_r", 0); + } + /* * At some point we can add a real interface to bootefi so we can call - * this directly. For now, go through the CLI like distro boot. + * this directly. For now, go through the CLI, like distro boot. */ - snprintf(cmd, sizeof(cmd), "bootefi %lx %lx", - (ulong)map_to_sysmem(bflow->buf), - (ulong)map_to_sysmem(gd->fdt_blob)); + snprintf(cmd, sizeof(cmd), "bootefi %lx %lx", kernel, fdt); if (run_command(cmd, 0)) return log_msg_ret("run", -EINVAL); diff --git a/boot/bootmeth_efi_mgr.c b/boot/bootmeth_efi_mgr.c index 2f327c1f8ce..e9d973429f7 100644 --- a/boot/bootmeth_efi_mgr.c +++ b/boot/bootmeth_efi_mgr.c @@ -36,7 +36,7 @@ static int efi_mgr_check(struct udevice *dev, struct bootflow_iter *iter) int ret; /* Must be an bootstd device */ - ret = bootflow_iter_uses_system(iter); + ret = bootflow_iter_check_system(iter); if (ret) return log_msg_ret("net", ret); diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index e6992168c06..ecf8557af83 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -44,10 +44,13 @@ static int distro_pxe_check(struct udevice *dev, struct bootflow_iter *iter) int ret; /* This only works on network devices */ - ret = bootflow_iter_uses_network(iter); + ret = bootflow_iter_check_net(iter); if (ret) return log_msg_ret("net", ret); + if (iter->method_flags & BOOTFLOW_METHF_DHCP_ONLY) + return log_msg_ret("dhcp", -ENOTSUPP); + return 0; } diff --git a/boot/bootmeth_script.c b/boot/bootmeth_script.c index c7061eb998f..225eb18ee6c 100644 --- a/boot/bootmeth_script.c +++ b/boot/bootmeth_script.c @@ -19,18 +19,16 @@ #include <image.h> #include <malloc.h> #include <mapmem.h> +#include <net.h> #define SCRIPT_FNAME1 "boot.scr.uimg" #define SCRIPT_FNAME2 "boot.scr" static int script_check(struct udevice *dev, struct bootflow_iter *iter) { - int ret; - - /* This only works on block devices */ - ret = bootflow_iter_uses_blk_dev(iter); - if (ret) - return log_msg_ret("blk", ret); + /* This works on block devices, network devices and SPI Flash */ + if (iter->method_flags & BOOTFLOW_METHF_PXE_ONLY) + return log_msg_ret("pxe", -ENOTSUPP); return 0; } @@ -65,11 +63,11 @@ static int script_fill_info(struct bootflow *bflow) return 0; } -static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow) +static int script_read_bootflow_file(struct udevice *bootstd, + struct bootflow *bflow) { struct blk_desc *desc = NULL; const char *const *prefixes; - struct udevice *bootstd; const char *prefix; int ret, i; @@ -77,12 +75,12 @@ static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow) if (ret) return log_msg_ret("std", ret); - /* We require a partition table */ - if (!bflow->part) - return -ENOENT; - - if (bflow->blk) + if (bflow->blk) { + /* We require a partition table */ + if (!bflow->part) + return -ENOENT; desc = dev_get_uclass_plat(bflow->blk); + } prefixes = bootstd_get_prefixes(bootstd); i = 0; @@ -116,6 +114,76 @@ static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow) return 0; } +static int script_read_bootflow_net(struct bootflow *bflow) +{ + const char *addr_str; + int size, ret; + char *fname; + ulong addr; + + /* figure out the load address */ + addr_str = env_get("scriptaddr"); + addr = addr_str ? hextoul(addr_str, NULL) : image_load_addr; + + fname = env_get("boot_script_dhcp"); + if (!fname) + return log_msg_ret("dhc", -EINVAL); + + ret = dhcp_run(addr, fname, true); + if (ret) + return log_msg_ret("dhc", ret); + + size = env_get_hex("filesize", 0); + if (!size) + return log_msg_ret("sz", -EINVAL); + + bflow->buf = malloc(size + 1); + if (!bflow->buf) + return log_msg_ret("buf", -ENOMEM); + memcpy(bflow->buf, map_sysmem(addr, size), size); + bflow->buf[size] = '\0'; + bflow->size = size; + bflow->state = BOOTFLOWST_READY; + + return 0; +} + +static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + const struct udevice *media = dev_get_parent(bflow->dev); + struct udevice *bootstd; + int ret; + + ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); + if (ret) + return log_msg_ret("std", ret); + + if (IS_ENABLED(CONFIG_CMD_DHCP) && + device_get_uclass_id(media) == UCLASS_ETH) { + /* we only support reading from one device, so ignore 'dev' */ + ret = script_read_bootflow_net(bflow); + if (ret) + return log_msg_ret("net", ret); + } else { + ret = script_read_bootflow_file(bootstd, bflow); + if (ret) + return log_msg_ret("blk", ret); + } + + return 0; +} + +static int script_set_bootflow(struct udevice *dev, struct bootflow *bflow, + char *buf, int size) +{ + buf[size] = '\0'; + bflow->buf = buf; + bflow->size = size; + bflow->state = BOOTFLOWST_READY; + + return 0; +} + static int script_boot(struct udevice *dev, struct bootflow *bflow) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); @@ -126,6 +194,8 @@ static int script_boot(struct udevice *dev, struct bootflow *bflow) if (!ret) ret = env_set_hex("devnum", desc->devnum); if (!ret) + ret = env_set_hex("distro_bootpart", bflow->part); + if (!ret) ret = env_set("prefix", bflow->subdir); if (!ret && IS_ENABLED(CONFIG_ARCH_SUNXI) && !strcmp("mmc", blk_get_devtype(bflow->blk))) @@ -135,6 +205,7 @@ static int script_boot(struct udevice *dev, struct bootflow *bflow) log_debug("devtype: %s\n", env_get("devtype")); log_debug("devnum: %s\n", env_get("devnum")); + log_debug("distro_bootpart: %s\n", env_get("distro_bootpart")); log_debug("prefix: %s\n", env_get("prefix")); log_debug("mmc_bootdev: %s\n", env_get("mmc_bootdev")); @@ -159,6 +230,7 @@ static int script_bootmeth_bind(struct udevice *dev) static struct bootmeth_ops script_bootmeth_ops = { .check = script_check, .read_bootflow = script_read_bootflow, + .set_bootflow = script_set_bootflow, .read_file = bootmeth_common_read_file, .boot = script_boot, }; diff --git a/boot/bootstd-uclass.c b/boot/bootstd-uclass.c index 7887acdc11b..81555d341e3 100644 --- a/boot/bootstd-uclass.c +++ b/boot/bootstd-uclass.c @@ -10,6 +10,7 @@ #include <bootflow.h> #include <bootstd.h> #include <dm.h> +#include <env.h> #include <log.h> #include <malloc.h> #include <dm/device-internal.h> @@ -72,9 +73,23 @@ static int bootstd_remove(struct udevice *dev) return 0; } -const char *const *const bootstd_get_bootdev_order(struct udevice *dev) +const char *const *const bootstd_get_bootdev_order(struct udevice *dev, + bool *okp) { struct bootstd_priv *std = dev_get_priv(dev); + const char *targets = env_get("boot_targets"); + + *okp = true; + log_debug("- targets %s %p\n", targets, std->bootdev_order); + if (targets && *targets) { + str_free_list(std->env_order); + std->env_order = str_to_list(targets); + if (!std->env_order) { + *okp = false; + return NULL; + } + return std->env_order; + } return std->bootdev_order; } diff --git a/boot/vbe_simple_fw.c b/boot/vbe_simple_fw.c index 0a49d286703..d59a704ddba 100644 --- a/boot/vbe_simple_fw.c +++ b/boot/vbe_simple_fw.c @@ -176,7 +176,7 @@ static int simple_load_from_image(struct spl_image_info *spl_image, priv = dev_get_priv(meth); log_debug("simple %s\n", priv->storage); - ret = bootdev_find_by_label(priv->storage, &bdev); + ret = bootdev_find_by_label(priv->storage, &bdev, NULL); if (ret) return log_msg_ret("bd", ret); log_debug("bootdev %s\n", bdev->name); diff --git a/cmd/bootdev.c b/cmd/bootdev.c index ecd797c0503..5b1efaaee87 100644 --- a/cmd/bootdev.c +++ b/cmd/bootdev.c @@ -57,7 +57,7 @@ static int do_bootdev_select(struct cmd_tbl *cmdtp, int flag, int argc, std->cur_bootdev = NULL; return 0; } - if (bootdev_find_by_any(argv[1], &dev)) + if (bootdev_find_by_any(argv[1], &dev, NULL)) return CMD_RET_FAILURE; std->cur_bootdev = dev; @@ -107,14 +107,48 @@ static int do_bootdev_info(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_bootdev_hunt(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct bootstd_priv *priv; + const char *spec = NULL; + bool list = false; + int ret = 0; + + if (argc >= 2) { + if (!strcmp(argv[1], "-l")) + list = true; + else + spec = argv[1]; + } + + ret = bootstd_get_priv(&priv); + if (ret) + return ret; + if (list) { + bootdev_list_hunters(priv); + } else { + ret = bootdev_hunt(spec, true); + if (ret) { + printf("Failed (err=%dE)\n", ret); + + return CMD_RET_FAILURE; + } + } + + return 0; +} + #ifdef CONFIG_SYS_LONGHELP static char bootdev_help_text[] = - "list [-p] - list all available bootdevs (-p to probe)\n" - "bootdev select <bd> - select a bootdev by name | label | seq\n" - "bootdev info [-p] - show information about a bootdev (-p to probe)"; + "list [-p] - list all available bootdevs (-p to probe)\n" + "bootdev hunt [-l|<spec>] - use hunt drivers to find bootdevs\n" + "bootdev select <bd> - select a bootdev by name | label | seq\n" + "bootdev info [-p] - show information about a bootdev (-p to probe)"; #endif U_BOOT_CMD_WITH_SUBCMDS(bootdev, "Boot devices", bootdev_help_text, U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootdev_list), + U_BOOT_SUBCMD_MKENT(hunt, 2, 1, do_bootdev_hunt), U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootdev_select), U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootdev_info)); diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 2b6ed26fdcb..692bc6d117f 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -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; + 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)) { @@ -115,15 +115,14 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc, errors = strchr(argv[1], 'e'); no_global = strchr(argv[1], 'G'); list = strchr(argv[1], 'l'); + no_hunter = strchr(argv[1], 'H'); argc--; argv++; } - if (argc > 1) { - const char *label = argv[1]; - - if (bootdev_find_by_any(label, &dev)) - 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"); @@ -141,58 +140,42 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc, flags |= BOOTFLOWF_ALL; if (no_global) flags |= BOOTFLOWF_SKIP_GLOBAL; + if (!no_hunter) + flags |= BOOTFLOWF_HUNT; /* * 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_first(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) @@ -344,6 +327,12 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, printf("Logo size: %x (%d bytes)\n", bflow->logo_size, bflow->logo_size); } + printf("FDT: %s\n", bflow->fdt_fname); + if (bflow->fdt_fname) { + printf("FDT size: %x (%d bytes)\n", bflow->fdt_size, + bflow->fdt_size); + printf("FDT addr: %lx\n", bflow->fdt_addr); + } printf("Error: %d\n", bflow->err); if (dump && bflow->buf) { /* Set some sort of maximum on the size */ @@ -59,7 +59,11 @@ static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag, static int do_dm_dump_tree(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - dm_dump_tree(); + bool sort; + + sort = argc > 1 && !strcmp(argv[1], "-s"); + + dm_dump_tree(sort); return 0; } @@ -87,7 +91,7 @@ static char dm_help_text[] = "dm drivers Dump list of drivers with uclass and instances\n" DM_MEM_HELP "dm static Dump list of drivers with static platform data\n" - "dm tree Dump tree of driver model devices ('*' = activated)\n" + "dm tree [-s] Dump tree of driver model devices (-s=sort)\n" "dm uclass Dump list of instances for each uclass" ; #endif @@ -98,5 +102,5 @@ U_BOOT_CMD_WITH_SUBCMDS(dm, "Driver model low level access", dm_help_text, U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_dm_dump_drivers), DM_MEM U_BOOT_SUBCMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info), - U_BOOT_SUBCMD_MKENT(tree, 1, 1, do_dm_dump_tree), + U_BOOT_SUBCMD_MKENT(tree, 2, 1, do_dm_dump_tree), U_BOOT_SUBCMD_MKENT(uclass, 1, 1, do_dm_dump_uclass)); diff --git a/cmd/extension_board.c b/cmd/extension_board.c index f94abd612d5..2b672d888c6 100644 --- a/cmd/extension_board.c +++ b/cmd/extension_board.c @@ -5,7 +5,9 @@ */ #include <common.h> +#include <bootdev.h> #include <command.h> +#include <dm.h> #include <malloc.h> #include <extension_board.h> #include <mapmem.h> @@ -80,8 +82,7 @@ static int do_extension_list(struct cmd_tbl *cmdtp, int flag, return CMD_RET_SUCCESS; } -static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, - int argc, char *const argv[]) +static int extension_scan(bool show) { struct extension *extension, *next; int extension_num; @@ -91,12 +92,23 @@ static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, free(extension); } extension_num = extension_board_scan(&extension_list); + if (show && extension_num >= 0) + printf("Found %d extension board(s).\n", extension_num); + + /* either the number of extensions, or -ve for error */ + return extension_num; +} + + +static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int extension_num; + extension_num = extension_scan(true); if (extension_num < 0) return CMD_RET_FAILURE; - printf("Found %d extension board(s).\n", extension_num); - return CMD_RET_SUCCESS; } @@ -166,3 +178,26 @@ U_BOOT_CMD(extension, 3, 1, do_extensionops, "extension list - lists available extension(s) board(s)\n" "extension apply <extension number|all> - applies DT overlays corresponding to extension boards\n" ); + +static int extension_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + int ret; + + ret = env_set_hex("extension_overlay_addr", + env_get_hex("fdtoverlay_addr_r", 0)); + if (ret) + return log_msg_ret("env", ret); + + ret = extension_scan(show); + if (ret < 0) + return log_msg_ret("ext", ret); + + return 0; +} + +/* extensions should have a uclass - for now we use UCLASS_SIMPLE_BUS uclass */ +BOOTDEV_HUNTER(extension_bootdev_hunter) = { + .prio = BOOTDEVP_1_PRE_SCAN, + .uclass = UCLASS_SIMPLE_BUS, + .hunt = extension_bootdev_hunt, +}; diff --git a/cmd/net.c b/cmd/net.c index dd50930a362..4227321871c 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -4,6 +4,8 @@ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. */ +#define LOG_CATEGORY UCLASS_ETH + /* * Boot support */ @@ -13,6 +15,7 @@ #include <dm.h> #include <env.h> #include <image.h> +#include <log.h> #include <net.h> #include <net6.h> #include <net/udp.h> @@ -120,6 +123,38 @@ U_BOOT_CMD( "boot image via network using DHCP/TFTP protocol", "[loadAddress] [[hostIPaddr:]bootfilename]" ); + +int dhcp_run(ulong addr, const char *fname, bool autoload) +{ + char *dhcp_argv[] = {"dhcp", NULL, (char *)fname, NULL}; + struct cmd_tbl cmdtp = {}; /* dummy */ + char file_addr[17]; + int old_autoload; + int ret, result; + + log_debug("addr=%lx, fname=%s, autoload=%d\n", addr, fname, autoload); + old_autoload = env_get_yesno("autoload"); + ret = env_set("autoload", autoload ? "y" : "n"); + if (ret) + return log_msg_ret("en1", -EINVAL); + + if (autoload) { + sprintf(file_addr, "%lx", addr); + dhcp_argv[1] = file_addr; + } + + result = do_dhcp(&cmdtp, 0, !autoload ? 1 : fname ? 3 : 2, dhcp_argv); + + ret = env_set("autoload", old_autoload == -1 ? NULL : + old_autoload ? "y" : "n"); + if (ret) + return log_msg_ret("en2", -EINVAL); + + if (result) + return log_msg_ret("res", -ENOENT); + + return 0; +} #endif #if defined(CONFIG_CMD_NFS) diff --git a/cmd/vbe.c b/cmd/vbe.c index befaf07c64d..600690394eb 100644 --- a/cmd/vbe.c +++ b/cmd/vbe.c @@ -79,10 +79,13 @@ static int do_vbe_info(struct cmd_tbl *cmdtp, int flag, int argc, static int do_vbe_state(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - struct vbe_handoff *handoff; + struct vbe_handoff *handoff = NULL; int i; - handoff = bloblist_find(BLOBLISTT_VBE, sizeof(struct vbe_handoff)); + if (IS_ENABLED(CONFIG_BLOBLIST)) { + handoff = bloblist_find(BLOBLISTT_VBE, + sizeof(struct vbe_handoff)); + } if (!handoff) { printf("No VBE state\n"); return CMD_RET_FAILURE; diff --git a/common/event.c b/common/event.c index 231b9e6ffdd..c312556490e 100644 --- a/common/event.c +++ b/common/event.c @@ -123,7 +123,7 @@ int event_notify(enum event_t type, void *data, int size) ret = notify_static(&event); if (ret) - return log_msg_ret("dyn", ret); + return log_msg_ret("sta", ret); if (CONFIG_IS_ENABLED(EVENT_DYNAMIC)) { ret = notify_dynamic(&event); diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 6c5bacea323..34c342b6f58 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -132,7 +132,6 @@ CONFIG_ENV_IS_IN_EXT4=y CONFIG_ENV_EXT4_INTERFACE="host" CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0" CONFIG_ENV_IMPORT_FDT=y -# CONFIG_BOOTDEV_ETH is not set CONFIG_BOOTP_SEND_HOSTNAME=y CONFIG_NETCONSOLE=y CONFIG_IP_DEFRAG=y @@ -212,6 +211,7 @@ CONFIG_MMC_SANDBOX=y CONFIG_MMC_SDHCI=y CONFIG_MTD=y CONFIG_SPI_FLASH_SANDBOX=y +CONFIG_BOOTDEV_SPI_FLASH=y CONFIG_SPI_FLASH_ATMEL=y CONFIG_SPI_FLASH_EON=y CONFIG_SPI_FLASH_GIGADEVICE=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index f30e806ae29..477763dc9f1 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -40,6 +40,7 @@ CONFIG_CMD_MEMTEST=y CONFIG_CMD_DEMO=y CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y +CONFIG_CMD_IDE=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_OSD=y @@ -75,7 +76,6 @@ CONFIG_ENV_IS_NOWHERE=y CONFIG_ENV_IS_IN_EXT4=y CONFIG_ENV_EXT4_INTERFACE="host" CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0" -# CONFIG_BOOTDEV_ETH is not set CONFIG_BOOTP_SEND_HOSTNAME=y CONFIG_NETCONSOLE=y CONFIG_IP_DEFRAG=y @@ -91,6 +91,13 @@ CONFIG_ADC=y CONFIG_ADC_SANDBOX=y CONFIG_AXI=y CONFIG_AXI_SANDBOX=y +CONFIG_SYS_IDE_MAXBUS=1 +CONFIG_SYS_ATA_BASE_ADDR=0x100 +CONFIG_SYS_ATA_STRIDE=4 +CONFIG_SYS_ATA_DATA_OFFSET=0 +CONFIG_SYS_ATA_REG_OFFSET=1 +CONFIG_SYS_ATA_ALT_OFFSET=2 +CONFIG_SYS_ATA_IDE0_OFFSET=0 CONFIG_CLK=y CONFIG_CLK_COMPOSITE_CCF=y CONFIG_CLK_K210=y @@ -131,6 +138,7 @@ CONFIG_PWRSEQ=y CONFIG_I2C_EEPROM=y CONFIG_MMC_SANDBOX=y CONFIG_SPI_FLASH_SANDBOX=y +CONFIG_BOOTDEV_SPI_FLASH=y CONFIG_SPI_FLASH_ATMEL=y CONFIG_SPI_FLASH_EON=y CONFIG_SPI_FLASH_GIGADEVICE=y diff --git a/disk/part.c b/disk/part.c index 5ee60a7fb59..d449635254e 100644 --- a/disk/part.c +++ b/disk/part.c @@ -770,3 +770,19 @@ void part_set_generic_name(const struct blk_desc *dev_desc, sprintf(name, "%s%c%d", devtype, 'a' + dev_desc->devnum, part_num); } + +int part_get_bootable(struct blk_desc *desc) +{ + struct disk_partition info; + int p; + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + int ret; + + ret = part_get_info(desc, p, &info); + if (!ret && info.bootable) + return p; + } + + return 0; +} diff --git a/doc/develop/bootstd.rst b/doc/develop/bootstd.rst index 1ccc49424eb..281aabf74b2 100644 --- a/doc/develop/bootstd.rst +++ b/doc/develop/bootstd.rst @@ -71,8 +71,14 @@ MMC, NVMe). The bootdev accesses the device, including partitions and filesystems that might contain things related to an operating system. For example, an MMC bootdev provides access to the individual partitions on the -MMC device. It scans through these to find filesystems, then provides a list of -these for consideration. +MMC device. It scans through these to find filesystems with the boot flag set, +then provides a list of these for consideration. + +Some bootdevs are not visible until a bus is enumerated, e.g. flash sticks +attached via USB. To deal with this, each bootdev has an associated 'hunter' +which can hunt for bootdevs of a particular uclass type. For example, the SCSI +bootdev scans the SCSI bus looking for devices, creating a bootdev for each +Logical Unit Number (LUN) that it finds. Bootmeth @@ -175,7 +181,10 @@ reading from, but each is responsible for returning a valid bootflow if available. A helper called `bootdev_find_in_blk()` makes it fairly easy to implement this -function for each media device uclass, in a few lines of code. +function for each media device uclass, in a few lines of code. For many types +ot bootdevs, the `get_bootflow` member can be NULL, indicating that the default +handler is used. This is called `default_get_bootflow()` and it only works with +block devices. Bootdev drivers @@ -183,41 +192,16 @@ Bootdev drivers A bootdev driver is typically fairly simple. Here is one for mmc:: - static int mmc_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, - struct bootflow *bflow) - { - struct udevice *mmc_dev = dev_get_parent(dev); - struct udevice *blk; - int ret; - - ret = mmc_get_blk(mmc_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; - } - static int mmc_bootdev_bind(struct udevice *dev) { struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); - ucp->prio = BOOTDEVP_0_INTERNAL_FAST; + ucp->prio = BOOTDEVP_2_INTERNAL_FAST; return 0; } struct bootdev_ops mmc_bootdev_ops = { - .get_bootflow = mmc_get_bootflow, }; static const struct udevice_id mmc_bootdev_ids[] = { @@ -233,15 +217,77 @@ A bootdev driver is typically fairly simple. Here is one for mmc:: .of_match = mmc_bootdev_ids, }; -The implementation of the `get_bootflow()` method is simply to obtain the -block device and call a bootdev helper function to do the rest. The +You may notice that the `get_bootflow` memory is not provided, so is NULL. This +means that `default_get_bootflow()` is used. This simply obtains the +block device and calls a bootdev helper function to do the rest. The implementation of `bootdev_find_in_blk()` checks the partition table, and attempts to read a file from a filesystem on the partition number given by the -`@iter->part` parameter. +`@iter->part` parameter. If there are any bootable partitions in the table, +then only bootable partitions are considered. + +Each bootdev has a priority, which indicates the order in which it is used, +if `boot_targets` is not used. Faster bootdevs are used first, since they are +more likely to be able to boot the device quickly. + + +Environment Variables +--------------------- + +Various environment variables are used by standard boot. These allow the board +to control where things are placed when booting the OS. You should ensure that +your boards sets values for these. + +fdtfile + Name of the flattened device tree (FDT) file to load, e.g. + "rockchip/rk3399-rockpro64.dtb" + +fdtaddr_addr_r + Address at which to load the FDT, e.g. 0x01f00000 + +fdtoverlay_addr_r (needed if overlays are used) + Address at which to load the overlay for the FDT, e.g. 0x02000000 + +kernel_addr_r + Address at which to load the kernel, e.g. 0x02080000 + +kernel_comp_addr_r + Address to which to decompress the kernel, e.g. 0x08000000 + +kernel_comp_size + Size of available space for decompressed kernel, e.g. 0x2000000 + +pxefile_addr_r + Address at which to load the PXE file, e.g. 0x00600000 + +ramdisk_addr_r + Address at which to load the ramdisk, e.g. 0x06000000 + +scriptaddr + Address at which to load the U-Boot script, e.g. 0x00500000 + +script_offset_f + SPI flash offset from which to load the U-Boot script, e.g. 0xffe000 -Each bootdev has a priority, which indicates the order in which it is used. -Faster bootdevs are used first, since they are more likely to be able to boot -the device quickly. +script_size_f + Size of the script to load, e.g. 0x2000 + +Some variables are set by script bootmeth: + +devtype + Device type being used for boot, e.g. mmc + +devnum + Device number being used for boot, e.g. 1 + +distro_bootpart + Partition being used for boot, e.g. 2 + +prefix + Directory containing the script + +mmc_bootdev + Device number being used for boot (e.g. 1). This is only used by MMC on + sunxi boards. Device hierarchy @@ -259,13 +305,22 @@ media device:: bootdev 1 [ ] mmc_bootdev | | `-- sdhci@7e300000.bootdev The bootdev device is typically created automatically in the media uclass' -`post_bind()` method by calling `bootdev_setup_for_dev()`. The code typically -something like this:: +`post_bind()` method by calling `bootdev_setup_for_dev()` or +`bootdev_setup_sibling_blk()`. The code typically something like this:: + /* dev is the Ethernet device */ ret = bootdev_setup_for_dev(dev, "eth_bootdev"); if (ret) return log_msg_ret("bootdev", ret); +or:: + + /* blk is the block device (child of MMC device) + ret = bootdev_setup_sibling_blk(blk, "mmc_bootdev"); + if (ret) + return log_msg_ret("bootdev", ret); + + Here, `eth_bootdev` is the name of the Ethernet bootdev driver and `dev` is the ethernet device. This function is safe to call even if standard boot is not enabled, since it does nothing in that case. It can be added to all uclasses @@ -337,6 +392,10 @@ Standard boot is enabled with `CONFIG_BOOTSTD`. Each bootmeth has its own CONFIG option also. For example, `CONFIG_BOOTMETH_DISTRO` enables support for distro boot from a disk. +To enable all feature sof standard boot, use `CONFIG_BOOTSTD_FULL`. This +includes the full set of commands, more error messages when things go wrong and +bootmeth ordering with the bootmeths environment variable. + Available bootmeth drivers -------------------------- @@ -345,7 +404,8 @@ Bootmeth drivers are provided for: - distro boot from a disk (syslinux) - distro boot from a network (PXE) - - EFI boot using bootefi + - U-Boot scripts from disk, network or SPI flash + - EFI boot using bootefi from disk - VBE - EFI boot using boot manager @@ -412,26 +472,49 @@ those bootdevs. So, all up, we need a single bootstd device, one or more bootdev devices and one or more bootmeth devices. Once these are ready, typically a `bootflow scan` command is issued. This kicks -of the iteration process, which involves looking through the bootdevs and their -partitions one by one to find bootflows. +of the iteration process, which involves hunting for bootdevs and looking +through the bootdevs and their partitions one by one to find bootflows. -Iteration is kicked off using `bootflow_scan_first()`, which calls -`bootflow_scan_bootdev()`. +Iteration is kicked off using `bootflow_scan_first()`. The iterator is set up with `bootflow_iter_init()`. This simply creates an empty one with the given flags. Flags are used to control whether each iteration is displayed, whether to return iterations even if they did not result in a valid bootflow, whether to iterate through just a single bootdev, etc. -Then the ordering of bootdevs is determined, by `bootdev_setup_iter_order()`. By -default, the bootdevs are used in the order specified by the `boot_targets` -environment variable (e.g. "mmc2 mmc0 usb"). If that is missing then their -sequence order is used, as determined by the `/aliases` node, or failing that -their order in the devicetree. For BOOTSTD_FULL, if there is a `bootdev-order` -property in the bootstd node, then this is used as a final fallback. In any -case, the iterator ends up with a `dev_order` array containing the bootdevs that -are going to be used, with `num_devs` set to the number of bootdevs and -`cur_dev` starting at 0. +Then the iterator is set up to according to the parameters given: + +- When `dev` is provided, then a single bootdev is scanned. In this case, + `BOOTFLOWF_SKIP_GLOBAL` and `BOOTFLOWF_SINGLE_DEV` are set. No hunters are + used in this case + +- Otherwise, when `label` is provided, then a single label or named bootdev is + scanned. In this case `BOOTFLOWF_SKIP_GLOBAL` is set and there are three + options (with an effect on the `iter_incr()` function described later): + + - If `label` indicates a numeric bootdev number (e.g. "2") then + `BOOTFLOW_METHF_SINGLE_DEV` is set. In this case, moving to the next bootdev + simple stops, since there is only one. No hunters are used. + - If `label` indicates a particular media device (e.g. "mmc1") then + `BOOTFLOWF_SINGLE_MEDIA` is set. In this case, moving to the next bootdev + processes just the children of the media device. Hunters are used, in this + example just the "mmc" hunter. + - If `label` indicates a media uclass (e.g. "mmc") then + `BOOTFLOWF_SINGLE_UCLASS` is set. In this case, all bootdevs in that uclass + are used. Hunters are used, in this example just the "mmc" hunter + +- Otherwise, none of the above flags is set and iteration is set up to work + through `boot_targets` environment variable (or `bootdev-order` device tree + property) in order, running the relevant hunter first. In this case + `cur_label` is used to indicate the label being processed. If there is no list + of labels, then all bootdevs are processed in order of priority, running the + hunters as it goes. + +With the above it is therefore possible to iterate in a variety of ways. + +No attempt is made to determine the ordering of bootdevs, since this cannot be +known in advance if we are using the hunters. Any hunter might discover a new +bootdev and disturb the original ordering. Next, the ordering of bootmeths is determined, by `bootmeth_setup_iter_order()`. By default the ordering is again by sequence number, i.e. the `/aliases` node, @@ -446,21 +529,21 @@ 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. -At this point the iterator is ready to use, with the first bootdev and bootmeth -selected. Most of the other fields are 0. This means that the current partition +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 is 0, which is taken to mean the whole device, since partition numbers start at 1. It also means that `max_part` is 0, i.e. the maximum partition number we know about is 0, meaning that, as far as we know, there is no partition table on this bootdev. -With the iterator ready, `bootflow_scan_bootdev()` checks whether the current +With the iterator ready, `bootflow_scan_first()` checks whether the current settings produce a valid bootflow. This is handled by `bootflow_check()`, which either returns 0 (if it got something) or an error if not (more on that later). If the `BOOTFLOWF_ALL` iterator flag is set, even errors are returned as incomplete bootflows, but normally an error results in moving onto the next iteration. -Note that `bootflow_check()` handles global bootmeths explicitly, but calling +Note that `bootflow_check()` handles global bootmeths explicitly, by calling `bootmeth_get_bootflow()` on each one. The `doing_global` flag indicates when the iterator is in that state. @@ -468,7 +551,7 @@ The `bootflow_scan_next()` function handles moving onto the next iteration and checking it. In fact it sits in a loop doing that repeatedly until it finds something it wants to return. -The actual 'moving on' part is implemented in `iter_incr()`. This is a very +The actual 'moving on' part is implemented in `iter_incr()`. This is a fairly simple function. It increments the first counter. If that hits its maximum, it sets it to zero and increments the second counter. You can think of all the counters together as a number with three digits which increment in order, with @@ -522,6 +605,12 @@ like this: The changeover of the value of `doing_global` from true to false is handled in `iter_incr()` as well. +Note that the value in the `bootdev` column above is not actually stored - it is +just for illustration. In practice, `iter_incr()` uses the flags to determine +whether to move to the next bootdev in the uclass, the next child of the media +device, the next label, or the next priority level, depending on the flag +settings (see `BOOTFLOW_METHF_SINGLE_DEV`, etc. above). + There is no expectation that iteration will actually finish. Quite often a valid bootflow is found early on. With `bootflow scan -b`, that causes the bootflow to be immediately booted. Assuming it is successful, the iteration never @@ -567,9 +656,9 @@ global bootmeth). Each one can adopt its own approach. Going down a level, what does the bootdev do in its `get_bootflow()` method? Let us consider the MMC bootdev. In that case the call to -`bootdev_get_bootflow()` ends up in `mmc_get_bootflow()`. It locates the parent -device of the bootdev, i.e. the `UCLASS_MMC` device itself, then finds the block -device associated with it. It then calls the helper function +`bootdev_get_bootflow()` ends up in `default_get_bootflow()`. It locates the +parent device of the bootdev, i.e. the `UCLASS_MMC` device itself, then finds +the block device associated with it. It then calls the helper function `bootdev_find_in_blk()` to do all the work. This is common with just about any bootdev that is based on a media device. @@ -586,7 +675,9 @@ calls the bootmeth device once more, this time to read the bootflow. Note: At present a filesystem is needed for the bootmeth to be called on block devices, simply because we don't have any examples where this is not the case. -This feature can be added as needed. +This feature can be added as needed. Note that sandbox is a special case, since +in that case the host filesystem can be accessed even though the block device +is NULL. If we take the example of the `bootmeth_distro` driver, this call ends up at `distro_read_bootflow()`. It has the filesystem ready, so tries various @@ -594,9 +685,9 @@ filenames to try to find the `extlinux.conf` file, reading it if possible. If all goes well the bootflow ends up in the `BOOTFLOWST_READY` state. At this point, we fall back from the bootmeth driver, to -`bootdev_find_in_blk()`, then back to `mmc_get_bootflow()`, then to +`bootdev_find_in_blk()`, then back to `default_get_bootflow()`, then to `bootdev_get_bootflow()`, then to `bootflow_check()` and finally to its caller, -either `bootflow_scan_bootdev()` or `bootflow_scan_next()`. In either case, +either `bootflow_scan_first()` or `bootflow_scan_next()`. In either case, the bootflow is returned as the result of this iteration, assuming it made it to the `BOOTFLOWST_READY` state. @@ -625,9 +716,9 @@ Tests are located in `test/boot` and cover the core functionality as well as the commands. All tests use sandbox so can be run on a standard Linux computer and in U-Boot's CI. -For testing, a DOS-formatted disk image is used with a single FAT partition on -it. This is created in `setup_bootflow_image()`, with a canned one from the -source tree used if it cannot be created (e.g. in CI). +For testing, a DOS-formatted disk image is used with a FAT partition on it and +a second unused partition. This is created in `setup_bootflow_image()`, with a +canned one from the source tree used if it cannot be created (e.g. in CI). Bootflow internals diff --git a/doc/develop/driver-model/nvme.rst b/doc/develop/driver-model/nvme.rst index fd0c0f00d2d..75518133121 100644 --- a/doc/develop/driver-model/nvme.rst +++ b/doc/develop/driver-model/nvme.rst @@ -27,7 +27,7 @@ How it works ------------ There is an NVMe uclass driver (driver name "nvme"), an NVMe host controller driver (driver name "nvme") and an NVMe namespace block driver (driver name -"nvme-blk"). The host controller driver is supposed to probe the hardware and +"nvme_blk"). The host controller driver is supposed to probe the hardware and do necessary initialization to put the controller into a ready state at which it is able to scan all available namespaces attached to it. Scanning namespace is triggered by the NVMe uclass driver and the actual work is done in the NVMe diff --git a/doc/usage/cmd/bootdev.rst b/doc/usage/cmd/bootdev.rst index 5e02e32c514..6c68d0bf840 100644 --- a/doc/usage/cmd/bootdev.rst +++ b/doc/usage/cmd/bootdev.rst @@ -8,9 +8,10 @@ Synopis :: - bootdev list [-p] - list all available bootdevs (-p to probe)\n" - bootdev select <bm> - select a bootdev by name\n" - bootdev info [-p] - show information about a bootdev"; + bootdev list [-p] - list all available bootdevs (-p to probe) + bootdev hunt [-l|<spec>] - use hunt drivers to find bootdevs + bootdev select <bm> - select a bootdev by name + bootdev info [-p] - show information about a bootdev Description ----------- @@ -63,6 +64,17 @@ Name: with `.bootdev` +bootdev hunt +~~~~~~~~~~~~ + +This hunts for new bootdevs, or shows a list of hunters. + +Use `-l` to list the available bootdev hunters. + +To run hunters, specify the name of the hunter to run, e.g. "mmc". If no +name is provided, all hunters are run. + + bootdev select ~~~~~~~~~~~~~~~~~ @@ -83,7 +95,7 @@ This shows information on the current bootdev, with the format looking like this: ========= ======================= -Name mmc@7e202000.bootdev +Name `mmc@7e202000.bootdev` Sequence 0 Status Probed Uclass mmc @@ -128,6 +140,34 @@ one of them:: Uclass: mmc Bootflows: 1 (1 valid) +This shows using one of the available hunters, then listing them:: + + => bootdev hunt usb + Hunting with: usb + Bus usb@1: scanning bus usb@1 for devices... + 3 USB Device(s) found + => bootdev hunt -l + Prio Used Uclass Hunter + ---- ---- --------------- --------------- + 6 ethernet eth_bootdev + 1 simple_bus (none) + 5 ide ide_bootdev + 2 mmc mmc_bootdev + 4 nvme nvme_bootdev + 4 scsi scsi_bootdev + 4 spi_flash sf_bootdev + 5 * usb usb_bootdev + 4 virtio virtio_bootdev + (total hunters: 9) + => usb stor + Device 0: Vendor: sandbox Rev: 1.0 Prod: flash + Type: Hard Disk + Capacity: 4.0 MB = 0.0 GB (8192 x 512) + Device 1: Vendor: sandbox Rev: 1.0 Prod: flash + Type: Hard Disk + Capacity: 0.0 MB = 0.0 GB (1 x 512) + => + Return value ------------ diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst index aa12dc2e3ab..cad09bbec9a 100644 --- a/doc/usage/cmd/bootflow.rst +++ b/doc/usage/cmd/bootflow.rst @@ -8,7 +8,7 @@ Synopis :: - bootflow scan [-abel] [bootdev] + bootflow scan [-abelGH] [bootdev] bootflow list [-e] bootflow select [<num|name>] bootflow info [-d] @@ -57,6 +57,16 @@ Flags are: is happening during scanning. Use it with the `-b` flag to see which bootdev and bootflows are being tried. +-G + Skip global bootmeths when scanning. By default these are tried first, but + this flag disables them. + +-H + Don't use bootdev hunters. By default these are used before each boot + priority or label is tried, to see if more bootdevs can be discovered, but + this flag disables that process. + + The optional argument specifies a particular bootdev to scan. This can either be the name of a bootdev or its sequence number (both shown with `bootdev list`). Alternatively a convenience label can be used, like `mmc0`, which is the type of @@ -145,6 +155,7 @@ Subdir (none) Filename /extlinux/extlinux.conf Buffer 3db7ad48 Size 232 (562 bytes) +FDT: <NULL> Error 0 ========= =============================== @@ -169,6 +180,10 @@ Buffer Size Size of the bootflow file +FDT: + Filename of the device tree, if supported. The EFI bootmeth uses this to + remember the filename to load. If `<NULL>` then there is none. + Error Error number returned from scanning for the bootflow. This is 0 if the bootflow is in the 'loaded' state, or a negative error value on error. You diff --git a/doc/usage/cmd/dm.rst b/doc/usage/cmd/dm.rst index 7bc1962a754..236cd02bd62 100644 --- a/doc/usage/cmd/dm.rst +++ b/doc/usage/cmd/dm.rst @@ -12,7 +12,7 @@ Synopis dm devres dm drivers dm static - dm tree + dm tree [-s] dm uclass Description @@ -123,6 +123,9 @@ Name Shows the device name as well as the tree structure, since child devices are shown attached to their parent. +If -s is given, the top-level devices (those which are children of the root +device) are shown sorted in order of uclass ID, so it is easier to find a +particular device type. dm uclass ~~~~~~~~~ diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index de6131f1d9b..272c48b8e57 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -675,6 +675,12 @@ static int ata_scsiop_inquiry(struct ahci_uc_priv *uc_priv, /* Read id from sata */ port = pccb->target; + /* If this port number is not valid, give up */ + if (!(uc_priv->port_map & (1 << port))) { + debug("Port %x not valid in map %x\n", port, uc_priv->port_map); + return -ENODEV; + } + if (ahci_device_data_io(uc_priv, port, (u8 *)&fis, sizeof(fis), (u8 *)tmpid, ATA_ID_WORDS * 2, 0)) { debug("scsi_ahci: SCSI inquiry command failure.\n"); diff --git a/drivers/block/ide.c b/drivers/block/ide.c index eaa58d859c6..1ad9b6c1267 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -9,6 +9,7 @@ #include <common.h> #include <ata.h> #include <blk.h> +#include <bootdev.h> #include <dm.h> #include <ide.h> #include <log.h> @@ -443,9 +444,6 @@ static void atapi_inquiry(struct blk_desc *dev_desc) device = dev_desc->devnum; dev_desc->type = DEV_TYPE_UNKNOWN; /* not yet valid */ -#ifndef CONFIG_BLK - dev_desc->block_read = atapi_read; -#endif memset(ccb, 0, sizeof(ccb)); memset(iobuf, 0, sizeof(iobuf)); @@ -692,6 +690,7 @@ __weak unsigned char ide_inb(int dev, int port) void ide_init(void) { + struct udevice *dev; unsigned char c; int i, bus; @@ -759,29 +758,14 @@ void ide_init(void) ide_dev_desc[i].log2blksz = LOG2_INVALID(typeof(ide_dev_desc[i].log2blksz)); ide_dev_desc[i].lba = 0; -#ifndef CONFIG_BLK - ide_dev_desc[i].block_read = ide_read; - ide_dev_desc[i].block_write = ide_write; -#endif if (!ide_bus_ok[IDE_BUS(i)]) continue; ide_ident(&ide_dev_desc[i]); dev_print(&ide_dev_desc[i]); - -#ifndef CONFIG_BLK - if ((ide_dev_desc[i].lba > 0) && (ide_dev_desc[i].blksz > 0)) { - /* initialize partition type */ - part_init(&ide_dev_desc[i]); - } -#endif } schedule(); -#ifdef CONFIG_BLK - struct udevice *dev; - uclass_first_device(UCLASS_IDE, &dev); -#endif } __weak void ide_input_swap_data(int dev, ulong *sect_buf, int words) @@ -830,17 +814,10 @@ __weak void ide_input_data(int dev, ulong *sect_buf, int words) } } -#ifdef CONFIG_BLK ulong ide_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *buffer) -#else -ulong ide_read(struct blk_desc *block_dev, lbaint_t blknr, lbaint_t blkcnt, - void *buffer) -#endif { -#ifdef CONFIG_BLK struct blk_desc *block_dev = dev_get_uclass_plat(dev); -#endif int device = block_dev->devnum; ulong n = 0; unsigned char c; @@ -957,17 +934,10 @@ IDE_READ_E: return n; } -#ifdef CONFIG_BLK ulong ide_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, const void *buffer) -#else -ulong ide_write(struct blk_desc *block_dev, lbaint_t blknr, lbaint_t blkcnt, - const void *buffer) -#endif { -#ifdef CONFIG_BLK struct blk_desc *block_dev = dev_get_uclass_plat(dev); -#endif int device = block_dev->devnum; ulong n = 0; unsigned char c; @@ -1056,7 +1026,6 @@ int ide_device_present(int dev) } #endif -#ifdef CONFIG_BLK static int ide_blk_probe(struct udevice *udev) { struct blk_desc *desc = dev_get_uclass_plat(udev); @@ -1087,6 +1056,45 @@ U_BOOT_DRIVER(ide_blk) = { .probe = ide_blk_probe, }; +static int ide_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_5_SCAN_SLOW; + + return 0; +} + +static int ide_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + ide_init(); + + return 0; +} + +struct bootdev_ops ide_bootdev_ops = { +}; + +static const struct udevice_id ide_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-ide" }, + { } +}; + +U_BOOT_DRIVER(ide_bootdev) = { + .name = "ide_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &ide_bootdev_ops, + .bind = ide_bootdev_bind, + .of_match = ide_bootdev_ids, +}; + +BOOTDEV_HUNTER(ide_bootdev_hunter) = { + .prio = BOOTDEVP_5_SCAN_SLOW, + .uclass = UCLASS_IDE, + .hunt = ide_bootdev_hunt, + .drv = DM_DRIVER_REF(ide_bootdev), +}; + static int ide_probe(struct udevice *udev) { struct udevice *blk_dev; @@ -1118,6 +1126,10 @@ static int ide_probe(struct udevice *udev) ret = blk_probe_or_unbind(blk_dev); if (ret) return ret; + + ret = bootdev_setup_for_dev(udev, "ide_bootdev"); + if (ret) + return log_msg_ret("bootdev", ret); } } @@ -1141,11 +1153,3 @@ UCLASS_DRIVER(ide) = { .name = "ide", .id = UCLASS_IDE, }; -#else -U_BOOT_LEGACY_BLK(ide) = { - .uclass_idname = "ide", - .uclass_id = UCLASS_IDE, - .max_devs = CONFIG_SYS_IDE_MAXDEVICE, - .desc = ide_dev_desc, -}; -#endif diff --git a/drivers/core/dump.c b/drivers/core/dump.c index 1c1f7e4d308..0c7d2ec4d0c 100644 --- a/drivers/core/dump.c +++ b/drivers/core/dump.c @@ -5,12 +5,34 @@ #include <common.h> #include <dm.h> +#include <malloc.h> #include <mapmem.h> +#include <sort.h> #include <dm/root.h> #include <dm/util.h> #include <dm/uclass-internal.h> -static void show_devices(struct udevice *dev, int depth, int last_flag) +/** + * struct sort_info - information used for sorting + * + * @dev: List of devices + * @alloced: Maximum number of devices in @dev + */ +struct sort_info { + struct udevice **dev; + int size; +}; + +static int h_cmp_uclass_id(const void *d1, const void *d2) +{ + const struct udevice *const *dev1 = d1; + const struct udevice *const *dev2 = d2; + + return device_get_uclass_id(*dev1) - device_get_uclass_id(*dev2); +} + +static void show_devices(struct udevice *dev, int depth, int last_flag, + struct udevice **devs) { int i, is_last; struct udevice *child; @@ -39,21 +61,52 @@ static void show_devices(struct udevice *dev, int depth, int last_flag) printf("%s\n", dev->name); - device_foreach_child(child, dev) { - is_last = list_is_last(&child->sibling_node, &dev->child_head); - show_devices(child, depth + 1, (last_flag << 1) | is_last); + if (devs) { + int count; + int i; + + count = 0; + device_foreach_child(child, dev) + devs[count++] = child; + qsort(devs, count, sizeof(struct udevice *), h_cmp_uclass_id); + + for (i = 0; i < count; i++) { + show_devices(devs[i], depth + 1, + (last_flag << 1) | (i == count - 1), + devs + count); + } + } else { + device_foreach_child(child, dev) { + is_last = list_is_last(&child->sibling_node, + &dev->child_head); + show_devices(child, depth + 1, + (last_flag << 1) | is_last, NULL); + } } } -void dm_dump_tree(void) +void dm_dump_tree(bool sort) { struct udevice *root; root = dm_root(); if (root) { + int dev_count, uclasses; + struct udevice **devs = NULL; + + dm_get_stats(&dev_count, &uclasses); + printf(" Class Index Probed Driver Name\n"); printf("-----------------------------------------------------------\n"); - show_devices(root, -1, 0); + if (sort) { + devs = calloc(dev_count, sizeof(struct udevice *)); + if (!devs) { + printf("(out of memory)\n"); + return; + } + } + show_devices(root, -1, 0, devs); + free(devs); } } diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 759a6b728c8..01d9b0201f2 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -421,7 +421,7 @@ int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg) mmc->cfg = cfg; mmc->priv = dev; - ret = bootdev_setup_for_dev(dev, "mmc_bootdev"); + ret = bootdev_setup_sibling_blk(bdev, "mmc_bootdev"); if (ret) return log_msg_ret("bootdev", ret); diff --git a/drivers/mmc/mmc_bootdev.c b/drivers/mmc/mmc_bootdev.c index b4f41fb3a67..b57b8a62276 100644 --- a/drivers/mmc/mmc_bootdev.c +++ b/drivers/mmc/mmc_bootdev.c @@ -11,41 +11,16 @@ #include <dm.h> #include <mmc.h> -static int mmc_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, - struct bootflow *bflow) -{ - struct udevice *mmc_dev = dev_get_parent(dev); - struct udevice *blk; - int ret; - - ret = mmc_get_blk(mmc_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; -} - static int mmc_bootdev_bind(struct udevice *dev) { struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); - ucp->prio = BOOTDEVP_0_INTERNAL_FAST; + ucp->prio = BOOTDEVP_2_INTERNAL_FAST; return 0; } struct bootdev_ops mmc_bootdev_ops = { - .get_bootflow = mmc_get_bootflow, }; static const struct udevice_id mmc_bootdev_ids[] = { @@ -60,3 +35,9 @@ U_BOOT_DRIVER(mmc_bootdev) = { .bind = mmc_bootdev_bind, .of_match = mmc_bootdev_ids, }; + +BOOTDEV_HUNTER(mmc_bootdev_hunter) = { + .prio = BOOTDEVP_2_INTERNAL_FAST, + .uclass = UCLASS_MMC, + .drv = DM_DRIVER_REF(mmc_bootdev), +}; diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c index ba79a5565c3..0ba7940a4db 100644 --- a/drivers/mmc/sandbox_mmc.c +++ b/drivers/mmc/sandbox_mmc.c @@ -183,7 +183,7 @@ static int sandbox_mmc_probe(struct udevice *dev) priv->csize = 0; priv->size = (priv->csize + 1) * SIZE_MULTIPLE; /* 1 MiB */ - priv->buf = malloc(priv->size); + priv->buf = calloc(1, priv->size); if (!priv->buf) { log_err("%s: Not enough memory (%x bytes)\n", dev->name, priv->size); diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 7b858a3a919..a9617c6c58c 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -80,6 +80,14 @@ config SF_DEFAULT_SPEED if SPI_FLASH +config BOOTDEV_SPI_FLASH + bool "SPI Flash bootdev support" + help + Enable a boot device for SPI flash. This allows reading a script + from SPI flash so that it can be used to boot an Operating System. + + If unsure, say N + config SPI_FLASH_SFDP_SUPPORT bool "SFDP table parsing support for SPI NOR flashes" depends on !SPI_FLASH_BAR diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 99cc4185522..409395382f5 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_SPI_FLASH) += spi-nor.o obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_MTD) += sf_mtd.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o +obj-$(CONFIG_$(SPL_TPL_)BOOTDEV_SPI_FLASH) += sf_bootdev.o diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c index e6e650ef8c0..df1f75390c4 100644 --- a/drivers/mtd/spi/sf-uclass.c +++ b/drivers/mtd/spi/sf-uclass.c @@ -6,6 +6,7 @@ #define LOG_CATEGORY UCLASS_SPI_FLASH #include <common.h> +#include <bootdev.h> #include <dm.h> #include <log.h> #include <malloc.h> @@ -13,6 +14,7 @@ #include <spi_flash.h> #include <asm/global_data.h> #include <dm/device-internal.h> +#include <test/test.h> #include "sf_internal.h" DECLARE_GLOBAL_DATA_PTR; @@ -86,6 +88,14 @@ int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, static int spi_flash_post_bind(struct udevice *dev) { + int ret; + + if (CONFIG_IS_ENABLED(BOOTDEV_SPI_FLASH) && test_sf_bootdev_enabled()) { + ret = bootdev_setup_for_dev(dev, "sf_bootdev"); + if (ret) + return log_msg_ret("bd", ret); + } + #if defined(CONFIG_NEEDS_MANUAL_RELOC) struct dm_spi_flash_ops *ops = sf_get_ops(dev); static int reloc_done; @@ -101,6 +111,7 @@ static int spi_flash_post_bind(struct udevice *dev) reloc_done++; } #endif + return 0; } diff --git a/drivers/mtd/spi/sf_bootdev.c b/drivers/mtd/spi/sf_bootdev.c new file mode 100644 index 00000000000..d6b47b11ce4 --- /dev/null +++ b/drivers/mtd/spi/sf_bootdev.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Read a bootflow from SPI flash + * + * Copyright 2022 Google LLC + */ + +#include <common.h> +#include <bootdev.h> +#include <bootflow.h> +#include <bootmeth.h> +#include <dm.h> +#include <env.h> +#include <malloc.h> +#include <spi_flash.h> + +static int sf_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, + struct bootflow *bflow) +{ + struct udevice *sf = dev_get_parent(dev); + uint size; + char *buf; + int ret; + + /* We only support the whole device, not partitions */ + if (iter->part) + return log_msg_ret("max", -ESHUTDOWN); + + size = env_get_hex("script_size_f", 0); + if (!size) + return log_msg_ret("sz", -EINVAL); + + buf = malloc(size + 1); + if (!buf) + return log_msg_ret("buf", -ENOMEM); + + ret = spi_flash_read_dm(sf, env_get_hex("script_offset_f", 0), + size, buf); + if (ret) + return log_msg_ret("cmd", -EINVAL); + bflow->state = BOOTFLOWST_MEDIA; + + ret = bootmeth_set_bootflow(bflow->method, bflow, buf, size); + if (ret) { + free(buf); + return log_msg_ret("method", ret); + } + + return 0; +} + +static int sf_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_4_SCAN_FAST; + + return 0; +} + +struct bootdev_ops sf_bootdev_ops = { + .get_bootflow = sf_get_bootflow, +}; + +static const struct udevice_id sf_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-sf" }, + { } +}; + +U_BOOT_DRIVER(sf_bootdev) = { + .name = "sf_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &sf_bootdev_ops, + .bind = sf_bootdev_bind, + .of_match = sf_bootdev_ids, +}; + +BOOTDEV_HUNTER(sf_bootdev_hunter) = { + .prio = BOOTDEVP_4_SCAN_FAST, + .uclass = UCLASS_SPI_FLASH, + .drv = DM_DRIVER_REF(sf_bootdev), +}; diff --git a/drivers/nvme/nvme-uclass.c b/drivers/nvme/nvme-uclass.c index 239a92abba6..f3af6a27b63 100644 --- a/drivers/nvme/nvme-uclass.c +++ b/drivers/nvme/nvme-uclass.c @@ -7,9 +7,63 @@ #define LOG_CATEGORY UCLASS_NVME #include <common.h> +#include <bootdev.h> #include <dm.h> +#include <init.h> +#include <log.h> +#include <nvme.h> + +static int nvme_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_4_SCAN_FAST; + + return 0; +} + +static int nvme_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + int ret; + + /* init PCI first since this is often used to provide NVMe */ + if (IS_ENABLED(CONFIG_PCI)) { + ret = pci_init(); + if (ret) + log_warning("Failed to init PCI (%dE)\n", ret); + } + + ret = nvme_scan_namespace(); + if (ret) + return log_msg_ret("scan", ret); + + return 0; +} UCLASS_DRIVER(nvme) = { .name = "nvme", .id = UCLASS_NVME, }; + +struct bootdev_ops nvme_bootdev_ops = { +}; + +static const struct udevice_id nvme_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-nvme" }, + { } +}; + +U_BOOT_DRIVER(nvme_bootdev) = { + .name = "nvme_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &nvme_bootdev_ops, + .bind = nvme_bootdev_bind, + .of_match = nvme_bootdev_ids, +}; + +BOOTDEV_HUNTER(nvme_bootdev_hunter) = { + .prio = BOOTDEVP_4_SCAN_FAST, + .uclass = UCLASS_NVME, + .hunt = nvme_bootdev_hunt, + .drv = DM_DRIVER_REF(nvme_bootdev), +}; diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index 6d0d3f3ca2b..74e7a5b0110 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -6,6 +6,7 @@ #include <common.h> #include <blk.h> +#include <bootdev.h> #include <cpu_func.h> #include <dm.h> #include <errno.h> @@ -893,6 +894,10 @@ int nvme_init(struct udevice *udev) if (ret) goto free_id; + ret = bootdev_setup_sibling_blk(ns_udev, "nvme_bootdev"); + if (ret) + return log_msg_ret("bootdev", ret); + ret = blk_probe_or_unbind(ns_udev); if (ret) goto free_id; diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d1b40c61401..d8d6de59090 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -6,6 +6,13 @@ ifndef CONFIG_SPL_BUILD obj-$(CONFIG_DM_SCSI) += scsi-uclass.o obj-$(CONFIG_SCSI) += scsi.o + +ifdef CONFIG_SCSI +ifdef CONFIG_DM_SCSI +obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += scsi_bootdev.o +endif +endif + endif ifdef CONFIG_SPL_BUILD diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index a020a7da23a..6caeb3fcdd0 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -4,8 +4,11 @@ * Denis Peter, MPL AG Switzerland */ +#define LOG_CATEGORY UCLASS_SCSI + #include <common.h> #include <blk.h> +#include <bootdev.h> #include <bootstage.h> #include <dm.h> #include <env.h> @@ -558,7 +561,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) struct udevice *bdev; struct blk_desc bd; struct blk_desc *bdesc; - char str[10]; + char str[10], *name; /* * detect the scsi driver to get information about its geometry (block @@ -574,12 +577,16 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) * block devices created */ snprintf(str, sizeof(str), "id%dlun%d", id, lun); - ret = blk_create_devicef(dev, "scsi_blk", str, UCLASS_SCSI, -1, + name = strdup(str); + if (!name) + return log_msg_ret("nam", -ENOMEM); + ret = blk_create_devicef(dev, "scsi_blk", name, UCLASS_SCSI, -1, bd.blksz, bd.lba, &bdev); if (ret) { debug("Can't create device\n"); return ret; } + device_set_name_alloced(bdev); bdesc = dev_get_uclass_plat(bdev); bdesc->target = id; @@ -598,7 +605,11 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) ret = blk_probe_or_unbind(bdev); if (ret < 0) /* TODO: undo create */ - return ret; + return log_msg_ret("pro", ret); + + ret = bootdev_setup_sibling_blk(bdev, "scsi_bootdev"); + if (ret) + return log_msg_ret("bd", ret); if (verbose) { printf(" Device %d: ", bdesc->devnum); @@ -638,12 +649,23 @@ int scsi_scan(bool verbose) if (verbose) printf("scanning bus for devices...\n"); - blk_unbind_all(UCLASS_SCSI); - ret = uclass_get(UCLASS_SCSI, &uc); if (ret) return ret; + /* remove all children of the SCSI devices */ + uclass_foreach_dev(dev, uc) { + log_debug("unbind %s\n", dev->name); + ret = device_chld_remove(dev, NULL, DM_REMOVE_NORMAL); + if (!ret) + ret = device_chld_unbind(dev, NULL); + if (ret) { + if (verbose) + printf("unable to unbind devices (%dE)\n", ret); + return log_msg_ret("unb", ret); + } + } + uclass_foreach_dev(dev, uc) { ret = scsi_scan_dev(dev, verbose); if (ret) diff --git a/drivers/scsi/scsi_bootdev.c b/drivers/scsi/scsi_bootdev.c new file mode 100644 index 00000000000..991013fe1ef --- /dev/null +++ b/drivers/scsi/scsi_bootdev.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Bootdevice for USB + * + * Copyright 2021 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <bootdev.h> +#include <dm.h> +#include <init.h> +#include <scsi.h> + +static int scsi_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_4_SCAN_FAST; + + return 0; +} + +static int scsi_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + int ret; + + if (IS_ENABLED(CONFIG_PCI)) { + ret = pci_init(); + if (ret) + return log_msg_ret("pci", ret); + } + + ret = scsi_scan(true); + if (ret) + return log_msg_ret("scs", ret); + + return 0; +} + +struct bootdev_ops scsi_bootdev_ops = { +}; + +static const struct udevice_id scsi_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-scsi" }, + { } +}; + +U_BOOT_DRIVER(scsi_bootdev) = { + .name = "scsi_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &scsi_bootdev_ops, + .bind = scsi_bootdev_bind, + .of_match = scsi_bootdev_ids, +}; + +BOOTDEV_HUNTER(scsi_bootdev_hunter) = { + .prio = BOOTDEVP_4_SCAN_FAST, + .uclass = UCLASS_SCSI, + .hunt = scsi_bootdev_hunt, + .drv = DM_DRIVER_REF(scsi_bootdev), +}; diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 93c318c3d1d..28f7ca9654d 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -563,6 +563,8 @@ static int usb_find_and_bind_driver(struct udevice *parent, if (!str) return -ENOMEM; ret = device_bind_driver(parent, "usb_dev_generic_drv", str, devp); + if (!ret) + device_set_name_alloced(*devp); error: debug("%s: No match found: %d\n", __func__, ret); diff --git a/drivers/usb/host/usb_bootdev.c b/drivers/usb/host/usb_bootdev.c index b85f699933d..32919f99286 100644 --- a/drivers/usb/host/usb_bootdev.c +++ b/drivers/usb/host/usb_bootdev.c @@ -11,40 +11,21 @@ #include <dm.h> #include <usb.h> -static int usb_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; -} - static int usb_bootdev_bind(struct udevice *dev) { struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); - ucp->prio = BOOTDEVP_3_SCAN_SLOW; + ucp->prio = BOOTDEVP_5_SCAN_SLOW; return 0; } +static int usb_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + return usb_init(); +} + struct bootdev_ops usb_bootdev_ops = { - .get_bootflow = usb_get_bootflow, }; static const struct udevice_id usb_bootdev_ids[] = { @@ -59,3 +40,10 @@ U_BOOT_DRIVER(usb_bootdev) = { .bind = usb_bootdev_bind, .of_match = usb_bootdev_ids, }; + +BOOTDEV_HUNTER(usb_bootdev_hunter) = { + .prio = BOOTDEVP_5_SCAN_SLOW, + .uclass = UCLASS_USB, + .hunt = usb_bootdev_hunt, + .drv = DM_DRIVER_REF(usb_bootdev), +}; diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index da4f2f26a63..91af412ec1d 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -18,6 +18,7 @@ #define LOG_CATEGORY UCLASS_VIRTIO #include <common.h> +#include <bootdev.h> #include <dm.h> #include <log.h> #include <malloc.h> @@ -163,7 +164,7 @@ int virtio_finalize_features(struct udevice *vdev) return ret; if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) { debug("(%s): device refuses features %x\n", vdev->name, status); - return -ENODEV; + return -EINVAL; } return 0; @@ -214,6 +215,7 @@ static int virtio_uclass_post_probe(struct udevice *udev) struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); char dev_name[30], *str; struct udevice *vdev; + const char *name; int ret; if (uc_priv->device >= VIRTIO_ID_MAX_NUM) { @@ -222,20 +224,19 @@ static int virtio_uclass_post_probe(struct udevice *udev) return 0; } - if (!virtio_drv_name[uc_priv->device]) { + name = virtio_drv_name[uc_priv->device]; + if (!name) { debug("(%s): underlying virtio device driver unavailable\n", udev->name); return 0; } - snprintf(dev_name, sizeof(dev_name), "%s#%d", - virtio_drv_name[uc_priv->device], dev_seq(udev)); + snprintf(dev_name, sizeof(dev_name), "%s#%d", name, dev_seq(udev)); str = strdup(dev_name); if (!str) return -ENOMEM; - ret = device_bind_driver(udev, virtio_drv_name[uc_priv->device], - str, &vdev); + ret = device_bind_driver(udev, name, str, &vdev); if (ret == -ENOENT) { debug("(%s): no driver configured\n", udev->name); return 0; @@ -246,6 +247,12 @@ static int virtio_uclass_post_probe(struct udevice *udev) } device_set_name_alloced(vdev); + if (uc_priv->device == VIRTIO_ID_BLOCK) { + ret = bootdev_setup_for_dev(udev, name); + if (ret) + return log_msg_ret("bootdev", ret); + } + INIT_LIST_HEAD(&uc_priv->vqs); return 0; @@ -349,6 +356,26 @@ static int virtio_uclass_child_post_probe(struct udevice *vdev) return 0; } +static int virtio_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_4_SCAN_FAST; + + return 0; +} + +static int virtio_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + int ret; + + ret = uclass_probe_all(UCLASS_VIRTIO); + if (ret && ret != -ENOENT) + return log_msg_ret("vir", ret); + + return 0; +} + UCLASS_DRIVER(virtio) = { .name = "virtio", .id = UCLASS_VIRTIO, @@ -360,3 +387,26 @@ UCLASS_DRIVER(virtio) = { .child_post_probe = virtio_uclass_child_post_probe, .per_device_auto = sizeof(struct virtio_dev_priv), }; + +struct bootdev_ops virtio_bootdev_ops = { +}; + +static const struct udevice_id virtio_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-virtio" }, + { } +}; + +U_BOOT_DRIVER(virtio_bootdev) = { + .name = "virtio_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &virtio_bootdev_ops, + .bind = virtio_bootdev_bind, + .of_match = virtio_bootdev_ids, +}; + +BOOTDEV_HUNTER(virtio_bootdev_hunter) = { + .prio = BOOTDEVP_4_SCAN_FAST, + .uclass = UCLASS_VIRTIO, + .hunt = virtio_bootdev_hunt, + .drv = DM_DRIVER_REF(virtio_bootdev), +}; diff --git a/drivers/virtio/virtio_sandbox.c b/drivers/virtio/virtio_sandbox.c index 5484ae3a1a0..b34f1d60455 100644 --- a/drivers/virtio/virtio_sandbox.c +++ b/drivers/virtio/virtio_sandbox.c @@ -161,24 +161,13 @@ static int virtio_sandbox_probe(struct udevice *udev) /* fake some information for testing */ priv->device_features = BIT_ULL(VIRTIO_F_VERSION_1); - uc_priv->device = VIRTIO_ID_RNG; + uc_priv->device = dev_read_u32_default(udev, "virtio-type", + VIRTIO_ID_RNG); uc_priv->vendor = ('u' << 24) | ('b' << 16) | ('o' << 8) | 't'; return 0; } -/* check virtio device driver's remove routine was called to reset the device */ -static int virtio_sandbox_child_post_remove(struct udevice *vdev) -{ - u8 status; - - virtio_get_status(vdev, &status); - if (status) - panic("virtio device was not reset\n"); - - return 0; -} - static const struct dm_virtio_ops virtio_sandbox1_ops = { .get_config = virtio_sandbox_get_config, .set_config = virtio_sandbox_set_config, @@ -203,7 +192,6 @@ U_BOOT_DRIVER(virtio_sandbox1) = { .of_match = virtio_sandbox1_ids, .ops = &virtio_sandbox1_ops, .probe = virtio_sandbox_probe, - .child_post_remove = virtio_sandbox_child_post_remove, .priv_auto = sizeof(struct virtio_sandbox_priv), }; diff --git a/include/bootdev.h b/include/bootdev.h index 9fc219839fe..b92ff4d4f15 100644 --- a/include/bootdev.h +++ b/include/bootdev.h @@ -11,6 +11,7 @@ struct bootflow; struct bootflow_iter; +struct bootstd_priv; struct udevice; /** @@ -20,19 +21,87 @@ struct udevice; * * Smallest value is the highest priority. By default, bootdevs are scanned from * highest to lowest priority + * + * BOOTDEVP_0_NONE: Invalid value, do not use + * @BOOTDEVP_6_PRE_SCAN: Scan bootdevs with this priority always, before + * starting any bootflow scan + * @BOOTDEVP_2_INTERNAL_FAST: Internal devices which don't need scanning and + * generally very quick to access, e.g. less than 100ms + * @BOOTDEVP_3_INTERNAL_SLOW: Internal devices which don't need scanning but + * take a significant fraction of a second to access + * @BOOTDEVP_4_SCAN_FAST: Extenal devices which need scanning or bus + * enumeration to find, but this enumeration happens quickly, typically under + * 100ms + * @BOOTDEVP_5_SCAN_SLOW: Extenal devices which need scanning or bus + * enumeration to find. The enumeration takes significant fraction of a second + * to complete + * @BOOTDEVP_6_NET_BASE: Basic network devices which are quickly and easily + * available. Typically used for an internal Ethernet device + * @BOOTDEVP_7_NET_FALLBACK: Secondary network devices which require extra time + * to start up, or are less desirable. Typically used for secondary Ethernet + * devices. Note that USB ethernet devices are found during USB enumeration, + * so do not use this priority */ enum bootdev_prio_t { - BOOTDEVP_0_INTERNAL_FAST = 10, - BOOTDEVP_1_INTERNAL_SLOW = 20, - BOOTDEVP_2_SCAN_FAST = 30, - BOOTDEVP_3_SCAN_SLOW = 40, - BOOTDEVP_4_NET_BASE = 50, - BOOTDEVP_5_NET_FALLBACK = 60, - BOOTDEVP_6_SYSTEM = 70, + BOOTDEVP_0_NONE, + BOOTDEVP_1_PRE_SCAN, + BOOTDEVP_2_INTERNAL_FAST, + BOOTDEVP_3_INTERNAL_SLOW, + BOOTDEVP_4_SCAN_FAST, + BOOTDEVP_5_SCAN_SLOW, + BOOTDEVP_6_NET_BASE, + BOOTDEVP_7_NET_FALLBACK, BOOTDEVP_COUNT, }; +struct bootdev_hunter; + +/** + * bootdev_hunter_func - function to probe for bootdevs of a given type + * + * This should hunt around for bootdevs of the given type, binding them as it + * finds them. This may involve bus enumeration, etc. + * + * @info: Info structure describing this hunter + * @show: true to show information from the hunter + * Returns: 0 if OK, -ve on error + */ +typedef int (*bootdev_hunter_func)(struct bootdev_hunter *info, bool show); + +/** + * struct bootdev_hunter - information about how to hunt for bootdevs + * + * @prio: Scanning priority of this hunter + * @uclass: Uclass ID for the media associated with this bootdev + * @drv: bootdev driver for the things found by this hunter + * @hunt: Function to call to hunt for bootdevs of this type (NULL if none) + * + * Some bootdevs are not visible until other devices are enumerated. For + * example, USB bootdevs only appear when the USB bus is enumerated. + * + * On the other hand, we don't always want to enumerate all the buses just to + * find the first valid bootdev. Ideally we want to work through them in + * priority order, so that the fastest bootdevs are discovered first. + * + * This struct holds information about the bootdev so we can determine the probe + * order and how to hunt for bootdevs of this type + */ +struct bootdev_hunter { + enum bootdev_prio_t prio; + enum uclass_id uclass; + struct driver *drv; + bootdev_hunter_func hunt; +}; + +/* declare a new bootdev hunter */ +#define BOOTDEV_HUNTER(__name) \ + ll_entry_declare(struct bootdev_hunter, __name, bootdev_hunter) + +/* access a bootdev hunter by name */ +#define BOOTDEV_HUNTER_GET(__name) \ + ll_entry_get(struct bootdev_hunter, __name, bootdev_hunter) + /** * struct bootdev_uc_plat - uclass information about a bootdev * @@ -50,7 +119,10 @@ struct bootdev_uc_plat { /** struct bootdev_ops - Operations for the bootdev uclass */ struct bootdev_ops { /** - * get_bootflow() - get a bootflow + * get_bootflow() - get a bootflow (optional) + * + * If this is NULL then the default implementaton is used, which is + * default_get_bootflow() * * @dev: Bootflow device to check * @iter: Provides current dev, part, method to get. Should update @@ -171,40 +243,136 @@ int bootdev_next_bootflow(struct bootflow **bflowp); * @label: Label to look up (e.g. "mmc1" or "mmc0") * @devp: Returns the bootdev device found, or NULL if none (note it does not * return the media device, but its bootdev child) + * @method_flagsp: If non-NULL, returns any flags implied by the label + * (enum bootflow_meth_flags_t), 0 if none. Unset if function fails * Return: 0 if OK, -EINVAL if the uclass is not supported by this board, - * -ENOENT if there is no device with that number + * -ENOENT if there is no device with that number */ -int bootdev_find_by_label(const char *label, struct udevice **devp); +int bootdev_find_by_label(const char *label, struct udevice **devp, + int *method_flagsp); /** * bootdev_find_by_any() - Find a bootdev by name, label or sequence * * @name: name (e.g. "mmc2.bootdev"), label ("mmc2"), or sequence ("2") to find * @devp: returns the device found, on success - * Return: 0 if OK, -ve on error + * @method_flagsp: If non-NULL, returns any flags implied by the label + * (enum bootflow_meth_flags_t), 0 if none. Unset if function fails + * Return: 0 if OK, -EINVAL if the uclass is not supported by this board, + * -ENOENT if there is no device with that number */ -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); /** - * bootdev_setup_iter_order() - Set up the ordering of bootdevs to scan - * - * This sets up the ordering information in @iter, based on the priority of each - * bootdev and the bootdev-order property in the bootstd node + * bootdev_setup_iter() - Set up iteration through bootdevs * - * If a single device is requested, no ordering is needed + * 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 + * @method_flagsp: If non-NULL, returns any flags implied by the label + * (enum bootflow_meth_flags_t), 0 if none * Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve * on other error */ -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); + +/** + * bootdev_list_hunters() - List the available bootdev hunters + * + * These provide a way to find new bootdevs by enumerating buses, etc. This + * function lists the available hunters + * + * @std: Pointer to bootstd private info + */ +void bootdev_list_hunters(struct bootstd_priv *std); + +/** + * bootdev_hunt() - Hunt for bootdevs matching a particular spec + * + * This runs the selected hunter (or all if @spec is NULL) to try to find new + * bootdevs. + * + * @spec: Spec to match, e.g. "mmc0", or NULL for any. If provided, this must + * match a uclass name so that the hunter can be determined. Any trailing number + * is ignored + * @show: true to show each hunter before using it + * Returns: 0 if OK, -ve on error + */ +int bootdev_hunt(const char *spec, bool show); + +/** + * bootdev_hunt_prio() - Hunt for bootdevs of a particular priority + * + * This runs all hunters which can find bootdevs of the given priority. + * + * @prio: Priority to use + * @show: true to show each hunter as it is used + * Returns: 0 if OK, -ve on error + */ +int bootdev_hunt_prio(enum bootdev_prio_t prio, bool show); + +/** + * bootdev_hunt_and_find_by_label() - Hunt for bootdevs by label + * + * Runs the hunter for the label, then tries to find the bootdev, possible + * created by the hunter + * + * @label: Label to look up (e.g. "mmc1" or "mmc0") + * @devp: Returns the bootdev device found, or NULL if none (note it does not + * return the media device, but its bootdev child) + * @method_flagsp: If non-NULL, returns any flags implied by the label + * (enum bootflow_meth_flags_t), 0 if none. Unset if function fails + * Return: 0 if OK, -EINVAL if the uclass is not supported by this board, + * -ENOENT if there is no device with that number + */ +int bootdev_hunt_and_find_by_label(const char *label, struct udevice **devp, + int *method_flagsp); + +/** + * bootdev_next_label() - Move to the next bootdev in the label sequence + * + * Looks through the remaining labels until it finds one that matches a bootdev. + * Bootdev scanners are used as needed. For example a label "mmc1" results in + * running the "mmc" bootdrv. + * + * @iter: Interation info, containing iter->cur_label + * @devp: New bootdev found, if any was found + * @method_flagsp: If non-NULL, returns any flags implied by the label + * (enum bootflow_meth_flags_t), 0 if none + * Returns 0 if OK, -ENODEV if no bootdev was found + */ +int bootdev_next_label(struct bootflow_iter *iter, struct udevice **devp, + int *method_flagsp); + +/** + * bootdev_next_prio() - Find the next bootdev in priority order + * + * This moves @devp to the next bootdev with the current priority. If there is + * none, then it moves to the next priority and scans for new bootdevs there. + * + * @iter: Interation info, containing iter->cur_prio + * @devp: On entry this is the previous bootdev that was considered. On exit + * this is the new bootdev, if any was found + * Returns 0 on success (*devp is updated), -ENODEV if there are no more + * bootdevs at any priority + */ +int bootdev_next_prio(struct bootflow_iter *iter, struct udevice **devp); #if CONFIG_IS_ENABLED(BOOTSTD) /** - * bootdev_setup_for_dev() - Bind a new bootdev device + * bootdev_setup_for_dev() - Bind a new bootdev device (deprecated) + * + * Please use bootdev_setup_sibling_blk() instead since it supports multiple + * (child) block devices for each media device. * * Creates a bootdev device as a child of @parent. This should be called from * the driver's bind() method or its uclass' post_bind() method. diff --git a/include/bootflow.h b/include/bootflow.h index c201246c6de..f516bf8dea4 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -7,12 +7,17 @@ #ifndef __bootflow_h #define __bootflow_h +#include <bootdev.h> #include <dm/ofnode_decl.h> #include <linux/list.h> struct bootstd_priv; struct expo; +enum { + BOOTFLOW_MAX_USED_DEVS = 16, +}; + /** * enum bootflow_state_t - states that a particular bootflow can be in * @@ -43,7 +48,7 @@ enum bootflow_state_t { * @glob_node: Points to siblings in the global list (all bootdev) * @dev: Bootdevice device which produced this bootflow * @blk: Block device which contains this bootflow, NULL if this is a network - * device + * device or sandbox 'host' device * @part: Partition number (0 for whole device) * @fs_type: Filesystem type (FS_TYPE...) if this is fixed by the media, else 0. * For example, the sandbox host-filesystem bootdev sets this to @@ -60,6 +65,9 @@ enum bootflow_state_t { * @err: Error number received (0 if OK) * @os_name: Name of the OS / distro being booted, or NULL if not known * (allocated) + * @fdt_fname: Filename of FDT file + * @fdt_size: Size of FDT file + * @fdt_addr: Address of loaded fdt */ struct bootflow { struct list_head bm_node; @@ -79,23 +87,64 @@ struct bootflow { int size; int err; char *os_name; + char *fdt_fname; + int fdt_size; + ulong fdt_addr; }; /** * enum bootflow_flags_t - flags for the bootflow iterator * * @BOOTFLOWF_FIXED: Only used fixed/internal media - * @BOOTFLOWF_SHOW: Show each bootdev before scanning it + * @BOOTFLOWF_SHOW: Show each bootdev before scanning it; show each hunter + * before using it * @BOOTFLOWF_ALL: Return bootflows with errors as well - * @BOOTFLOWF_SINGLE_DEV: Just scan one bootmeth - * @BOOTFLOWF_SKIP_GLOBAL: Don't scan global bootmeths + * @BOOTFLOWF_HUNT: Hunt for new bootdevs using the bootdrv hunters + * + * Internal flags: + * @BOOTFLOWF_SINGLE_DEV: (internal) Just scan one bootdev + * @BOOTFLOWF_SKIP_GLOBAL: (internal) Don't scan global bootmeths + * @BOOTFLOWF_SINGLE_UCLASS: (internal) Keep scanning through all devices in + * this uclass (used with things like "mmc") + * @BOOTFLOWF_SINGLE_MEDIA: (internal) Scan one media device in the uclass (used + * with things like "mmc1") */ enum bootflow_flags_t { BOOTFLOWF_FIXED = 1 << 0, BOOTFLOWF_SHOW = 1 << 1, BOOTFLOWF_ALL = 1 << 2, - BOOTFLOWF_SINGLE_DEV = 1 << 3, - BOOTFLOWF_SKIP_GLOBAL = 1 << 4, + BOOTFLOWF_HUNT = 1 << 3, + + /* + * flags used internally by standard boot - do not set these when + * calling bootflow_scan_bootdev() etc. + */ + BOOTFLOWF_SINGLE_DEV = 1 << 16, + BOOTFLOWF_SKIP_GLOBAL = 1 << 17, + BOOTFLOWF_SINGLE_UCLASS = 1 << 18, + BOOTFLOWF_SINGLE_MEDIA = 1 << 19, +}; + +/** + * enum bootflow_meth_flags_t - flags controlling which bootmeths are used + * + * Used during iteration, e.g. by bootdev_find_by_label(), to determine which + * bootmeths are used for the current bootdev. The flags reset when the bootdev + * changes + * + * @BOOTFLOW_METHF_DHCP_ONLY: Only use dhcp (scripts and EFI) + * @BOOTFLOW_METHF_PXE_ONLY: Only use pxe (PXE boot) + * @BOOTFLOW_METHF_SINGLE_DEV: Scan only a single bootdev (used for labels like + * "3"). This is used if a sequence number is provided instead of a label + * @BOOTFLOW_METHF_SINGLE_UCLASS: Scan all bootdevs in this one uclass (used + * with things like "mmc"). If this is not set, then the bootdev has an integer + * value in the label (like "mmc2") + */ +enum bootflow_meth_flags_t { + BOOTFLOW_METHF_DHCP_ONLY = 1 << 0, + BOOTFLOW_METHF_PXE_ONLY = 1 << 1, + BOOTFLOW_METHF_SINGLE_DEV = 1 << 2, + BOOTFLOW_METHF_SINGLE_UCLASS = 1 << 3, }; /** @@ -118,25 +167,31 @@ enum bootflow_flags_t { * @flags: Flags to use (see enum bootflow_flags_t). If BOOTFLOWF_GLOBAL_FIRST is * enabled then the global bootmeths are being scanned, otherwise we have * moved onto the bootdevs - * @dev: Current bootdev, NULL if none + * @dev: Current bootdev, NULL if none. This is only ever updated in + * bootflow_iter_set_dev() * @part: Current partition number (0 for whole device) * @method: Current bootmeth * @max_part: Maximum hardware partition number in @dev, 0 if there is no * partition table + * @first_bootable: First bootable partition, or 0 if none * @err: Error obtained from checking the last iteration. This is used to skip * forward (e.g. to skip the current partition because it is not valid) * -ESHUTDOWN: try next bootdev - * @num_devs: Number of bootdevs in @dev_order - * @cur_dev: Current bootdev number, an index into @dev_order[] - * @dev_order: List of bootdevs to scan, in order of priority. The scan starts - * with the first one on the list + * @num_devs: Number of bootdevs in @dev_used + * @max_devs: Maximum number of entries in @dev_used + * @dev_used: List of bootdevs used during iteration + * @labels: List of labels to scan for bootdevs + * @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 + * @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 * @doing_global: true if we are iterating through the global bootmeths (which * happens before the normal ones) + * @method_flags: flags controlling which methods should be used for this @dev + * (enum bootflow_meth_flags_t) */ struct bootflow_iter { int flags; @@ -144,15 +199,20 @@ struct bootflow_iter { int part; struct udevice *method; int max_part; + int first_bootable; int err; int num_devs; - int cur_dev; - struct udevice **dev_order; + int max_devs; + struct udevice *dev_used[BOOTFLOW_MAX_USED_DEVS]; + const char *const *labels; + int cur_label; int num_methods; int cur_method; int first_glob_method; + enum bootdev_prio_t cur_prio; struct udevice **method_order; bool doing_global; + int method_flags; }; /** @@ -197,36 +257,23 @@ int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter, const struct udevice *bmeth); /** - * bootflow_scan_bootdev() - find the first bootflow in a bootdev + * bootflow_scan_first() - find the first bootflow for a device or label * * If @flags includes BOOTFLOWF_ALL then bootflows with errors are returned too * * @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); - -/** - * bootflow_scan_first() - find the first bootflow - * - * This works through the available bootdev devices until it finds one that - * can supply a bootflow. It then returns that - * - * If @flags includes BOOTFLOWF_ALL then bootflows with errors are returned too - * - * @iter: Place to store private info (inited by this call), with - * @flags: Flags for bootdev (enum bootflow_flags_t) - * @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_first(struct bootflow_iter *iter, int flags, +int bootflow_scan_first(struct udevice *dev, const char *label, + struct bootflow_iter *iter, int flags, struct bootflow *bflow); /** @@ -312,33 +359,42 @@ const char *bootflow_state_get_name(enum bootflow_state_t state); void bootflow_remove(struct bootflow *bflow); /** - * bootflow_iter_uses_blk_dev() - Check that a bootflow uses a block device + * bootflow_iter_check_blk() - Check that a bootflow uses a block device * * This checks the bootdev in the bootflow to make sure it uses a block device * * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. ethernet) */ -int bootflow_iter_uses_blk_dev(const struct bootflow_iter *iter); +int bootflow_iter_check_blk(const struct bootflow_iter *iter); + +/** + * bootflow_iter_check_sf() - Check that a bootflow uses SPI FLASH + * + * This checks the bootdev in the bootflow to make sure it uses SPI flash + * + * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. ethernet) + */ +int bootflow_iter_check_sf(const struct bootflow_iter *iter); /** - * bootflow_iter_uses_network() - Check that a bootflow uses a network device + * bootflow_iter_check_net() - Check that a bootflow uses a network device * * This checks the bootdev in the bootflow to make sure it uses a network * device * * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. MMC) */ -int bootflow_iter_uses_network(const struct bootflow_iter *iter); +int bootflow_iter_check_net(const struct bootflow_iter *iter); /** - * bootflow_iter_uses_system() - Check that a bootflow uses the bootstd device + * bootflow_iter_check_system() - Check that a bootflow uses the bootstd device * * This checks the bootdev in the bootflow to make sure it uses the bootstd * device * * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. MMC) */ -int bootflow_iter_uses_system(const struct bootflow_iter *iter); +int bootflow_iter_check_system(const struct bootflow_iter *iter); /** * bootflow_menu_new() - Create a new bootflow menu diff --git a/include/bootmeth.h b/include/bootmeth.h index 669b14ce81e..b12dfd42c90 100644 --- a/include/bootmeth.h +++ b/include/bootmeth.h @@ -88,6 +88,22 @@ struct bootmeth_ops { int (*read_bootflow)(struct udevice *dev, struct bootflow *bflow); /** + * set_bootflow() - set the bootflow for a device + * + * This provides a bootflow file to the bootmeth, to see if it is valid. + * If it is, the bootflow is set up accordingly. + * + * @dev: Bootmethod device to use + * @bflow: On entry, provides bootdev. + * Returns updated bootflow if found + * @buf: Buffer containing the possible bootflow file + * @size: Size of file + * Return: 0 if OK, -ve on error + */ + int (*set_bootflow)(struct udevice *dev, struct bootflow *bflow, + char *buf, int size); + + /** * read_file() - read a file needed for a bootflow * * Read a file from the same place as the bootflow came from @@ -174,6 +190,23 @@ int bootmeth_check(struct udevice *dev, struct bootflow_iter *iter); int bootmeth_read_bootflow(struct udevice *dev, struct bootflow *bflow); /** + * bootmeth_set_bootflow() - set the bootflow for a device + * + * This provides a bootflow file to the bootmeth, to see if it is valid. + * If it is, the bootflow is set up accordingly. + * + * @dev: Bootmethod device to use + * @bflow: On entry, provides bootdev. + * Returns updated bootflow if found + * @buf: Buffer containing the possible bootflow file (must be allocated + * by caller to @size + 1 bytes) + * @size: Size of file + * Return: 0 if OK, -ve on error + */ +int bootmeth_set_bootflow(struct udevice *dev, struct bootflow *bflow, + char *buf, int size); + +/** * bootmeth_read_file() - read a file needed for a bootflow * * Read a file from the same place as the bootflow came from @@ -240,7 +273,7 @@ int bootmeth_set_order(const char *order_str); * caller before reading the file. * * @bflow: Information about file to try - * @desc: Block descriptor to read from + * @desc: Block descriptor to read from (NULL for sandbox host) * @prefix: Filename prefix to prepend to @fname (NULL for none) * @fname: Filename to read * Return: 0 if OK, -ENOMEM if not enough memory to allocate bflow->fname, diff --git a/include/bootstd.h b/include/bootstd.h index 4fa0d531001..dddb3e15384 100644 --- a/include/bootstd.h +++ b/include/bootstd.h @@ -22,7 +22,10 @@ struct udevice; * @prefixes: NULL-terminated list of prefixes to use for bootflow filenames, * e.g. "/", "/boot/"; NULL if none * @bootdev_order: Order to use for bootdevs (or NULL if none), with each item - * being a bootdev label, e.g. "mmc2", "mmc1"; + * being a bootdev label, e.g. "mmc2", "mmc1" (NULL terminated) + * @env_order: Order as specified by the boot_targets env var (or NULL if none), + * with each item being a bootdev label, e.g. "mmc2", "mmc1" (NULL + * terminated) * @cur_bootdev: Currently selected bootdev (for commands) * @cur_bootflow: Currently selected bootflow (for commands) * @glob_head: Head for the global list of all bootflows across all bootdevs @@ -30,10 +33,13 @@ struct udevice; * @bootmeth_order: List of bootmeth devices to use, in order, NULL-terminated * @vbe_bootmeth: Currently selected VBE bootmeth, NULL if none * @theme: Node containing the theme information + * @hunters_used: Bitmask of used hunters, indexed by their position in the + * linker list. The bit is set if the hunter has been used already */ struct bootstd_priv { const char **prefixes; const char **bootdev_order; + const char **env_order; struct udevice *cur_bootdev; struct bootflow *cur_bootflow; struct list_head glob_head; @@ -41,6 +47,7 @@ struct bootstd_priv { struct udevice **bootmeth_order; struct udevice *vbe_bootmeth; ofnode theme; + uint hunters_used; }; /** @@ -51,9 +58,13 @@ struct bootstd_priv { * The list is alloced by the bootstd driver so should not be freed. That is the * reason for all the const stuff in the function signature * - * Return: list of string points, terminated by NULL; or NULL if no boot order + * @dev: bootstd device + * @okp: returns true if OK, false if out of memory + * Return: list of string pointers, terminated by NULL; or NULL if no boot + * order. Note that this returns NULL in the case of an empty list */ -const char *const *const bootstd_get_bootdev_order(struct udevice *dev); +const char *const *const bootstd_get_bootdev_order(struct udevice *dev, + bool *okp); /** * bootstd_get_prefixes() - Get the filename-prefixes list diff --git a/include/configs/rk3399_common.h b/include/configs/rk3399_common.h index 95cb27c8951..3ef9ffa2e9c 100644 --- a/include/configs/rk3399_common.h +++ b/include/configs/rk3399_common.h @@ -48,15 +48,12 @@ #define ROCKCHIP_DEVICE_SETTINGS #endif -#include <config_distro_bootcmd.h> -#include <environment/distro/sf.h> #define CFG_EXTRA_ENV_SETTINGS \ ENV_MEM_LAYOUT_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0" \ "partitions=" PARTS_DEFAULT \ ROCKCHIP_DEVICE_SETTINGS \ - BOOTENV \ - BOOTENV_SF \ + "boot_targets=" BOOT_TARGETS "\0" \ "altbootcmd=" \ "setenv boot_syslinux_conf extlinux/extlinux-rollback.conf;" \ "run distro_bootcmd\0" diff --git a/include/configs/rockchip-common.h b/include/configs/rockchip-common.h index 1f6b82f2d02..0b23e4c0433 100644 --- a/include/configs/rockchip-common.h +++ b/include/configs/rockchip-common.h @@ -65,12 +65,14 @@ BOOT_TARGET_PXE(func) \ BOOT_TARGET_DHCP(func) \ BOOT_TARGET_SF(func) +#define BOOT_TARGETS "mmc1 mmc0 nvme scsi usb pxe dhcp spi" #else #define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_MMC(func) \ BOOT_TARGET_USB(func) \ BOOT_TARGET_PXE(func) \ BOOT_TARGET_DHCP(func) +#define BOOT_TARGETS "mmc1 mmc0 usb pxe dhcp" #endif #ifdef CONFIG_ARM64 diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 376f741cc2b..33e43c20db6 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -47,9 +47,9 @@ enum uclass_id { UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */ UCLASS_DISPLAY, /* Display (e.g. DisplayPort, HDMI) */ - UCLASS_DSI_HOST, /* Display Serial Interface host */ UCLASS_DMA, /* Direct Memory Access */ UCLASS_DSA, /* Distributed (Ethernet) Switch Architecture */ + UCLASS_DSI_HOST, /* Display Serial Interface host */ UCLASS_ECDSA, /* Elliptic curve cryptographic device */ UCLASS_EFI_LOADER, /* Devices created by UEFI applications */ UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ @@ -101,6 +101,7 @@ enum uclass_id { UCLASS_PINCTRL, /* Pinctrl (pin muxing/configuration) device */ UCLASS_PMIC, /* PMIC I/O device */ UCLASS_POWER_DOMAIN, /* (SoC) Power domains */ + UCLASS_PVBLOCK, /* Xen virtual block device */ UCLASS_PWM, /* Pulse-width modulator */ UCLASS_PWRSEQ, /* Power sequence device */ UCLASS_QFW, /* QEMU firmware config device */ @@ -142,7 +143,6 @@ enum uclass_id { UCLASS_W1, /* Dallas 1-Wire bus */ UCLASS_W1_EEPROM, /* one-wire EEPROMs */ UCLASS_WDT, /* Watchdog Timer driver */ - UCLASS_PVBLOCK, /* Xen virtual block device */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/dm/util.h b/include/dm/util.h index e10c6060ce0..4bb49e9e8c0 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -26,8 +26,12 @@ struct list_head; */ int list_count_items(struct list_head *head); -/* Dump out a tree of all devices */ -void dm_dump_tree(void); +/** + * Dump out a tree of all devices + * + * @sort: Sort by uclass name + */ +void dm_dump_tree(bool sort); /* Dump out a list of uclasses and their devices */ void dm_dump_uclass(void); diff --git a/include/net.h b/include/net.h index ee08f3307ec..399af5e0645 100644 --- a/include/net.h +++ b/include/net.h @@ -66,6 +66,21 @@ struct in_addr { int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); /** + * dhcp_run() - Run DHCP on the current ethernet device + * + * This sets the autoload variable, then puts it back to similar to its original + * state (y, n or unset). + * + * @addr: Address to load the file into (0 if @autoload is false) + * @fname: Filename of file to load (NULL if @autoload is false or to use the + * default filename) + * @autoload: true to load the file, false to just get the network IP + * @return 0 if OK, -EINVAL if the environment failed, -ENOENT if ant file was + * not found + */ +int dhcp_run(ulong addr, const char *fname, bool autoload); + +/** * An incoming packet handler. * @param pkt pointer to the application packet * @param dport destination UDP port @@ -886,4 +901,20 @@ static inline struct in_addr env_get_ip(char *var) */ void reset_phy(void); +#if CONFIG_IS_ENABLED(NET) +/** + * eth_set_enable_bootdevs() - Enable or disable binding of Ethernet bootdevs + * + * These get in the way of bootstd testing, so are normally disabled by tests. + * This provide control of this setting. It only affects binding of Ethernet + * devices, so if that has already happened, this flag does nothing. + * + * @enable: true to enable binding of bootdevs when binding new Ethernet + * devices, false to disable it + */ +void eth_set_enable_bootdevs(bool enable); +#else +static inline void eth_set_enable_bootdevs(bool enable) {} +#endif + #endif /* __NET_H__ */ diff --git a/include/part.h b/include/part.h index 807370d9429..be75c735495 100644 --- a/include/part.h +++ b/include/part.h @@ -303,6 +303,14 @@ part_get_info_by_dev_and_name_or_num(const char *dev_iface, } #endif +/** + * part_get_bootable() - Find the first bootable partition + * + * @desc: Block-device descriptor + * @return first bootable partition, or 0 if there is none + */ +int part_get_bootable(struct blk_desc *desc); + struct udevice; /** * part_create_block_devices - Create block devices for disk partitions diff --git a/include/test/test.h b/include/test/test.h index 4ad74614afc..838e3ce8a8f 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -71,6 +71,8 @@ enum { * since it cannot access the flags. */ UT_TESTF_MANUAL = BIT(8), + UT_TESTF_ETH_BOOTDEV = BIT(9), /* enable Ethernet bootdevs */ + UT_TESTF_SF_BOOTDEV = BIT(10), /* enable SPI flash bootdevs */ }; /** @@ -169,4 +171,74 @@ static inline int test_load_other_fdt(struct unit_test_state *uts) return ret; } +/** + * Control skipping of time delays + * + * Some tests have unnecessay time delays (e.g. USB). Allow these to be + * skipped to speed up testing + * + * @param skip_delays true to skip delays from now on, false to honour delay + * requests + */ +static inline void test_set_skip_delays(bool skip_delays) +{ +#ifdef CONFIG_SANDBOX + state_set_skip_delays(skip_delays); +#endif +} + +/** + * test_set_eth_enable() - Enable / disable Ethernet + * + * Allows control of whether Ethernet packets are actually send/received + * + * @enable: true to enable Ethernet, false to disable + */ +static inline void test_set_eth_enable(bool enable) +{ +#ifdef CONFIG_SANDBOX + sandbox_set_eth_enable(enable); +#endif +} + +/* Allow ethernet to be disabled for testing purposes */ +static inline bool test_eth_enabled(void) +{ + bool enabled = true; + +#ifdef CONFIG_SANDBOX + enabled = sandbox_eth_enabled(); +#endif + return enabled; +} + +/* Allow ethernet bootdev to be ignored for testing purposes */ +static inline bool test_eth_bootdev_enabled(void) +{ + bool enabled = true; + +#ifdef CONFIG_SANDBOX + enabled = sandbox_eth_enabled(); +#endif + return enabled; +} + +/* Allow SPI flash bootdev to be ignored for testing purposes */ +static inline bool test_sf_bootdev_enabled(void) +{ + bool enabled = true; + +#ifdef CONFIG_SANDBOX + enabled = sandbox_sf_bootdev_enabled(); +#endif + return enabled; +} + +static inline void test_sf_set_enable_bootdevs(bool enable) +{ +#ifdef CONFIG_SANDBOX + sandbox_sf_set_enable_bootdevs(enable); +#endif +} + #endif /* __TEST_TEST_H */ diff --git a/include/vsprintf.h b/include/vsprintf.h index e006af200fd..ed8a060ee17 100644 --- a/include/vsprintf.h +++ b/include/vsprintf.h @@ -329,6 +329,30 @@ char *strmhz(char *buf, unsigned long hz); void str_to_upper(const char *in, char *out, size_t len); /** + * str_to_list() - Convert a string to a list of string pointers + * + * Splits a string containing space-delimited substrings into a number of + * separate strings, e.g. "this is" becomes {"this", "is", NULL}. If @instr is + * empty then this returns just {NULL}. The string should have only a single + * space between items, with no leading or trailing spaces. + * + * @instr: String to process (this is alloced by this function) + * Returns: List of string pointers, terminated by NULL. Each entry points to + * a string. If @instr is empty, the list consists just of a single NULL entry. + * Note that the first entry points to the alloced string. + * Returns NULL if out of memory + */ +const char **str_to_list(const char *instr); + +/** + * str_free_list() - Free a string list + * + * @ptr: String list to free, as created by str_to_list(). This can also be + * NULL, in which case the function does nothing + */ +void str_free_list(const char **ptr); + +/** * vsscanf - Unformat a buffer into a list of arguments * @inp: input buffer * @fmt0: format of buffer diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index d170a864605..d2256713a8e 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -424,13 +424,16 @@ static efi_status_t efi_disk_add_dev( if (!node) { ret = EFI_OUT_OF_RESOURCES; + log_debug("no node\n"); goto error; } /* Parent must expose EFI_BLOCK_IO_PROTOCOL */ ret = efi_search_protocol(parent, &efi_block_io_guid, &handler); - if (ret != EFI_SUCCESS) + if (ret != EFI_SUCCESS) { + log_debug("search failed\n"); goto error; + } /* * Link the partition (child controller) to the block device @@ -439,8 +442,10 @@ static efi_status_t efi_disk_add_dev( ret = efi_protocol_open(handler, &protocol_interface, NULL, &diskobj->header, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER); - if (ret != EFI_SUCCESS) - goto error; + if (ret != EFI_SUCCESS) { + log_debug("prot open failed\n"); + goto error; + } diskobj->dp = efi_dp_append_node(dp_parent, node); efi_free_pool(node); @@ -471,8 +476,10 @@ static efi_status_t efi_disk_add_dev( */ esp_guid, NULL, NULL); - if (ret != EFI_SUCCESS) + if (ret != EFI_SUCCESS) { + log_debug("install failed %lx\n", ret); goto error; + } /* * On partitions or whole disks without partitions install the @@ -485,8 +492,10 @@ static efi_status_t efi_disk_add_dev( ret = efi_add_protocol(&diskobj->header, &efi_simple_file_system_protocol_guid, diskobj->volume); - if (ret != EFI_SUCCESS) + if (ret != EFI_SUCCESS) { + log_debug("simple FS failed\n"); return ret; + } } diskobj->ops = block_io_disk_template; diskobj->dev_index = dev_index; @@ -556,18 +565,21 @@ static int efi_disk_create_raw(struct udevice *dev, efi_handle_t agent_handle) ret = efi_disk_add_dev(NULL, NULL, desc, diskid, NULL, 0, &disk, agent_handle); if (ret != EFI_SUCCESS) { - if (ret == EFI_NOT_READY) + if (ret == EFI_NOT_READY) { log_notice("Disk %s not ready\n", dev->name); - else + ret = -EBUSY; + } else { log_err("Adding disk for %s failed (err=%ld/%#lx)\n", dev->name, ret, ret); + ret = -ENOENT; + } - return -1; + return ret; } if (efi_link_dev(&disk->header, dev)) { efi_free_pool(disk->dp); efi_delete_handle(&disk->header); - return -1; + return -EINVAL; } return 0; diff --git a/lib/strto.c b/lib/strto.c index 6462d4fddf1..154921165cb 100644 --- a/lib/strto.c +++ b/lib/strto.c @@ -11,6 +11,7 @@ #include <common.h> #include <errno.h> +#include <malloc.h> #include <linux/ctype.h> /* from lib/kstrtox.c */ @@ -222,3 +223,43 @@ void str_to_upper(const char *in, char *out, size_t len) if (len) *out = '\0'; } + +const char **str_to_list(const char *instr) +{ + const char **ptr; + char *str, *p; + int count, i; + + /* don't allocate if the string is empty */ + str = *instr ? strdup(instr) : (char *)instr; + if (!str) + return NULL; + + /* count the number of space-separated strings */ + for (count = *str != '\0', p = str; *p; p++) { + if (*p == ' ') { + count++; + *p = '\0'; + } + } + + /* allocate the pointer array, allowing for a NULL terminator */ + ptr = calloc(count + 1, sizeof(char *)); + if (!ptr) { + if (*str) + free(str); + return NULL; + } + + for (i = 0, p = str; i < count; p += strlen(p) + 1, i++) + ptr[i] = p; + + return ptr; +} + +void str_free_list(const char **ptr) +{ + if (ptr) + free((char *)ptr[0]); + free(ptr); +} diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 530d8088c7f..8de3882fb6c 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -145,6 +145,7 @@ static noinline char *put_dec(char *buf, uint64_t num) #define LEFT 16 /* left justified */ #define SMALL 32 /* Must be 32 == 0x20 */ #define SPECIAL 64 /* 0x */ +#define ERRSTR 128 /* %dE showing error string if enabled */ /* * Macro to add a new character to our output string, but only if it will @@ -678,6 +679,8 @@ repeat: break; case 'd': + if (fmt[1] == 'E') + flags |= ERRSTR; case 'i': flags |= SIGN; case 'u': @@ -712,6 +715,15 @@ repeat: } str = number(str, end, num, base, field_width, precision, flags); + if (IS_ENABLED(CONFIG_ERRNO_STR) && (flags & ERRSTR)) { + const char *p; + + ADDCH(str, ':'); + ADDCH(str, ' '); + for (p = errno_str(num); *p; p++) + ADDCH(str, *p); + fmt++; + } } if (size > 0) { diff --git a/net/eth-uclass.c b/net/eth-uclass.c index f41da4b37b3..b01a910938e 100644 --- a/net/eth-uclass.c +++ b/net/eth-uclass.c @@ -38,9 +38,12 @@ struct eth_device_priv { * struct eth_uclass_priv - The structure attached to the uclass itself * * @current: The Ethernet device that the network functions are using + * @no_bootdevs: true to skip binding Ethernet bootdevs (this is a negative flag + * so that the default value enables it) */ struct eth_uclass_priv { struct udevice *current; + bool no_bootdevs; }; /* eth_errno - This stores the most recent failure code from DM functions */ @@ -59,6 +62,14 @@ static struct eth_uclass_priv *eth_get_uclass_priv(void) return uclass_get_priv(uc); } +void eth_set_enable_bootdevs(bool enable) +{ + struct eth_uclass_priv *priv = eth_get_uclass_priv(); + + if (priv) + priv->no_bootdevs = !enable; +} + void eth_set_current_to_next(void) { struct eth_uclass_priv *uc_priv; @@ -477,6 +488,7 @@ int eth_initialize(void) static int eth_post_bind(struct udevice *dev) { + struct eth_uclass_priv *priv = uclass_get_priv(dev->uclass); int ret; if (strchr(dev->name, ' ')) { @@ -488,7 +500,7 @@ static int eth_post_bind(struct udevice *dev) #ifdef CONFIG_DM_ETH_PHY eth_phy_binds_nodes(dev); #endif - if (CONFIG_IS_ENABLED(BOOTDEV_ETH)) { + if (CONFIG_IS_ENABLED(BOOTDEV_ETH) && !priv->no_bootdevs) { ret = bootdev_setup_for_dev(dev, "eth_bootdev"); if (ret) return log_msg_ret("bootdev", ret); diff --git a/net/eth_bootdev.c b/net/eth_bootdev.c index b735966d2bc..13e5fcd3bdf 100644 --- a/net/eth_bootdev.c +++ b/net/eth_bootdev.c @@ -6,6 +6,8 @@ * Written by Simon Glass <sjg@chromium.org> */ +#define LOG_CATEGORY UCLASS_BOOTSTD + #include <common.h> #include <bootdev.h> #include <bootflow.h> @@ -13,8 +15,10 @@ #include <bootmeth.h> #include <distro.h> #include <dm.h> +#include <init.h> #include <log.h> #include <net.h> +#include <test/test.h> static int eth_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, struct bootflow *bflow) @@ -23,7 +27,7 @@ static int eth_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, int ret; /* Must be an Ethernet device */ - ret = bootflow_iter_uses_network(iter); + ret = bootflow_iter_check_net(iter); if (ret) return log_msg_ret("net", ret); @@ -41,30 +45,8 @@ static int eth_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, if (!bflow->name) return log_msg_ret("name", -ENOMEM); - /* - * There is not a direct interface to the network stack so run - * everything through the command-line interpreter for now. - * - * Don't bother checking the result of dhcp. It can fail with: - * - * DHCP client bound to address 192.168.4.50 (4 ms) - * *** Warning: no boot file name; using 'C0A80432.img' - * Using smsc95xx_eth device - * TFTP from server 192.168.4.1; our IP address is 192.168.4.50 - * Filename 'C0A80432.img'. - * Load address: 0x200000 - * Loading: * - * TFTP error: 'File not found' (1) - * - * This is not a real failure, since we don't actually care if the - * boot file exists. - */ - log_debug("running dhcp\n"); - run_command("dhcp", 0); - bflow->state = BOOTFLOWST_MEDIA; - /* See distro_pxe_read_bootflow() for the standard impl of this */ - log_debug("dhcp complete - reading bootflow with method %s\n", + log_debug("dhcp complete - reading bootflow with method '%s'\n", bflow->method->name); ret = bootmeth_read_bootflow(bflow->method, bflow); log_debug("reading bootflow returned %d\n", ret); @@ -78,7 +60,36 @@ static int eth_bootdev_bind(struct udevice *dev) { struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); - ucp->prio = BOOTDEVP_4_NET_BASE; + ucp->prio = BOOTDEVP_6_NET_BASE; + + return 0; +} + +static int eth_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + int ret; + + if (!test_eth_enabled()) + return 0; + + /* init PCI first since this is often used to provide Ehternet */ + if (IS_ENABLED(CONFIG_PCI)) { + ret = pci_init(); + if (ret) + log_warning("Failed to init PCI (%dE)\n", ret); + } + + /* + * Ethernet devices can also come from USB, but that is a higher + * priority (BOOTDEVP_5_SCAN_SLOW) than ethernet, so it should have been + * enumerated already. If something like 'bootflow scan dhcp' is used + * then the user will need to run 'usb start' first. + */ + if (IS_ENABLED(CONFIG_CMD_DHCP)) { + ret = dhcp_run(0, NULL, false); + if (ret) + return -EINVAL; + } return 0; } @@ -99,3 +110,10 @@ U_BOOT_DRIVER(eth_bootdev) = { .bind = eth_bootdev_bind, .of_match = eth_bootdev_ids, }; + +BOOTDEV_HUNTER(eth_bootdev_hunt) = { + .prio = BOOTDEVP_6_NET_BASE, + .uclass = UCLASS_ETH, + .hunt = eth_bootdev_hunt, + .drv = DM_DRIVER_REF(eth_bootdev), +}; diff --git a/net/net.c b/net/net.c index 57da9bda85a..c9a749f6cc8 100644 --- a/net/net.c +++ b/net/net.c @@ -106,6 +106,7 @@ #endif #include <watchdog.h> #include <linux/compiler.h> +#include <test/test.h> #include "arp.h" #include "bootp.h" #include "cdp.h" @@ -465,6 +466,9 @@ restart: debug_cond(DEBUG_INT_STATE, "--- net_loop Init\n"); net_init_loop(); + if (!test_eth_enabled()) + return 0; + switch (net_check_prereq(protocol)) { case 1: /* network not configured */ diff --git a/test/boot/bootdev.c b/test/boot/bootdev.c index 1c2a79fb108..ef5215bbcec 100644 --- a/test/boot/bootdev.c +++ b/test/boot/bootdev.c @@ -102,22 +102,89 @@ BOOTSTD_TEST(bootdev_test_cmd_select, UT_TESTF_DM | UT_TESTF_SCAN_FDT); static int bootdev_test_labels(struct unit_test_state *uts) { struct udevice *dev, *media; + int mflags = 0; - ut_assertok(bootdev_find_by_label("mmc2", &dev)); + ut_assertok(bootdev_find_by_label("mmc2", &dev, &mflags)); ut_asserteq(UCLASS_BOOTDEV, device_get_uclass_id(dev)); + ut_asserteq(0, mflags); media = dev_get_parent(dev); ut_asserteq(UCLASS_MMC, device_get_uclass_id(media)); ut_asserteq_str("mmc2", media->name); + /* Check method flags */ + ut_assertok(bootdev_find_by_label("pxe", &dev, &mflags)); + ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_PXE_ONLY, + mflags); + ut_assertok(bootdev_find_by_label("dhcp", &dev, &mflags)); + ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_DHCP_ONLY, + mflags); + /* Check invalid uclass */ - ut_asserteq(-EINVAL, bootdev_find_by_label("fred0", &dev)); + ut_asserteq(-EINVAL, bootdev_find_by_label("fred0", &dev, &mflags)); /* Check unknown sequence number */ - ut_asserteq(-ENOENT, bootdev_find_by_label("mmc6", &dev)); + ut_asserteq(-ENOENT, bootdev_find_by_label("mmc6", &dev, &mflags)); + + return 0; +} +BOOTSTD_TEST(bootdev_test_labels, UT_TESTF_DM | UT_TESTF_SCAN_FDT | + UT_TESTF_ETH_BOOTDEV); + +/* Check bootdev_find_by_any() */ +static int bootdev_test_any(struct unit_test_state *uts) +{ + struct udevice *dev, *media; + int mflags; + + /* + * with ethernet enabled we have 8 devices ahead of the mmc ones: + * + * ut_assertok(run_command("bootdev list", 0)); + * Seq Probed Status Uclass Name + * --- ------ ------ -------- ------------------ + * 0 [ + ] OK ethernet eth@10002000.bootdev + * 1 [ ] OK ethernet eth@10003000.bootdev + * 2 [ ] OK ethernet sbe5.bootdev + * 3 [ ] OK ethernet eth@10004000.bootdev + * 4 [ ] OK ethernet phy-test-eth.bootdev + * 5 [ ] OK ethernet dsa-test-eth.bootdev + * 6 [ ] OK ethernet dsa-test@0.bootdev + * 7 [ ] OK ethernet dsa-test@1.bootdev + * 8 [ ] OK mmc mmc2.bootdev + * 9 [ + ] OK mmc mmc1.bootdev + * a [ ] OK mmc mmc0.bootdev + */ + console_record_reset_enable(); + ut_assertok(bootdev_find_by_any("8", &dev, &mflags)); + ut_asserteq(UCLASS_BOOTDEV, device_get_uclass_id(dev)); + ut_asserteq(BOOTFLOW_METHF_SINGLE_DEV, mflags); + media = dev_get_parent(dev); + ut_asserteq(UCLASS_MMC, device_get_uclass_id(media)); + ut_asserteq_str("mmc2", media->name); + ut_assert_console_end(); + + /* there should not be this many bootdevs */ + ut_asserteq(-ENODEV, bootdev_find_by_any("50", &dev, &mflags)); + ut_assert_nextline("Cannot find '50' (err=-19)"); + ut_assert_console_end(); + + /* Check method flags */ + ut_assertok(bootdev_find_by_any("pxe", &dev, &mflags)); + ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_PXE_ONLY, + mflags); + + /* Check invalid uclass */ + mflags = 123; + ut_asserteq(-EINVAL, bootdev_find_by_any("fred0", &dev, &mflags)); + ut_assert_nextline("Unknown uclass 'fred0' in label"); + ut_assert_nextline("Cannot find bootdev 'fred0' (err=-22)"); + ut_asserteq(123, mflags); + ut_assert_console_end(); return 0; } -BOOTSTD_TEST(bootdev_test_labels, UT_TESTF_DM | UT_TESTF_SCAN_FDT); +BOOTSTD_TEST(bootdev_test_any, UT_TESTF_DM | UT_TESTF_SCAN_FDT | + UT_TESTF_ETH_BOOTDEV); /* Check bootdev ordering with the bootdev-order property */ static int bootdev_test_order(struct unit_test_state *uts) @@ -135,20 +202,31 @@ static int bootdev_test_order(struct unit_test_state *uts) * mmc2 - nothing connected */ ut_assertok(env_set("boot_targets", NULL)); - ut_assertok(bootflow_scan_first(&iter, 0, &bflow)); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); ut_asserteq(2, iter.num_devs); - ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name); - ut_asserteq_str("mmc1.bootdev", iter.dev_order[1]->name); + ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name); + ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name); bootflow_iter_uninit(&iter); /* Use the environment variable to override it */ ut_assertok(env_set("boot_targets", "mmc1 mmc2")); - ut_assertok(bootflow_scan_first(&iter, 0, &bflow)); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); + ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); ut_asserteq(2, iter.num_devs); - ut_asserteq_str("mmc1.bootdev", iter.dev_order[0]->name); - ut_asserteq_str("mmc2.bootdev", iter.dev_order[1]->name); + ut_asserteq_str("mmc1.bootdev", iter.dev_used[0]->name); + ut_asserteq_str("mmc2.bootdev", iter.dev_used[1]->name); bootflow_iter_uninit(&iter); + return 0; +} +BOOTSTD_TEST(bootdev_test_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check default bootdev ordering */ +static int bootdev_test_order_default(struct unit_test_state *uts) +{ + struct bootflow_iter iter; + struct bootflow bflow; + /* * Now drop both orderings, to check the default (prioriy/sequence) * ordering @@ -156,31 +234,19 @@ static int bootdev_test_order(struct unit_test_state *uts) ut_assertok(env_set("boot_targets", NULL)); ut_assertok(bootstd_test_drop_bootdev_order(uts)); - ut_assertok(bootflow_scan_first(&iter, 0, &bflow)); - ut_asserteq(3, iter.num_devs); - ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name); - ut_asserteq_str("mmc1.bootdev", iter.dev_order[1]->name); - ut_asserteq_str("mmc0.bootdev", iter.dev_order[2]->name); - - /* - * Check that adding aliases for the bootdevs works. We just fake it by - * setting the sequence numbers directly. - */ - iter.dev_order[0]->seq_ = 0; - iter.dev_order[1]->seq_ = 3; - iter.dev_order[2]->seq_ = 2; - bootflow_iter_uninit(&iter); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); + ut_asserteq(2, iter.num_devs); + ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name); + ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name); - ut_assertok(bootflow_scan_first(&iter, 0, &bflow)); + ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); ut_asserteq(3, iter.num_devs); - ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name); - ut_asserteq_str("mmc0.bootdev", iter.dev_order[1]->name); - ut_asserteq_str("mmc1.bootdev", iter.dev_order[2]->name); + ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name); bootflow_iter_uninit(&iter); return 0; } -BOOTSTD_TEST(bootdev_test_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT); +BOOTSTD_TEST(bootdev_test_order_default, UT_TESTF_DM | UT_TESTF_SCAN_FDT); /* Check bootdev ordering with the uclass priority */ static int bootdev_test_prio(struct unit_test_state *uts) @@ -190,6 +256,11 @@ static int bootdev_test_prio(struct unit_test_state *uts) struct bootflow bflow; struct udevice *blk; + test_set_skip_delays(true); + + /* disable ethernet since the hunter will run dhcp */ + test_set_eth_enable(false); + /* Start up USB which gives us three additional bootdevs */ usb_started = false; ut_assertok(run_command("usb start", 0)); @@ -198,26 +269,440 @@ static int bootdev_test_prio(struct unit_test_state *uts) /* 3 MMC and 3 USB bootdevs: MMC should come before USB */ console_record_reset_enable(); - ut_assertok(bootflow_scan_first(&iter, 0, &bflow)); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); + ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); ut_asserteq(6, iter.num_devs); - ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name); + ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name); ut_asserteq_str("usb_mass_storage.lun0.bootdev", - iter.dev_order[3]->name); + iter.dev_used[3]->name); - ut_assertok(bootdev_get_sibling_blk(iter.dev_order[3], &blk)); + ut_assertok(bootdev_get_sibling_blk(iter.dev_used[3], &blk)); ut_asserteq_str("usb_mass_storage.lun0", blk->name); /* adjust the priority of the first USB bootdev to the highest */ - ucp = dev_get_uclass_plat(iter.dev_order[3]); - ucp->prio = 1; + ucp = dev_get_uclass_plat(iter.dev_used[3]); + ucp->prio = BOOTDEVP_1_PRE_SCAN; + /* try again but enable hunting, which brings in SCSI */ bootflow_iter_uninit(&iter); - ut_assertok(bootflow_scan_first(&iter, 0, &bflow)); - ut_asserteq(6, iter.num_devs); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, BOOTFLOWF_HUNT, + &bflow)); + ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow)); + ut_asserteq(7, iter.num_devs); ut_asserteq_str("usb_mass_storage.lun0.bootdev", - iter.dev_order[0]->name); - ut_asserteq_str("mmc2.bootdev", iter.dev_order[1]->name); + iter.dev_used[0]->name); + ut_asserteq_str("mmc2.bootdev", iter.dev_used[1]->name); return 0; } BOOTSTD_TEST(bootdev_test_prio, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check listing hunters */ +static int bootdev_test_hunter(struct unit_test_state *uts) +{ + struct bootstd_priv *std; + + test_set_skip_delays(true); + + /* get access to the used hunters */ + ut_assertok(bootstd_get_priv(&std)); + + console_record_reset_enable(); + bootdev_list_hunters(std); + ut_assert_nextline("Prio Used Uclass Hunter"); + ut_assert_nextlinen("----"); + ut_assert_nextline(" 6 ethernet eth_bootdev"); + ut_assert_nextline(" 1 simple_bus (none)"); + ut_assert_nextline(" 5 ide ide_bootdev"); + ut_assert_nextline(" 2 mmc mmc_bootdev"); + ut_assert_nextline(" 4 nvme nvme_bootdev"); + ut_assert_nextline(" 4 scsi scsi_bootdev"); + ut_assert_nextline(" 4 spi_flash sf_bootdev"); + ut_assert_nextline(" 5 usb usb_bootdev"); + ut_assert_nextline(" 4 virtio virtio_bootdev"); + ut_assert_nextline("(total hunters: 9)"); + ut_assert_console_end(); + + ut_assertok(bootdev_hunt("usb1", false)); + ut_assert_nextline( + "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found"); + ut_assert_console_end(); + + /* USB is sixth in the list, so bit 7 */ + ut_asserteq(BIT(7), std->hunters_used); + + return 0; +} +BOOTSTD_TEST(bootdev_test_hunter, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check 'bootdev hunt' command */ +static int bootdev_test_cmd_hunt(struct unit_test_state *uts) +{ + struct bootstd_priv *std; + + test_set_skip_delays(true); + + /* get access to the used hunters */ + ut_assertok(bootstd_get_priv(&std)); + + console_record_reset_enable(); + ut_assertok(run_command("bootdev hunt -l", 0)); + ut_assert_nextline("Prio Used Uclass Hunter"); + ut_assert_nextlinen("----"); + ut_assert_nextline(" 6 ethernet eth_bootdev"); + ut_assert_skip_to_line("(total hunters: 9)"); + ut_assert_console_end(); + + /* Use the MMC hunter and see that it updates */ + ut_assertok(run_command("bootdev hunt mmc", 0)); + ut_assertok(run_command("bootdev hunt -l", 0)); + ut_assert_skip_to_line(" 5 ide ide_bootdev"); + ut_assert_nextline(" 2 * mmc mmc_bootdev"); + ut_assert_skip_to_line("(total hunters: 9)"); + ut_assert_console_end(); + + /* Scan all hunters */ + test_set_eth_enable(false); + test_set_skip_delays(true); + ut_assertok(run_command("bootdev hunt", 0)); + ut_assert_nextline("Hunting with: ethernet"); + + /* This is the extension feature which has no uclass at present */ + ut_assert_nextline("Hunting with: simple_bus"); + ut_assert_nextline("Found 2 extension board(s)."); + ut_assert_nextline("Hunting with: ide"); + ut_assert_nextline("Bus 0: not available "); + + /* mmc hunter has already been used so should not run again */ + + ut_assert_nextline("Hunting with: nvme"); + ut_assert_nextline("Hunting with: scsi"); + ut_assert_nextline("scanning bus for devices..."); + ut_assert_skip_to_line("Hunting with: spi_flash"); + ut_assert_nextline("Hunting with: usb"); + ut_assert_nextline( + "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found"); + ut_assert_nextline("Hunting with: virtio"); + ut_assert_console_end(); + + /* List available hunters */ + ut_assertok(run_command("bootdev hunt -l", 0)); + ut_assert_nextlinen("Prio"); + ut_assert_nextlinen("----"); + ut_assert_nextline(" 6 * ethernet eth_bootdev"); + ut_assert_nextline(" 1 * simple_bus (none)"); + ut_assert_nextline(" 5 * ide ide_bootdev"); + ut_assert_nextline(" 2 * mmc mmc_bootdev"); + ut_assert_nextline(" 4 * nvme nvme_bootdev"); + ut_assert_nextline(" 4 * scsi scsi_bootdev"); + ut_assert_nextline(" 4 * spi_flash sf_bootdev"); + ut_assert_nextline(" 5 * usb usb_bootdev"); + ut_assert_nextline(" 4 * virtio virtio_bootdev"); + ut_assert_nextline("(total hunters: 9)"); + ut_assert_console_end(); + + ut_asserteq(GENMASK(MAX_HUNTER, 0), std->hunters_used); + + return 0; +} +BOOTSTD_TEST(bootdev_test_cmd_hunt, UT_TESTF_DM | UT_TESTF_SCAN_FDT | + UT_TESTF_ETH_BOOTDEV); + +/* Check searching for bootdevs using the hunters */ +static int bootdev_test_hunt_scan(struct unit_test_state *uts) +{ + struct bootflow_iter iter; + struct bootstd_priv *std; + struct bootflow bflow; + + /* get access to the used hunters */ + ut_assertok(bootstd_get_priv(&std)); + + ut_assertok(bootstd_test_drop_bootdev_order(uts)); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, + BOOTFLOWF_SHOW | BOOTFLOWF_HUNT | + BOOTFLOWF_SKIP_GLOBAL, &bflow)); + ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used); + + return 0; +} +BOOTSTD_TEST(bootdev_test_hunt_scan, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check that only bootable partitions are processed */ +static int bootdev_test_bootable(struct unit_test_state *uts) +{ + struct bootflow_iter iter; + struct bootflow bflow; + struct udevice *blk; + + memset(&iter, '\0', sizeof(iter)); + memset(&bflow, '\0', sizeof(bflow)); + iter.part = 0; + ut_assertok(uclass_get_device_by_name(UCLASS_BLK, "mmc1.blk", &blk)); + iter.dev = blk; + ut_assertok(device_find_next_child(&iter.dev)); + uclass_first_device(UCLASS_BOOTMETH, &bflow.method); + + /* + * initially we don't have any knowledge of which partitions are + * bootable, but mmc1 has two partitions, with the first one being + * bootable + */ + iter.part = 2; + ut_asserteq(-EINVAL, bootdev_find_in_blk(iter.dev, blk, &iter, &bflow)); + ut_asserteq(0, iter.first_bootable); + + /* scan with part == 0 to get the partition info */ + iter.part = 0; + ut_asserteq(-ENOENT, bootdev_find_in_blk(iter.dev, blk, &iter, &bflow)); + ut_asserteq(1, iter.first_bootable); + + /* now it will refuse to use non-bootable partitions */ + iter.part = 2; + ut_asserteq(-EINVAL, bootdev_find_in_blk(iter.dev, blk, &iter, &bflow)); + + return 0; +} +BOOTSTD_TEST(bootdev_test_bootable, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check hunting for bootdev of a particular priority */ +static int bootdev_test_hunt_prio(struct unit_test_state *uts) +{ + test_set_skip_delays(true); + + console_record_reset_enable(); + ut_assertok(bootdev_hunt_prio(BOOTDEVP_4_SCAN_FAST, false)); + ut_assert_nextline("scanning bus for devices..."); + ut_assert_skip_to_line(" Type: Hard Disk"); + ut_assert_nextlinen(" Capacity:"); + ut_assert_console_end(); + + /* now try a different priority, verbosely */ + ut_assertok(bootdev_hunt_prio(BOOTDEVP_5_SCAN_SLOW, true)); + ut_assert_nextline("Hunting with: ide"); + ut_assert_nextline("Bus 0: not available "); + ut_assert_nextline("Hunting with: usb"); + ut_assert_nextline( + "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootdev_test_hunt_prio, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check hunting for bootdevs with a particular label */ +static int bootdev_test_hunt_label(struct unit_test_state *uts) +{ + struct udevice *dev, *old; + struct bootstd_priv *std; + int mflags; + + /* get access to the used hunters */ + ut_assertok(bootstd_get_priv(&std)); + + /* scan an unknown uclass */ + console_record_reset_enable(); + old = (void *)&mflags; /* arbitrary pointer to check against dev */ + dev = old; + mflags = 123; + ut_asserteq(-EINVAL, + bootdev_hunt_and_find_by_label("fred", &dev, &mflags)); + ut_assert_nextline("Unknown uclass 'fred' in label"); + ut_asserteq_ptr(old, dev); + ut_asserteq(123, mflags); + ut_assert_console_end(); + ut_asserteq(0, std->hunters_used); + + /* scan an invalid mmc controllers */ + ut_asserteq(-ENOENT, + bootdev_hunt_and_find_by_label("mmc4", &dev, &mflags)); + ut_asserteq_ptr(old, dev); + ut_asserteq(123, mflags); + ut_assert_nextline("Unknown seq 4 for label 'mmc4'"); + ut_assert_console_end(); + + ut_assertok(bootstd_test_check_mmc_hunter(uts)); + + /* scan for a particular mmc controller */ + ut_assertok(bootdev_hunt_and_find_by_label("mmc1", &dev, &mflags)); + ut_assertnonnull(dev); + ut_asserteq_str("mmc1.bootdev", dev->name); + ut_asserteq(0, mflags); + ut_assert_console_end(); + + /* scan all of usb */ + test_set_skip_delays(true); + ut_assertok(bootdev_hunt_and_find_by_label("usb", &dev, &mflags)); + ut_assertnonnull(dev); + ut_asserteq_str("usb_mass_storage.lun0.bootdev", dev->name); + ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS, mflags); + ut_assert_nextlinen("Bus usb@1: scanning bus usb@1"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootdev_test_hunt_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check iterating to the next label in a list */ +static int bootdev_test_next_label(struct unit_test_state *uts) +{ + const char *const labels[] = {"mmc0", "scsi", "dhcp", "pxe", NULL}; + struct bootflow_iter iter; + struct bootstd_priv *std; + struct bootflow bflow; + struct udevice *dev; + int mflags; + + test_set_eth_enable(false); + + /* get access to the used hunters */ + ut_assertok(bootstd_get_priv(&std)); + + memset(&iter, '\0', sizeof(iter)); + memset(&bflow, '\0', sizeof(bflow)); + iter.part = 0; + uclass_first_device(UCLASS_BOOTMETH, &bflow.method); + iter.cur_label = -1; + iter.labels = labels; + + dev = NULL; + mflags = 123; + ut_assertok(bootdev_next_label(&iter, &dev, &mflags)); + console_record_reset_enable(); + ut_assert_console_end(); + ut_assertnonnull(dev); + ut_asserteq_str("mmc0.bootdev", dev->name); + ut_asserteq(0, mflags); + + ut_assertok(bootstd_test_check_mmc_hunter(uts)); + + ut_assertok(bootdev_next_label(&iter, &dev, &mflags)); + ut_assert_nextline("scanning bus for devices..."); + ut_assert_skip_to_line( + " Capacity: 1.9 MB = 0.0 GB (4095 x 512)"); + ut_assert_console_end(); + ut_assertnonnull(dev); + ut_asserteq_str("scsi.id0lun0.bootdev", dev->name); + ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS, mflags); + + /* SCSI is sixth in the list, so bit 5 */ + ut_asserteq(BIT(MMC_HUNTER) | BIT(5), std->hunters_used); + + ut_assertok(bootdev_next_label(&iter, &dev, &mflags)); + ut_assert_console_end(); + ut_assertnonnull(dev); + ut_asserteq_str("eth@10002000.bootdev", dev->name); + ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_DHCP_ONLY, + mflags); + + /* dhcp: Ethernet is first so bit 0 */ + ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used); + + ut_assertok(bootdev_next_label(&iter, &dev, &mflags)); + ut_assert_console_end(); + ut_assertnonnull(dev); + ut_asserteq_str("eth@10002000.bootdev", dev->name); + ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_PXE_ONLY, + mflags); + + /* pxe: Ethernet is first so bit 0 */ + ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used); + + mflags = 123; + ut_asserteq(-ENODEV, bootdev_next_label(&iter, &dev, &mflags)); + ut_asserteq(123, mflags); + ut_assert_console_end(); + + /* no change */ + ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used); + + return 0; +} +BOOTSTD_TEST(bootdev_test_next_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT | + UT_TESTF_ETH_BOOTDEV | UT_TESTF_SF_BOOTDEV); + + +/* Check iterating to the next prioirty in a list */ +static int bootdev_test_next_prio(struct unit_test_state *uts) +{ + struct bootflow_iter iter; + struct bootstd_priv *std; + struct bootflow bflow; + struct udevice *dev; + int ret; + + test_set_eth_enable(false); + test_set_skip_delays(true); + + /* get access to the used hunters */ + ut_assertok(bootstd_get_priv(&std)); + + memset(&iter, '\0', sizeof(iter)); + memset(&bflow, '\0', sizeof(bflow)); + iter.part = 0; + uclass_first_device(UCLASS_BOOTMETH, &bflow.method); + iter.cur_prio = 0; + iter.flags = BOOTFLOWF_SHOW; + + dev = NULL; + console_record_reset_enable(); + ut_assertok(bootdev_next_prio(&iter, &dev)); + ut_assertnonnull(dev); + ut_asserteq_str("mmc2.bootdev", dev->name); + + /* hunt flag not set, so this should not use any hunters */ + ut_asserteq(0, std->hunters_used); + ut_assert_console_end(); + + /* now try again with hunting enabled */ + iter.flags = BOOTFLOWF_SHOW | BOOTFLOWF_HUNT; + iter.cur_prio = 0; + iter.part = 0; + + ut_assertok(bootdev_next_prio(&iter, &dev)); + ut_asserteq_str("mmc2.bootdev", dev->name); + ut_assert_nextline("Hunting with: simple_bus"); + ut_assert_nextline("Found 2 extension board(s)."); + ut_assert_nextline("Hunting with: mmc"); + ut_assert_console_end(); + + ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used); + + ut_assertok(bootdev_next_prio(&iter, &dev)); + ut_asserteq_str("mmc1.bootdev", dev->name); + + ut_assertok(bootdev_next_prio(&iter, &dev)); + ut_asserteq_str("mmc0.bootdev", dev->name); + ut_assert_console_end(); + + ut_assertok(bootdev_next_prio(&iter, &dev)); + ut_asserteq_str("spi.bin@0.bootdev", dev->name); + ut_assert_skip_to_line("Hunting with: spi_flash"); + + /* + * this scans all bootdevs of priority BOOTDEVP_4_SCAN_FAST before it + * starts looking at the devices, so we se virtio as well + */ + ut_assert_nextline("Hunting with: virtio"); + ut_assert_nextlinen("SF: Detected m25p16"); + + ut_assertok(bootdev_next_prio(&iter, &dev)); + ut_asserteq_str("spi.bin@1.bootdev", dev->name); + ut_assert_nextlinen("SF: Detected m25p16"); + ut_assert_console_end(); + + /* keep going until there are no more bootdevs */ + do { + ret = bootdev_next_prio(&iter, &dev); + } while (!ret); + ut_asserteq(-ENODEV, ret); + ut_assertnull(dev); + ut_asserteq(GENMASK(MAX_HUNTER, 0), std->hunters_used); + + ut_assert_skip_to_line("Hunting with: ethernet"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootdev_test_next_prio, UT_TESTF_DM | UT_TESTF_SCAN_FDT | + UT_TESTF_SF_BOOTDEV); diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 5b76cd3ab14..b9284fc464a 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -51,11 +51,14 @@ static int bootflow_cmd(struct unit_test_state *uts) console_record_reset_enable(); ut_assertok(run_command("bootdev select 1", 0)); ut_assert_console_end(); - ut_assertok(run_command("bootflow scan -l", 0)); + ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'"); ut_assert_nextline("Seq Method State Uclass Part Name Filename"); ut_assert_nextlinen("---"); + 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("No more bootdevs"); ut_assert_nextlinen("---"); ut_assert_nextline("(1 bootflow, 1 valid)"); ut_assert_console_end(); @@ -73,28 +76,53 @@ static int bootflow_cmd(struct unit_test_state *uts) } BOOTSTD_TEST(bootflow_cmd, UT_TESTF_DM | UT_TESTF_SCAN_FDT); -/* Check 'bootflow scan' with a name / label / seq */ +/* Check 'bootflow scan' with a label / seq */ static int bootflow_cmd_label(struct unit_test_state *uts) { + test_set_eth_enable(false); + console_record_reset_enable(); - ut_assertok(run_command("bootflow scan -l mmc1", 0)); - ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'"); + ut_assertok(run_command("bootflow scan -lH mmc1", 0)); + ut_assert_nextline("Scanning for bootflows with label 'mmc1'"); ut_assert_skip_to_line("(1 bootflow, 1 valid)"); ut_assert_console_end(); - ut_assertok(run_command("bootflow scan -l mmc0.bootdev", 0)); - ut_assert_nextline("Scanning for bootflows in bootdev 'mmc0.bootdev'"); + ut_assertok(run_command("bootflow scan -lH 0", 0)); + ut_assert_nextline("Scanning for bootflows with label '0'"); ut_assert_skip_to_line("(0 bootflows, 0 valid)"); ut_assert_console_end(); - ut_assertok(run_command("bootflow scan -l 0", 0)); - ut_assert_nextline("Scanning for bootflows in bootdev 'mmc2.bootdev'"); + /* + * with ethernet enabled we have 8 devices ahead of the mmc ones: + * + * ut_assertok(run_command("bootdev list", 0)); + * Seq Probed Status Uclass Name + * --- ------ ------ -------- ------------------ + * 0 [ + ] OK ethernet eth@10002000.bootdev + * 1 [ ] OK ethernet eth@10003000.bootdev + * 2 [ ] OK ethernet sbe5.bootdev + * 3 [ ] OK ethernet eth@10004000.bootdev + * 4 [ ] OK ethernet phy-test-eth.bootdev + * 5 [ ] OK ethernet dsa-test-eth.bootdev + * 6 [ ] OK ethernet dsa-test@0.bootdev + * 7 [ ] OK ethernet dsa-test@1.bootdev + * 8 [ ] OK mmc mmc2.bootdev + * 9 [ + ] OK mmc mmc1.bootdev + * a [ ] OK mmc mmc0.bootdev + */ + ut_assertok(run_command("bootflow scan -lH 9", 0)); + ut_assert_nextline("Scanning for bootflows with label '9'"); + ut_assert_skip_to_line("(1 bootflow, 1 valid)"); + + ut_assertok(run_command("bootflow scan -lH 0", 0)); + ut_assert_nextline("Scanning for bootflows with label '0'"); ut_assert_skip_to_line("(0 bootflows, 0 valid)"); ut_assert_console_end(); return 0; } -BOOTSTD_TEST(bootflow_cmd_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT); +BOOTSTD_TEST(bootflow_cmd_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT | + UT_TESTF_ETH_BOOTDEV); /* Check 'bootflow scan/list' commands using all bootdevs */ static int bootflow_cmd_glob(struct unit_test_state *uts) @@ -102,7 +130,7 @@ static int bootflow_cmd_glob(struct unit_test_state *uts) ut_assertok(bootstd_test_drop_bootdev_order(uts)); console_record_reset_enable(); - ut_assertok(run_command("bootflow scan -lG", 0)); + ut_assertok(run_command("bootflow scan -lGH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); ut_assert_nextline("Seq Method State Uclass Part Name Filename"); ut_assert_nextlinen("---"); @@ -134,7 +162,7 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts) ut_assertok(bootstd_test_drop_bootdev_order(uts)); console_record_reset_enable(); - ut_assertok(run_command("bootflow scan -aleG", 0)); + ut_assertok(run_command("bootflow scan -aleGH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); ut_assert_nextline("Seq Method State Uclass Part Name Filename"); ut_assert_nextlinen("---"); @@ -199,6 +227,7 @@ static int bootflow_cmd_info(struct unit_test_state *uts) ut_assert_nextline("Size: 253 (595 bytes)"); ut_assert_nextline("OS: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)"); ut_assert_nextline("Logo: (none)"); + ut_assert_nextline("FDT: <NULL>"); ut_assert_nextline("Error: 0"); ut_assert_console_end(); @@ -247,7 +276,8 @@ static int bootflow_iter(struct unit_test_state *uts) /* The first device is mmc2.bootdev which has no media */ ut_asserteq(-EPROTONOSUPPORT, - bootflow_scan_first(&iter, BOOTFLOWF_ALL | BOOTFLOWF_SKIP_GLOBAL, &bflow)); + bootflow_scan_first(NULL, NULL, &iter, + BOOTFLOWF_ALL | BOOTFLOWF_SKIP_GLOBAL, &bflow)); ut_asserteq(2, iter.num_methods); ut_asserteq(0, iter.cur_method); ut_asserteq(0, iter.part); @@ -315,8 +345,8 @@ static int bootflow_iter(struct unit_test_state *uts) ut_asserteq(BOOTFLOWST_FS, bflow.state); bootflow_free(&bflow); - /* Then more to partition 2 which doesn't exist */ - ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow)); + /* Then more to partition 2 which exists but is not bootable */ + ut_asserteq(-EINVAL, bootflow_scan_next(&iter, &bflow)); ut_asserteq(2, iter.num_methods); ut_asserteq(0, iter.cur_method); ut_asserteq(2, iter.part); @@ -338,22 +368,24 @@ BOOTSTD_TEST(bootflow_iter, UT_TESTF_DM | UT_TESTF_SCAN_FDT); /* Check using the system bootdev */ static int bootflow_system(struct unit_test_state *uts) { - struct udevice *dev; + struct udevice *bootstd, *dev; if (!IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) return -EAGAIN; - ut_assertok(uclass_get_device_by_name(UCLASS_BOOTMETH, "efi_mgr", - &dev)); + ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); + ut_assertok(device_bind(bootstd, DM_DRIVER_GET(bootmeth_efi_mgr), + "efi_mgr", 0, ofnode_null(), &dev)); + ut_assertok(device_probe(dev)); sandbox_set_fake_efi_mgr_dev(dev, true); /* We should get a single 'bootmgr' method right at the end */ bootstd_clear_glob(); console_record_reset_enable(); - ut_assertok(run_command("bootflow scan -l", 0)); + ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_skip_to_line( " 0 efi_mgr ready (none) 0 <NULL> <NULL>"); ut_assert_skip_to_line("No more bootdevs"); - ut_assert_skip_to_line("(5 bootflows, 5 valid)"); + ut_assert_skip_to_line("(2 bootflows, 2 valid)"); ut_assert_console_end(); return 0; @@ -380,11 +412,11 @@ static int bootflow_iter_disable(struct unit_test_state *uts) bootstd_clear_glob(); console_record_reset_enable(); ut_assertok(inject_response(uts)); - ut_assertok(run_command("bootflow scan -lb", 0)); + ut_assertok(run_command("bootflow scan -lbH", 0)); /* Try to boot the bootmgr flow, which will fail */ console_record_reset_enable(); - ut_assertok(bootflow_scan_first(&iter, 0, &bflow)); + ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); ut_asserteq(3, iter.num_methods); ut_asserteq_str("sandbox", iter.method->name); ut_assertok(inject_response(uts)); @@ -416,7 +448,7 @@ static int bootflow_scan_glob_bootmeth(struct unit_test_state *uts) */ console_record_reset_enable(); ut_assertok(bootmeth_set_order("efi firmware0")); - ut_assertok(run_command("bootflow scan -lG", 0)); + ut_assertok(run_command("bootflow scan -lGH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); ut_assert_nextline( "Seq Method State Uclass Part Name Filename"); @@ -425,7 +457,7 @@ static int bootflow_scan_glob_bootmeth(struct unit_test_state *uts) ut_assert_nextline("(0 bootflows, 0 valid)"); ut_assert_console_end(); - ut_assertok(run_command("bootflow scan -l", 0)); + ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); ut_assert_nextline( "Seq Method State Uclass Part Name Filename"); @@ -531,6 +563,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 * diff --git a/test/boot/bootstd_common.c b/test/boot/bootstd_common.c index 7a40836507a..e71a2975c53 100644 --- a/test/boot/bootstd_common.c +++ b/test/boot/bootstd_common.c @@ -7,6 +7,7 @@ */ #include <common.h> +#include <bootdev.h> #include <bootstd.h> #include <dm.h> #include <memalign.h> @@ -67,6 +68,24 @@ int bootstd_test_drop_bootdev_order(struct unit_test_state *uts) return 0; } +int bootstd_test_check_mmc_hunter(struct unit_test_state *uts) +{ + struct bootdev_hunter *start, *mmc; + struct bootstd_priv *std; + uint seq; + + /* get access to the used hunters */ + ut_assertok(bootstd_get_priv(&std)); + + /* check that the hunter was used */ + start = ll_entry_start(struct bootdev_hunter, bootdev_hunter); + mmc = BOOTDEV_HUNTER_GET(mmc_bootdev_hunter); + seq = mmc - start; + ut_asserteq(BIT(seq), std->hunters_used); + + return 0; +} + int do_ut_bootstd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(bootstd_test); diff --git a/test/boot/bootstd_common.h b/test/boot/bootstd_common.h index c5e0fd1ceab..136a79b5178 100644 --- a/test/boot/bootstd_common.h +++ b/test/boot/bootstd_common.h @@ -20,6 +20,11 @@ #define TEST_VERSION "U-Boot v2022.04-local2" #define TEST_VERNUM 0x00010002 +enum { + MAX_HUNTER = 8, + MMC_HUNTER = 3, /* ID of MMC hunter */ +}; + struct unit_test_state; /** @@ -40,4 +45,12 @@ int bootstd_test_drop_bootdev_order(struct unit_test_state *uts); */ int bootstd_setup_for_tests(void); +/** + * bootstd_test_check_mmc_hunter() - Check that the mmc bootdev hunter was used + * + * @uts: Unit test state to use for ut_assert...() functions + * Returns: 0 if OK (used), other value on error (not used) + */ +int bootstd_test_check_mmc_hunter(struct unit_test_state *uts); + #endif diff --git a/test/cmd_ut.c b/test/cmd_ut.c index 067bd0828a1..1713d0d1c85 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -189,7 +189,7 @@ static char ut_help_text[] = "\nfdt - fdt command" #endif #ifdef CONFIG_CONSOLE_TRUETYPE - "\nut font - font command\n" + "\nut font - font command" #endif #ifdef CONFIG_CMD_LOADM "\nloadm - loadm command parameters and loading memory blob" diff --git a/test/dm/part.c b/test/dm/part.c index 78dd8472c2e..35e99eeb01a 100644 --- a/test/dm/part.c +++ b/test/dm/part.c @@ -11,8 +11,8 @@ #include <dm/test.h> #include <test/ut.h> -static inline int do_test(struct unit_test_state *uts, int expected, - const char *part_str, bool whole) +static int do_test(struct unit_test_state *uts, int expected, + const char *part_str, bool whole) { struct blk_desc *mmc_dev_desc; struct disk_partition part_info; @@ -43,7 +43,7 @@ static int dm_test_part(struct unit_test_state *uts) }, }; - ut_asserteq(1, blk_get_device_by_str("mmc", "1", &mmc_dev_desc)); + ut_asserteq(2, blk_get_device_by_str("mmc", "2", &mmc_dev_desc)); if (CONFIG_IS_ENABLED(RANDOM_UUID)) { gen_rand_uuid_str(parts[0].uuid, UUID_STR_FORMAT_STD); gen_rand_uuid_str(parts[1].uuid, UUID_STR_FORMAT_STD); @@ -54,11 +54,8 @@ static int dm_test_part(struct unit_test_state *uts) oldbootdevice = env_get("bootdevice"); -#define test(expected, part_str, whole) do { \ - ret = do_test(uts, expected, part_str, whole); \ - if (ret) \ - goto out; \ -} while (0) +#define test(expected, part_str, whole) \ + ut_assertok(do_test(uts, expected, part_str, whole)) env_set("bootdevice", NULL); test(-ENODEV, NULL, true); @@ -66,7 +63,7 @@ static int dm_test_part(struct unit_test_state *uts) env_set("bootdevice", "0"); test(0, NULL, true); test(0, "", true); - env_set("bootdevice", "1"); + env_set("bootdevice", "2"); test(1, NULL, false); test(1, "", false); test(1, "-", false); @@ -77,8 +74,8 @@ static int dm_test_part(struct unit_test_state *uts) test(0, ".0", true); test(0, ".0:0", true); test(-EINVAL, "#test1", true); - test(1, "1", false); - test(1, "1", true); + test(1, "2", false); + test(1, "2", true); test(-ENOENT, "1:0", false); test(0, "1:0", true); test(1, "1:1", false); @@ -88,12 +85,24 @@ static int dm_test_part(struct unit_test_state *uts) test(1, "1.0:1", false); test(2, "1.0:2", false); test(-EINVAL, "1#bogus", false); - test(1, "1#test1", false); - test(2, "1#test2", false); + test(1, "2#test1", false); + test(2, "2#test2", false); ret = 0; -out: env_set("bootdevice", oldbootdevice); return ret; } DM_TEST(dm_test_part, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_part_bootable(struct unit_test_state *uts) +{ + struct blk_desc *desc; + struct udevice *dev; + + ut_assertok(uclass_get_device_by_name(UCLASS_BLK, "mmc1.blk", &dev)); + desc = dev_get_uclass_plat(dev); + ut_asserteq(1, part_get_bootable(desc)); + + return 0; +} +DM_TEST(dm_test_part_bootable, UT_TESTF_SCAN_FDT); diff --git a/test/dm/virtio_device.c b/test/dm/virtio_device.c index b5c4523a028..fdda4da4178 100644 --- a/test/dm/virtio_device.c +++ b/test/dm/virtio_device.c @@ -100,6 +100,7 @@ DM_TEST(dm_test_virtio_all_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); static int dm_test_virtio_remove(struct unit_test_state *uts) { struct udevice *bus, *dev; + u8 status; /* check probe success */ ut_assertok(uclass_first_device_err(UCLASS_VIRTIO, &bus)); @@ -117,6 +118,8 @@ static int dm_test_virtio_remove(struct unit_test_state *uts) ut_asserteq(-EKEYREJECTED, device_remove(bus, DM_REMOVE_ACTIVE_ALL)); ut_asserteq(false, device_active(dev)); + virtio_get_status(dev, &status); + ut_assertok(status); return 0; } diff --git a/test/py/tests/bootstd/mmc1.img.xz b/test/py/tests/bootstd/mmc1.img.xz Binary files differindex 4e7f39b830e..cebf7b9c53b 100644 --- a/test/py/tests/bootstd/mmc1.img.xz +++ b/test/py/tests/bootstd/mmc1.img.xz diff --git a/test/py/tests/test_dm.py b/test/py/tests/test_dm.py index ea93061fdfa..68d4ea12235 100644 --- a/test/py/tests/test_dm.py +++ b/test/py/tests/test_dm.py @@ -16,6 +16,44 @@ def test_dm_compat(u_boot_console): for driver in drivers: assert driver in response + # check sorting - output looks something like this: + # testacpi 0 [ ] testacpi_drv |-- acpi-test + # testacpi 1 [ ] testacpi_drv | `-- child + # pci_emul_p 1 [ ] pci_emul_parent_drv |-- pci-emul2 + # pci_emul 5 [ ] sandbox_swap_case_em | `-- emul2@1f,0 + + # The number of '| ' and '--' matches indicate the indent level. We start + # checking sorting only after UCLASS_AXI_EMUL after which the names should + # be sorted. + + response = u_boot_console.run_command('dm tree -s') + lines = response.split('\n')[2:] + stack = [] # holds where we were up to at the previous indent level + prev = '' # uclass name of previous line + start = False + for line in lines: + indent = line.count('| ') + ('--' in line) + cur = line.split()[0] + if not start: + if cur != 'axi_emul': + continue + start = True + + # Handle going up or down an indent level + if indent > len(stack): + stack.append(prev) + prev = '' + elif indent < len(stack): + prev = stack.pop() + + # Check that the current uclass name is not alphabetically before the + # previous one + if 'emul' not in cur and cur < prev: + print('indent', cur >= prev, indent, prev, cur, stack) + assert cur >= prev + prev = cur + + @pytest.mark.buildconfigspec('cmd_dm') def test_dm_drivers(u_boot_console): """Test that each driver in `dm compat` is also listed in `dm drivers`.""" diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index 6958fabfa34..e8c8a6d6bd5 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -19,13 +19,14 @@ def mkdir_cond(dirname): if not os.path.exists(dirname): os.mkdir(dirname) -def setup_image(cons, mmc_dev, part_type): +def setup_image(cons, mmc_dev, part_type, second_part=False): """Create a 20MB disk image with a single partition Args: cons (ConsoleBase): Console to use mmc_dev (int): MMC device number to use, e.g. 1 part_type (int): Partition type, e.g. 0xc for FAT32 + second_part (bool): True to contain a small second partition Returns: tuple: @@ -36,9 +37,13 @@ def setup_image(cons, mmc_dev, part_type): mnt = os.path.join(cons.config.persistent_data_dir, 'mnt') mkdir_cond(mnt) + spec = f'type={part_type:x}, size=18M, bootable' + if second_part: + spec += '\ntype=c' + u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname) u_boot_utils.run_and_log(cons, 'sudo sfdisk %s' % fname, - stdin=f'type={part_type:x}'.encode('utf-8')) + stdin=spec.encode('utf-8')) return fname, mnt def mount_image(cons, fname, mnt, fstype): @@ -59,7 +64,7 @@ def mount_image(cons, fname, mnt, fstype): u_boot_utils.run_and_log(cons, f'sudo mkfs.{fstype} {part}') opts = '' if fstype == 'vfat': - opts += ' -o uid={os.getuid()},gid={os.getgid()}' + opts += f' -o uid={os.getuid()},gid={os.getgid()}' u_boot_utils.run_and_log(cons, f'sudo mount -o loop {part} {mnt}{opts}') u_boot_utils.run_and_log(cons, f'sudo chown {getpass.getuser()} {mnt}') return loop @@ -218,7 +223,7 @@ booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r} def setup_bootflow_image(cons): """Create a 20MB disk image with a single FAT partition""" mmc_dev = 1 - fname, mnt = setup_image(cons, mmc_dev, 0xc) + fname, mnt = setup_image(cons, mmc_dev, 0xc, second_part=True) loop = None mounted = False diff --git a/test/str_ut.c b/test/str_ut.c index 5a844347c2b..fa9328ede50 100644 --- a/test/str_ut.c +++ b/test/str_ut.c @@ -274,6 +274,88 @@ static int str_trailing(struct unit_test_state *uts) } STR_TEST(str_trailing, 0); +static int test_str_to_list(struct unit_test_state *uts) +{ + const char **ptr; + ulong start; + + /* check out of memory */ + start = ut_check_delta(0); + malloc_enable_testing(0); + ut_assertnull(str_to_list("")); + ut_assertok(ut_check_delta(start)); + + ut_assertnull(str_to_list("this is a test")); + ut_assertok(ut_check_delta(start)); + + malloc_enable_testing(1); + ut_assertnull(str_to_list("this is a test")); + ut_assertok(ut_check_delta(start)); + + /* for an empty string, only one nalloc is needed */ + malloc_enable_testing(1); + ptr = str_to_list(""); + ut_assertnonnull(ptr); + ut_assertnull(ptr[0]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + malloc_disable_testing(); + + /* test the same again, without any nalloc restrictions */ + ptr = str_to_list(""); + ut_assertnonnull(ptr); + ut_assertnull(ptr[0]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test a single string */ + start = ut_check_delta(0); + ptr = str_to_list("hi"); + ut_assertnonnull(ptr); + ut_assertnonnull(ptr[0]); + ut_asserteq_str("hi", ptr[0]); + ut_assertnull(ptr[1]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test two strings */ + ptr = str_to_list("hi there"); + ut_assertnonnull(ptr); + ut_assertnonnull(ptr[0]); + ut_asserteq_str("hi", ptr[0]); + ut_assertnonnull(ptr[1]); + ut_asserteq_str("there", ptr[1]); + ut_assertnull(ptr[2]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test leading, trailing and multiple spaces */ + ptr = str_to_list(" more space "); + ut_assertnonnull(ptr); + ut_assertnonnull(ptr[0]); + ut_asserteq_str("", ptr[0]); + ut_assertnonnull(ptr[1]); + ut_asserteq_str("more", ptr[1]); + ut_assertnonnull(ptr[2]); + ut_asserteq_str("", ptr[2]); + ut_assertnonnull(ptr[3]); + ut_asserteq_str("space", ptr[3]); + ut_assertnonnull(ptr[4]); + ut_asserteq_str("", ptr[4]); + ut_assertnonnull(ptr[5]); + ut_asserteq_str("", ptr[5]); + ut_assertnull(ptr[6]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test freeing a NULL pointer */ + str_free_list(NULL); + + return 0; +} +STR_TEST(test_str_to_list, 0); + int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(str_test); diff --git a/test/test-main.c b/test/test-main.c index 5931e94a915..ea959f4e859 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -10,6 +10,7 @@ #include <cyclic.h> #include <dm.h> #include <event.h> +#include <net.h> #include <of_live.h> #include <os.h> #include <dm/ofnode.h> @@ -296,17 +297,28 @@ static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) uts->start = mallinfo(); - if (test->flags & UT_TESTF_SCAN_PDATA) { + if (test->flags & UT_TESTF_SCAN_PDATA) ut_assertok(dm_scan_plat(false)); - ut_assertok(dm_scan_other(false)); - } if (test->flags & UT_TESTF_PROBE_TEST) ut_assertok(do_autoprobe(uts)); if (!CONFIG_IS_ENABLED(OF_PLATDATA) && - (test->flags & UT_TESTF_SCAN_FDT)) + (test->flags & UT_TESTF_SCAN_FDT)) { + /* + * only set this if we know the ethernet uclass will be created + */ + eth_set_enable_bootdevs(test->flags & UT_TESTF_ETH_BOOTDEV); + test_sf_set_enable_bootdevs(test->flags & UT_TESTF_SF_BOOTDEV); ut_assertok(dm_extended_scan(false)); + } + + /* + * Do this after FDT scan since dm_scan_other() in bootstd-uclass.c + * checks for the existence of bootstd + */ + if (test->flags & UT_TESTF_SCAN_PDATA) + ut_assertok(dm_scan_other(false)); if (IS_ENABLED(CONFIG_SANDBOX) && (test->flags & UT_TESTF_OTHER_FDT)) { /* make sure the other FDT is available */ @@ -635,9 +647,5 @@ int ut_run_list(const char *category, const char *prefix, else printf("Failures: %d\n", uts.fail_count); - /* Best efforts only...ignore errors */ - if (has_dm_tests) - dm_test_restore(uts.of_root); - return ret; } |