[Concept,5/8] test: Add bootflow test for BLS bootmethod

Message ID 20260213202417.223068-6-sjg@u-boot.org
State New
Headers
Series Add BLS Type #1 bootmethod with FIT and multi-initrd support |

Commit Message

Simon Glass Feb. 13, 2026, 8:24 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add a test that verifies the BLS bootmethod can discover and boot from
a Boot Loader Specification entry file.

The test creates a disk image (mmc15) with a BLS entry at
loader/entry.conf and verifies that bootflow scan finds it and reports
it correctly.

Test coverage:
- Disk image creation with BLS entry format
- Bootflow discovery via 'bootflow scan'
- Bootflow listing and info display
- Integration with standard bootdev framework

The disk image setup creates a minimal BLS Type #1 entry with kernel,
initrd, devicetree, and boot options.

Co-developed-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 arch/sandbox/dts/test.dts |  7 ++++
 test/boot/bootflow.c      | 54 ++++++++++++++++++++++++++++++
 test/py/img/bls.py        | 70 +++++++++++++++++++++++++++++++++++++++
 test/py/tests/test_ut.py  |  2 ++
 4 files changed, 133 insertions(+)
 create mode 100644 test/py/img/bls.py
  

Patch

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 33c25014e94..90167567757 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1296,6 +1296,13 @@ 
 		filename = "mmc14.img";
 	};
 
+	/* This is used for BLS tests */
+	mmc15 {
+		status = "disabled";
+		compatible = "sandbox,mmc";
+		filename = "mmc15.img";
+	};
+
 	pch {
 		compatible = "sandbox,pch";
 	};
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index 9a9cc68fa11..e1e50319740 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -36,6 +36,7 @@  DECLARE_GLOBAL_DATA_PTR;
 extern U_BOOT_DRIVER(bootmeth_android);
 extern U_BOOT_DRIVER(bootmeth_cros);
 extern U_BOOT_DRIVER(bootmeth_2script);
+extern U_BOOT_DRIVER(bootmeth_2bls);
 
 /* Use this as the vendor for EFI to tell the app to exit boot services */
 static u16 __efi_runtime_data test_vendor[] = u"U-Boot testing";
@@ -684,6 +685,13 @@  static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
 					"android", 0, ofnode_null(), &dev));
 	}
 
+	/* Enable the BLS bootmeth if needed */
+	if (IS_ENABLED(CONFIG_BOOTMETH_BLS) && bind_cros_android) {
+		ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+		ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_2bls),
+					"bls", 0, ofnode_null(), &dev));
+	}
+
 	/* Change the order to include the device */
 	std = dev_get_priv(bootstd);
 	old_order = std->bootdev_order;
