From e7ee728ace3c6cc45fce3d8f14560d2be99ec07a Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sat, 16 May 2026 00:38:27 +0100 Subject: test: boot: add runtime unit test for fit_verity_build_cmdline() Add test/boot/fit_verity.c with four tests that construct FIT blobs in memory and exercise fit_verity_build_cmdline(). Signed-off-by: Daniel Golle Reviewed-by: Simon Glass --- test/boot/Makefile | 1 + test/boot/fit_verity.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 test/boot/fit_verity.c (limited to 'test/boot') diff --git a/test/boot/Makefile b/test/boot/Makefile index 89538d4f0a6..d98f212b243 100644 --- a/test/boot/Makefile +++ b/test/boot/Makefile @@ -15,6 +15,7 @@ endif ifdef CONFIG_SANDBOX obj-$(CONFIG_$(PHASE_)CMDLINE) += bootm.o endif +obj-$(CONFIG_$(PHASE_)FIT_VERITY) += fit_verity.o obj-$(CONFIG_MEASURED_BOOT) += measurement.o ifdef CONFIG_OF_LIVE diff --git a/test/boot/fit_verity.c b/test/boot/fit_verity.c new file mode 100644 index 00000000000..7459a9d6f81 --- /dev/null +++ b/test/boot/fit_verity.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for FIT dm-verity cmdline generation + * + * Copyright 2026 Daniel Golle + */ + +#include +#include +#include + +#define FIT_VERITY_TEST(_name, _flags) UNIT_TEST(_name, _flags, fit_verity) + +/* FIT blob buffer size — generous to avoid FDT_ERR_NOSPACE */ +#define FIT_BUF_SIZE 4096 + +/* Test digest (32 bytes = sha256) */ +static const u8 test_digest[32] = { + 0x8e, 0x67, 0x91, 0x63, 0x7f, 0x93, 0xcb, 0xb8, + 0x1f, 0xc4, 0x52, 0x99, 0xe2, 0x03, 0xcb, 0xe8, + 0x5c, 0xa2, 0xe4, 0x7a, 0x38, 0xf5, 0x05, 0x1b, + 0xdd, 0xee, 0xce, 0x92, 0xd7, 0xb1, 0xc9, 0xf9, +}; + +/* Test salt (32 bytes) */ +static const u8 test_salt[32] = { + 0xaa, 0x7b, 0x11, 0xf8, 0xdb, 0x8f, 0xe2, 0xe5, + 0xbf, 0xd4, 0xec, 0xa1, 0xd1, 0x8a, 0x22, 0xb5, + 0xde, 0x7e, 0xa3, 0x9d, 0x2e, 0x1b, 0x93, 0xbb, + 0x72, 0x72, 0xce, 0x0c, 0x6c, 0xa3, 0xcc, 0x8e, +}; + +/** + * build_verity_fit() - construct a minimal FIT blob with dm-verity metadata + * @buf: output buffer (at least FIT_BUF_SIZE bytes) + * @num_loadables: number of filesystem loadables to create (1 or 2) + * + * Builds a FIT blob containing: + * - /images/rootfsN with type="filesystem" and a dm-verity subnode + * - /configurations/conf-1 referencing the loadable(s) + * + * Return: configuration node offset, or -ve on error + */ +static int build_verity_fit(void *buf, int num_loadables) +{ + int images_node, conf_node, confs_node, img_node, verity_node; + fdt32_t val; + int ret, i; + char name[32]; + /* + * Build the loadables string list. FDT stringlists are concatenated + * NUL-terminated strings. E.g. "rootfs0\0rootfs1\0" + */ + char loadables[128]; + int loadables_len = 0; + + ret = fdt_create_empty_tree(buf, FIT_BUF_SIZE); + if (ret) + return ret; + + /* /images */ + images_node = fdt_add_subnode(buf, 0, "images"); + if (images_node < 0) + return images_node; + + for (i = 0; i < num_loadables; i++) { + snprintf(name, sizeof(name), "rootfs%d", i); + + img_node = fdt_add_subnode(buf, images_node, name); + if (img_node < 0) + return img_node; + + ret = fdt_setprop_string(buf, img_node, FIT_TYPE_PROP, + "filesystem"); + if (ret) + return ret; + + verity_node = fdt_add_subnode(buf, img_node, + FIT_VERITY_NODENAME); + if (verity_node < 0) + return verity_node; + + ret = fdt_setprop_string(buf, verity_node, + FIT_VERITY_ALGO_PROP, "sha256"); + if (ret) + return ret; + + val = cpu_to_fdt32(4096); + ret = fdt_setprop(buf, verity_node, FIT_VERITY_DBS_PROP, + &val, sizeof(val)); + if (ret) + return ret; + + ret = fdt_setprop(buf, verity_node, FIT_VERITY_HBS_PROP, + &val, sizeof(val)); + if (ret) + return ret; + + val = cpu_to_fdt32(100); + ret = fdt_setprop(buf, verity_node, FIT_VERITY_NBLK_PROP, + &val, sizeof(val)); + if (ret) + return ret; + + val = cpu_to_fdt32(100); + ret = fdt_setprop(buf, verity_node, FIT_VERITY_HBLK_PROP, + &val, sizeof(val)); + if (ret) + return ret; + + ret = fdt_setprop(buf, verity_node, FIT_VERITY_DIGEST_PROP, + test_digest, sizeof(test_digest)); + if (ret) + return ret; + + ret = fdt_setprop(buf, verity_node, FIT_VERITY_SALT_PROP, + test_salt, sizeof(test_salt)); + if (ret) + return ret; + + /* Append to loadables stringlist */ + loadables_len += snprintf(loadables + loadables_len, + sizeof(loadables) - loadables_len, + "%s", name) + 1; + } + + /* /configurations/conf-1 */ + confs_node = fdt_add_subnode(buf, 0, "configurations"); + if (confs_node < 0) + return confs_node; + + conf_node = fdt_add_subnode(buf, confs_node, "conf-1"); + if (conf_node < 0) + return conf_node; + + ret = fdt_setprop(buf, conf_node, FIT_LOADABLE_PROP, + loadables, loadables_len); + if (ret) + return ret; + + return conf_node; +} + +/* Test: single dm-verity loadable produces correct cmdline fragments */ +static int fit_verity_test_single(struct unit_test_state *uts) +{ + char buf[FIT_BUF_SIZE]; + struct bootm_headers images; + int conf_noffset; + + conf_noffset = build_verity_fit(buf, 1); + ut_assert(conf_noffset >= 0); + + memset(&images, 0, sizeof(images)); + ut_assertok(fit_verity_build_cmdline(buf, conf_noffset, &images)); + + /* dm_mod_create should contain the target spec for rootfs0 */ + ut_assertnonnull(images.dm_mod_create); + ut_assert(strstr(images.dm_mod_create, "rootfs0,,,")); + ut_assert(strstr(images.dm_mod_create, "verity 1")); + ut_assert(strstr(images.dm_mod_create, "/dev/fit0")); + ut_assert(strstr(images.dm_mod_create, "4096 4096 100 100")); + ut_assert(strstr(images.dm_mod_create, "sha256")); + /* Check hex-encoded digest prefix */ + ut_assert(strstr(images.dm_mod_create, "8e6791637f93cbb8")); + /* Check hex-encoded salt prefix */ + ut_assert(strstr(images.dm_mod_create, "aa7b11f8db8fe2e5")); + + /* dm_mod_waitfor should reference /dev/fit0 */ + ut_assertnonnull(images.dm_mod_waitfor); + ut_asserteq_str("/dev/fit0", images.dm_mod_waitfor); + + fit_verity_free(&images); + ut_assertnull(images.dm_mod_create); + ut_assertnull(images.dm_mod_waitfor); + + return 0; +} +FIT_VERITY_TEST(fit_verity_test_single, 0); + +/* Test: FIT with no dm-verity subnode returns 0, pointers stay NULL */ +static int fit_verity_test_no_verity(struct unit_test_state *uts) +{ + char buf[FIT_BUF_SIZE]; + struct bootm_headers images; + int conf_node, images_node, img_node, confs_node; + int ret; + + ret = fdt_create_empty_tree(buf, FIT_BUF_SIZE); + ut_assertok(ret); + + images_node = fdt_add_subnode(buf, 0, "images"); + ut_assert(images_node >= 0); + + img_node = fdt_add_subnode(buf, images_node, "rootfs"); + ut_assert(img_node >= 0); + ut_assertok(fdt_setprop_string(buf, img_node, FIT_TYPE_PROP, + "filesystem")); + /* No dm-verity subnode */ + + confs_node = fdt_add_subnode(buf, 0, "configurations"); + ut_assert(confs_node >= 0); + conf_node = fdt_add_subnode(buf, confs_node, "conf-1"); + ut_assert(conf_node >= 0); + ut_assertok(fdt_setprop_string(buf, conf_node, FIT_LOADABLE_PROP, + "rootfs")); + + memset(&images, 0, sizeof(images)); + ut_asserteq(0, fit_verity_build_cmdline(buf, conf_node, &images)); + ut_assertnull(images.dm_mod_create); + ut_assertnull(images.dm_mod_waitfor); + + return 0; +} +FIT_VERITY_TEST(fit_verity_test_no_verity, 0); + +/* Test: two dm-verity loadables produce combined cmdline */ +static int fit_verity_test_two_loadables(struct unit_test_state *uts) +{ + char buf[FIT_BUF_SIZE]; + struct bootm_headers images; + int conf_noffset; + + conf_noffset = build_verity_fit(buf, 2); + ut_assert(conf_noffset >= 0); + + memset(&images, 0, sizeof(images)); + ut_assertok(fit_verity_build_cmdline(buf, conf_noffset, &images)); + + /* Both targets should appear, separated by ";" */ + ut_assertnonnull(images.dm_mod_create); + ut_assert(strstr(images.dm_mod_create, "rootfs0,,,")); + ut_assert(strstr(images.dm_mod_create, ";rootfs1,,,")); + ut_assert(strstr(images.dm_mod_create, "/dev/fit0")); + ut_assert(strstr(images.dm_mod_create, "/dev/fit1")); + + /* dm_mod_waitfor should list both devices */ + ut_assertnonnull(images.dm_mod_waitfor); + ut_assert(strstr(images.dm_mod_waitfor, "/dev/fit0")); + ut_assert(strstr(images.dm_mod_waitfor, "/dev/fit1")); + + fit_verity_free(&images); + return 0; +} +FIT_VERITY_TEST(fit_verity_test_two_loadables, 0); + +/* Test: invalid block size (not power of two) returns -EINVAL */ +static int fit_verity_test_bad_blocksize(struct unit_test_state *uts) +{ + char buf[FIT_BUF_SIZE]; + struct bootm_headers images; + int images_node, conf_node, confs_node, img_node, verity_node; + fdt32_t val; + int ret; + + ret = fdt_create_empty_tree(buf, FIT_BUF_SIZE); + ut_assertok(ret); + + images_node = fdt_add_subnode(buf, 0, "images"); + ut_assert(images_node >= 0); + + img_node = fdt_add_subnode(buf, images_node, "rootfs"); + ut_assert(img_node >= 0); + ut_assertok(fdt_setprop_string(buf, img_node, FIT_TYPE_PROP, + "filesystem")); + + verity_node = fdt_add_subnode(buf, img_node, FIT_VERITY_NODENAME); + ut_assert(verity_node >= 0); + + ut_assertok(fdt_setprop_string(buf, verity_node, + FIT_VERITY_ALGO_PROP, "sha256")); + + /* 3000 is not a power of two */ + val = cpu_to_fdt32(3000); + ut_assertok(fdt_setprop(buf, verity_node, FIT_VERITY_DBS_PROP, + &val, sizeof(val))); + val = cpu_to_fdt32(4096); + ut_assertok(fdt_setprop(buf, verity_node, FIT_VERITY_HBS_PROP, + &val, sizeof(val))); + + val = cpu_to_fdt32(100); + ut_assertok(fdt_setprop(buf, verity_node, FIT_VERITY_NBLK_PROP, + &val, sizeof(val))); + ut_assertok(fdt_setprop(buf, verity_node, FIT_VERITY_HBLK_PROP, + &val, sizeof(val))); + + ut_assertok(fdt_setprop(buf, verity_node, FIT_VERITY_DIGEST_PROP, + test_digest, sizeof(test_digest))); + ut_assertok(fdt_setprop(buf, verity_node, FIT_VERITY_SALT_PROP, + test_salt, sizeof(test_salt))); + + confs_node = fdt_add_subnode(buf, 0, "configurations"); + ut_assert(confs_node >= 0); + conf_node = fdt_add_subnode(buf, confs_node, "conf-1"); + ut_assert(conf_node >= 0); + ut_assertok(fdt_setprop_string(buf, conf_node, FIT_LOADABLE_PROP, + "rootfs")); + + memset(&images, 0, sizeof(images)); + ut_asserteq(-EINVAL, fit_verity_build_cmdline(buf, conf_node, &images)); + ut_assertnull(images.dm_mod_create); + ut_assertnull(images.dm_mod_waitfor); + + return 0; +} +FIT_VERITY_TEST(fit_verity_test_bad_blocksize, 0); -- cgit v1.3.1 From de12fa4df89277460aacaea7c9a43421190ee04d Mon Sep 17 00:00:00 2001 From: Randolph Sapp Date: Thu, 4 Jun 2026 10:50:37 -0500 Subject: test: boot: add a fdt reserved region check Add a image_fdt suite and a check for boot_fdt_add_mem_rsv_regions. This will ensure the user is properly informed of any reservation failures. It will also validate that reservations are cleaned up correctly when switching FDTs. Signed-off-by: Randolph Sapp Reviewed-by: Simon Glass Acked-by: Ilias Apalodimas --- test/boot/Makefile | 3 ++ test/boot/image_fdt.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ test/cmd_ut.c | 2 ++ test/py/tests/test_suite.py | 2 +- 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 test/boot/image_fdt.c (limited to 'test/boot') diff --git a/test/boot/Makefile b/test/boot/Makefile index 89538d4f0a6..12904f7f508 100644 --- a/test/boot/Makefile +++ b/test/boot/Makefile @@ -14,6 +14,9 @@ endif ifdef CONFIG_SANDBOX obj-$(CONFIG_$(PHASE_)CMDLINE) += bootm.o +ifdef CONFIG_UT_DM +obj-$(CONFIG_$(PHASE_)OF_LIBFDT) += image_fdt.o +endif endif obj-$(CONFIG_MEASURED_BOOT) += measurement.o diff --git a/test/boot/image_fdt.c b/test/boot/image_fdt.c new file mode 100644 index 00000000000..5417689a683 --- /dev/null +++ b/test/boot/image_fdt.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#define IMAGE_FDT_TEST(_name, _flags) UNIT_TEST(_name, _flags, image_fdt) + +DECLARE_GLOBAL_DATA_PTR; + +/** + * test_boot_fdt_add_mem_rsv_regions - Make sure dt reservations are created and + * destroyed correctly + * @uts: Test state + * + * This test depends on the UT_DM device tree and ensures the following + * statements hold true: The default reservation in test.dtb exists. + * Re-reserving that region will result in an error. Loading a new device tree + * will remove old reservations. + */ +static int test_boot_fdt_add_mem_rsv_regions(struct unit_test_state *uts) +{ + phys_addr_t start = CFG_SYS_SDRAM_BASE + 0x100000; + const void *old_blob = gd->fdt_blob; + int ret = CMD_RET_FAILURE; + ulong fdt_sz; + int nodeoffset; + void *new_blob; + + /* Default reservation should exist */ + ut_asserteq(1, lmb_is_reserved_flags(start, LMB_NOMAP)); + + /* Attempting to re-reserve should warn the user */ + boot_fdt_add_mem_rsv_regions(gd->fdt_blob); + ut_assert_nextlinen("ERROR: reserving"); + ut_assert_console_end(); + + /* Loading a new_blob device tree should be allowed */ + fdt_sz = fdt_totalsize(gd->fdt_blob); + new_blob = malloc(fdt_sz); + ut_assertnonnull(new_blob); + memcpy(new_blob, gd->fdt_blob, fdt_sz); + + nodeoffset = fdt_path_offset(new_blob, "/reserved-memory"); + if (nodeoffset < 0) + goto free_blob; + + if (fdt_del_node(new_blob, nodeoffset)) + goto free_blob; + + boot_fdt_add_mem_rsv_regions(new_blob); + gd->fdt_blob = new_blob; + + if (ut_check_console_end(uts)) { + ut_failf(uts, __FILE__, __LINE__, __func__, "console", + "Expected no more output, got '%s'", uts->actual_str); + goto switch_fdt; + } + + /* Reservation should not exist now */ + if (!lmb_is_reserved_flags(start, LMB_NOMAP)) + ret = 0; + + /* Cleanup */ +switch_fdt: + boot_fdt_add_mem_rsv_regions(old_blob); + gd->fdt_blob = old_blob; +free_blob: + free(new_blob); + return ret; +} +IMAGE_FDT_TEST(test_boot_fdt_add_mem_rsv_regions, UTF_CONSOLE); diff --git a/test/cmd_ut.c b/test/cmd_ut.c index 44e5fdfdaa6..363ed4eab30 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -61,6 +61,7 @@ SUITE_DECL(fdt); SUITE_DECL(fdt_overlay); SUITE_DECL(font); SUITE_DECL(hush); +SUITE_DECL(image_fdt); SUITE_DECL(lib); SUITE_DECL(loadm); SUITE_DECL(log); @@ -88,6 +89,7 @@ static struct suite suites[] = { SUITE(fdt_overlay, "device tree overlays"), SUITE(font, "font command"), SUITE(hush, "hush behaviour"), + SUITE(image_fdt, "image fdt parsing"), SUITE(lib, "library functions"), SUITE(loadm, "loadm command parameters and loading memory blob"), SUITE(log, "logging functions"), diff --git a/test/py/tests/test_suite.py b/test/py/tests/test_suite.py index 7fe9a90dfd3..08285f12a5f 100644 --- a/test/py/tests/test_suite.py +++ b/test/py/tests/test_suite.py @@ -8,7 +8,7 @@ import re EXPECTED_SUITES = [ 'addrmap', 'bdinfo', 'bloblist', 'bootm', 'bootstd', 'cmd', 'common', 'dm', 'env', 'exit', 'fdt_overlay', - 'fdt', 'font', 'hush', 'lib', + 'fdt', 'font', 'hush', 'image_fdt', 'lib', 'loadm', 'log', 'mbr', 'measurement', 'mem', 'pci_mps', 'setexpr', 'upl', ] -- cgit v1.3.1 From 93e9af685fefc454580dcf567b03c139a2fe8ebc Mon Sep 17 00:00:00 2001 From: Denis Mukhin Date: Tue, 23 Jun 2026 15:06:30 -0700 Subject: test: bootdev: scan with a broken high-priority device Add bootdev_hunt_fallthrough() test to verify that 'bootflow scan -l' falls back to a lower-priority bootdev when a higher-priority hunter fails. Introduce a simple 'sandbox-bootdev' device for the test. The new bootdev can be configured to produce an error at the hunting stage. Introduce new host_set_flags_by_label() API and a flags field to 'host_sb_plat' to simulate a bootdev hunter failure for the test. Adjust boot{dev,flow} tests which depend on bootdev hunters. Signed-off-by: Denis Mukhin Reviewed-by: Simon Glass --- drivers/block/Makefile | 2 +- drivers/block/host-uclass.c | 15 +++++++++ drivers/block/sandbox-bootdev.c | 73 +++++++++++++++++++++++++++++++++++++++++ include/sandbox_host.h | 18 ++++++++++ test/boot/bootdev.c | 23 ++++++------- test/boot/bootflow.c | 47 ++++++++++++++++++++++++++ test/boot/bootstd_common.h | 5 ++- 7 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 drivers/block/sandbox-bootdev.c (limited to 'test/boot') diff --git a/drivers/block/Makefile b/drivers/block/Makefile index f5a9d8637a3..c827fa81a2d 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -13,7 +13,7 @@ ifndef CONFIG_XPL_BUILD obj-$(CONFIG_IDE) += ide.o obj-$(CONFIG_RKMTD) += rkmtd.o endif -obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o +obj-$(CONFIG_SANDBOX) += sandbox.o sandbox-bootdev.o host-uclass.o host_dev.o obj-$(CONFIG_$(PHASE_)BLOCK_CACHE) += blkcache.o obj-$(CONFIG_$(PHASE_)BLKMAP) += blkmap.o obj-$(CONFIG_$(PHASE_)BLKMAP) += blkmap_helper.o diff --git a/drivers/block/host-uclass.c b/drivers/block/host-uclass.c index cf42bd1e07a..95b0b0b2ffe 100644 --- a/drivers/block/host-uclass.c +++ b/drivers/block/host-uclass.c @@ -150,6 +150,21 @@ struct udevice *host_find_by_label(const char *label) return NULL; } +int host_set_flags_by_label(const char *label, unsigned int flags) +{ + struct udevice *dev; + struct host_sb_plat *plat; + + dev = host_find_by_label(label); + if (!dev) + return -ENODEV; + + plat = dev_get_plat(dev); + plat->flags = flags; + + return 0; +} + struct udevice *host_get_cur_dev(void) { struct uclass *uc = uclass_find(UCLASS_HOST); diff --git a/drivers/block/sandbox-bootdev.c b/drivers/block/sandbox-bootdev.c new file mode 100644 index 00000000000..15af0c17d1f --- /dev/null +++ b/drivers/block/sandbox-bootdev.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#define LOG_CATEGORY UCLASS_HOST + +#include +#include +#include +#include + +static int sandbox_bootdev_bind(struct udevice *dev) +{ + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); + + ucp->prio = BOOTDEVP_4_SCAN_FAST; + + return 0; +} + +/** + * sandbox_bootdev_hunt() - Hunt host bootdev. + * + * Note, this hunter exists for bootdev testing to simulate a failure + * mode. Do not use as an example of a real hunter. + * + * @info: Hunter details. + * @show: Enable extra printouts. + * + * Returns: 0 if OK, -ve on error (expected by the test) + */ +static int sandbox_bootdev_hunt(struct bootdev_hunter *info, bool show) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + uclass_id_foreach_dev(UCLASS_HOST, dev, uc) { + struct host_sb_plat *plat = dev_get_plat(dev); + + log_debug("hunting %s\n", plat->label); + + if (plat->flags & HOST_FLAG_BROKEN) { + ret = -ETIME; + log_debug("cannot hunt sandbox device '%s': %d\n", + plat->label, ret); + return ret; + } + } + + return 0; +} + +static const struct bootdev_ops sandbox_bootdev_ops = { +}; + +static const struct udevice_id sandbox_bootdev_ids[] = { + { .compatible = "u-boot,bootdev-sandbox" }, + { } +}; + +U_BOOT_DRIVER(sandbox_bootdev) = { + .name = "sandbox_bootdev", + .id = UCLASS_BOOTDEV, + .ops = &sandbox_bootdev_ops, + .bind = sandbox_bootdev_bind, + .of_match = sandbox_bootdev_ids, +}; + +BOOTDEV_HUNTER(sandbox_bootdev_hunter) = { + .prio = BOOTDEVP_4_SCAN_FAST, + .uclass = UCLASS_HOST, + .hunt = sandbox_bootdev_hunt, + .drv = DM_DRIVER_REF(sandbox_bootdev), +}; diff --git a/include/sandbox_host.h b/include/sandbox_host.h index f7a5fc67230..1330358ef7a 100644 --- a/include/sandbox_host.h +++ b/include/sandbox_host.h @@ -8,17 +8,26 @@ #ifndef __SANDBOX_HOST__ #define __SANDBOX_HOST__ +/** + * Device flags. + */ +enum host_platform_flags { + HOST_FLAG_BROKEN = BIT(0), /** Simulate broken device */ +}; + /** * struct host_sb_plat - platform data for a host device * * @label: Label for this device (allocated) * @filename: Name of file this is attached to, or NULL (allocated) * @fd: File descriptor of file, or 0 for none (file is not open) + * @flags: Device flags (e.g. for unit tests). */ struct host_sb_plat { char *label; char *filename; int fd; + unsigned int flags; }; /** @@ -122,4 +131,13 @@ struct udevice *host_get_cur_dev(void); */ void host_set_cur_dev(struct udevice *dev); +/** + * host_set_flags_by_label() - Set the host device test flags + * + * @label: Label of the attachment, e.g. "test1" + * @flags: Device flags + * Returns: 0 if OK, -ve on error + */ +int host_set_flags_by_label(const char *label, unsigned int flags); + #endif /* __SANDBOX_HOST__ */ diff --git a/test/boot/bootdev.c b/test/boot/bootdev.c index 0820bf10ee0..c2eaf0b2c55 100644 --- a/test/boot/bootdev.c +++ b/test/boot/bootdev.c @@ -384,19 +384,19 @@ static int bootdev_test_hunter(struct unit_test_state *uts) ut_assert_nextline(" 2 mmc mmc_bootdev"); ut_assert_nextline(" 4 nvme nvme_bootdev"); ut_assert_nextline(" 4 qfw qfw_bootdev"); + ut_assert_nextline(" 4 host sandbox_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_nextline("(total hunters: 10)"); ut_assert_console_end(); ut_assertok(bootdev_hunt("usb1", false)); ut_assert_skip_to_line("Bus usb@1: 5 USB Device(s) found"); ut_assert_console_end(); - /* USB is 8th in the list, so bit 7 */ - ut_asserteq(BIT(7), std->hunters_used); + ut_asserteq(BIT(USB_HUNTER), std->hunters_used); return 0; } @@ -417,7 +417,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts) 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_skip_to_line("(total hunters: 10)"); ut_assert_console_end(); /* Use the MMC hunter and see that it updates */ @@ -425,7 +425,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts) 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_skip_to_line("(total hunters: 10)"); ut_assert_console_end(); /* Scan all hunters */ @@ -441,6 +441,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts) ut_assert_nextline("Hunting with: nvme"); ut_assert_nextline("Hunting with: qfw"); + ut_assert_nextline("Hunting with: host"); ut_assert_nextline("Hunting with: scsi"); ut_assert_nextline("scanning bus for devices..."); ut_assert_skip_to_line("Hunting with: spi_flash"); @@ -458,11 +459,12 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts) ut_assert_nextline(" 2 * mmc mmc_bootdev"); ut_assert_nextline(" 4 * nvme nvme_bootdev"); ut_assert_nextline(" 4 * qfw qfw_bootdev"); + ut_assert_nextline(" 4 * host sandbox_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_nextline("(total hunters: 10)"); ut_assert_console_end(); ut_asserteq(GENMASK(MAX_HUNTER, 0), std->hunters_used); @@ -646,8 +648,7 @@ static int bootdev_test_next_label(struct unit_test_state *uts) ut_asserteq_str("scsi.id0lun0.bootdev", dev->name); ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS, mflags); - /* SCSI is 6th in the list, so bit 5 */ - ut_asserteq(BIT(MMC_HUNTER) | BIT(5), std->hunters_used); + ut_asserteq(BIT(MMC_HUNTER) | BIT(SCSI_HUNTER), std->hunters_used); ut_assertok(bootdev_next_label(&iter, &dev, &mflags)); ut_assert_console_end(); @@ -657,7 +658,7 @@ static int bootdev_test_next_label(struct unit_test_state *uts) mflags); /* dhcp: Ethernet is first so bit 0 */ - ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used); + ut_asserteq(BIT(MMC_HUNTER) | BIT(SCSI_HUNTER) | BIT(0), std->hunters_used); ut_assertok(bootdev_next_label(&iter, &dev, &mflags)); ut_assert_console_end(); @@ -667,7 +668,7 @@ static int bootdev_test_next_label(struct unit_test_state *uts) mflags); /* pxe: Ethernet is first so bit 0 */ - ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used); + ut_asserteq(BIT(MMC_HUNTER) | BIT(SCSI_HUNTER) | BIT(0), std->hunters_used); mflags = 123; ut_asserteq(-ENODEV, bootdev_next_label(&iter, &dev, &mflags)); @@ -675,7 +676,7 @@ static int bootdev_test_next_label(struct unit_test_state *uts) ut_assert_console_end(); /* no change */ - ut_asserteq(BIT(MMC_HUNTER) | BIT(5) | BIT(0), std->hunters_used); + ut_asserteq(BIT(MMC_HUNTER) | BIT(SCSI_HUNTER) | BIT(0), std->hunters_used); return 0; } diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 56ee1952357..1cc137c9700 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -19,6 +19,8 @@ #include #ifdef CONFIG_SANDBOX #include +#include +#include #endif #include #include @@ -1532,3 +1534,48 @@ static int bootstd_images(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootstd_images, UTF_CONSOLE); + +#if defined(CONFIG_SANDBOX) && defined(CONFIG_BOOTMETH_GLOBAL) +/* + * Check that bootdev scanning does not stop if higher-priority bootdevs + * are failed to be hunted. + */ +static int bootdev_hunt_fallthrough(struct unit_test_state *uts) +{ + struct bootstd_priv *std; + struct udevice *dev; + + ut_assertok(bootstd_get_priv(&std)); + bootstd_test_drop_bootdev_order(uts); + test_set_skip_delays(true); + bootstd_reset_usb(); + console_record_reset_enable(); + + /* + * Create a sandbox block device (BOOTDEVP_4_SCAN_FAST) and mark it as + * broken so that bootdev_hunt_prio() returns an error. + */ + ut_asserteq(0, uclass_id_count(UCLASS_HOST)); + ut_assertok(host_create_device("test", true, DEFAULT_BLKSZ, &dev)); + ut_assertok(host_set_flags_by_label("test", HOST_FLAG_BROKEN)); + ut_asserteq(1, uclass_id_count(UCLASS_HOST)); + + /* + * Scan with hunting. + * The sandbox hunter at priority 4 must fail, but the USB hunter at + * priority 5 must still be reached. + */ + ut_assertok(run_command("bootflow scan -l", 0)); + + ut_assert(!(std->hunters_used & BIT(HOST_HUNTER))); + ut_assert_skip_to_line("Hunting with: host"); + + /* USB was hunted despite the sandbox hunter failure */ + ut_assert(std->hunters_used & BIT(USB_HUNTER)); + ut_assert_skip_to_line("Bus usb@1: 5 USB Device(s) found"); + + return 0; +} +BOOTSTD_TEST(bootdev_hunt_fallthrough, + UTF_DM | UTF_SCAN_FDT | UTF_SF_BOOTDEV | UTF_CONSOLE); +#endif /* CONFIG_SANDBOX */ diff --git a/test/boot/bootstd_common.h b/test/boot/bootstd_common.h index dd769313a84..672917454a3 100644 --- a/test/boot/bootstd_common.h +++ b/test/boot/bootstd_common.h @@ -21,8 +21,11 @@ #define TEST_VERNUM 0x00010002 enum { - MAX_HUNTER = 8, MMC_HUNTER = 2, /* ID of MMC hunter */ + HOST_HUNTER = 5, + SCSI_HUNTER = 6, + USB_HUNTER = 8, + MAX_HUNTER = 9, }; struct unit_test_state; -- cgit v1.3.1