@@ -1809,3 +1817,49 @@  static int bootflow_cmd_info_encrypted(struct unit_test_state *uts)
 	return 0;
 }
 BOOTSTD_TEST(bootflow_cmd_info_encrypted, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Check 'bootflow scan' finds a BLS bootflow */
+static int bootflow_cmd_bls(struct unit_test_state *uts)
+{
+	struct bootstd_priv *std;
+	const char **old_order;
+
+	ut_assertok(prep_mmc_bootdev(uts, "mmc15", true, &old_order));
+	ut_assertok(run_command("bootflow scan", 0));
+	ut_assert_console_end();
+
+	/* Restore the order used by the device tree */
+	ut_assertok(bootstd_get_priv(&std));
+	free(std->bootdev_order);
+	std->bootdev_order = old_order;
+
+	ut_assertok(run_command("bootflow list", 0));
+	ut_assert_nextline("Showing all bootflows");
+	ut_assert_nextline("Seq  Method       State   Uclass    Part  E  Name                      Filename");
+	ut_assert_nextlinen("---");
+	ut_assert_nextlinen("  0  extlinux");
+	ut_assert_nextline("  1  bls          ready   mmc          1     mmc15.bootdev.part_1      /loader/entry.conf");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(2 bootflows, 2 valid)");
+	ut_assert_console_end();
+
+	/* Select the BLS bootflow and check info */
+	ut_assertok(run_command("bootflow select 1", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootflow info", 0));
+	ut_assert_nextline("Name:      mmc15.bootdev.part_1");
+	ut_assert_nextline("Device:    mmc15.bootdev");
+	ut_assert_nextline("Block dev: mmc15.blk");
+	ut_assert_nextline("Method:    bls");
+	ut_assert_nextline("State:     ready");
+	ut_assert_nextline("Partition: 1");
+	if (IS_ENABLED(CONFIG_BLK_LUKS))
+		ut_assert_nextline("Encrypted: no");
+	ut_assert_nextline("Subdir:    (none)");
+	ut_assert_nextline("Filename:  /loader/entry.conf");
+	ut_assert_skip_to_line("Error:     0");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_bls, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
diff --git a/test/py/img/bls.py b/test/py/img/bls.py
new file mode 100644
index 00000000000..0f55aad8ba5
--- /dev/null
+++ b/test/py/img/bls.py
@@ -0,0 +1,70 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2026 Canonical Ltd
+
+"""Create BLS test disk images"""
+
+import gzip
+import os
+
+import utils
+from fs_helper import DiskHelper, FsHelper
+from img.common import mkdir_cond
+
+
+def setup_bls_image(config, log, devnum, basename):
+    """Create a 20MB BLS disk image with a single FAT partition
+
+    Args:
+        config (ArbitraryAttributeContainer): Configuration
+        log (multiplexed_log.Logfile): Log to write to
+        devnum (int): Device number to use, e.g. 5
+        basename (str): Base name to use in the filename, e.g. 'mmc'
+    """
+    vmlinux = 'vmlinuz-6.8.0'
+    initrd = 'initrd.img-6.8.0'
+    dtb = 'sandbox.dtb'
+
+    # BLS Type #1 entry format
+    script = f'''title Test Boot
+version 6.8.0
+linux /{vmlinux}
+options root=/dev/mmcblk0p2 ro quiet
+initrd /{initrd}
+devicetree /{dtb}'''
+
+    fsh = FsHelper(config, 'vfat', 18, prefix=basename)
+    fsh.setup()
+
+    # Create loader directory for BLS entry
+    loader = os.path.join(fsh.srcdir, 'loader')
+    mkdir_cond(loader)
+
+    # Create BLS entry file
+    conf = os.path.join(loader, 'entry.conf')
+    with open(conf, 'w', encoding='ascii') as fd:
+        print(script, file=fd)
+
+    # Create compressed kernel image
+    inf = os.path.join(config.persistent_data_dir, 'inf')
+    with open(inf, 'wb') as fd:
+        fd.write(gzip.compress(b'vmlinux'))
+    mkimage = config.build_dir + '/tools/mkimage'
+    utils.run_and_log_no_ubman(
+        log, f'{mkimage} -f auto -d {inf} {os.path.join(fsh.srcdir, vmlinux)}')
+
+    # Create initrd file
+    with open(os.path.join(fsh.srcdir, initrd), 'w', encoding='ascii') as fd:
+        print('initrd', file=fd)
+
+    # Create device tree blob
+    dtb_file = os.path.join(fsh.srcdir, dtb)
+    utils.run_and_log_no_ubman(
+        log, f'dtc -o {dtb_file}', stdin=b'/dts-v1/; / {};')
+
+    fsh.mk_fs()
+
+    # Create disk image with single bootable partition
+    img = DiskHelper(config, devnum, basename)
+    img.add_fs(fsh, DiskHelper.VFAT, bootable=True)
+    img.create()
+    fsh.cleanup()
diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
index da36898d803..e64ccd407a0 100644
--- a/test/py/tests/test_ut.py
+++ b/test/py/tests/test_ut.py
@@ -23,6 +23,7 @@  from img.common import mkdir_cond, copy_partition, setup_extlinux_image
 from img.fedora import setup_fedora_image
 from img.ubuntu import setup_ubuntu_image
 from img.armbian import setup_bootmenu_image
+from img.bls import setup_bls_image
 from img.chromeos import setup_cros_image
 from img.android import setup_android_image
 from img.efi import setup_efi_image
@@ -79,6 +80,7 @@  def test_ut_dm_init_bootstd(u_boot_config, u_boot_log):
         u_boot_log (multiplexed_log.Logfile): Log to write to
     """
     setup_fedora_image(u_boot_config, u_boot_log, 1, 'mmc')
+    setup_bls_image(u_boot_config, u_boot_log, 15, 'mmc')
     setup_bootmenu_image(u_boot_config, u_boot_log)
     setup_cedit_file(u_boot_config, u_boot_log)
     setup_cros_image(u_boot_config, u_boot_log)