Add a simple unit test for testing the RAUC bootmethod. Provide only the very basic tests for now, running a scan and list, to verify correct detection of the RAUC bootmethod. More advanced boot tests of this bootmethod can be added in a separate patch.
This requires another mmc image (mmc9) to contain the following partitions: 1. boot A: contains a dummy boot.scr 2. root A: contains an empty root filesystem 3. boot B: contains a dummy boot.scr 4. root B: contains an empty root filesystem The bootmeth_rauc scans all four partitions for existence and expects a boot script in each boot partition. Also add BOOTMETH_RAUC as a dependency on sandbox so that we can test this with: $ ./test/py/test.py -B sandbox --build -k test_ut # build the mmc9.img $ ./test/py/test.py -B sandbox --build -k bootflow_rauc Signed-off-by: Martin Schwan <[email protected]> --- arch/sandbox/dts/test.dts | 8 +++++++ configs/sandbox_defconfig | 1 + test/boot/bootflow.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++ test/py/tests/test_ut.py | 45 +++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index a2c739a2044c728a96b5f4acbf439a42d7e12fb5..25d1a8299c6462295edbe7bdade167c96ac7e9bf 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -46,6 +46,7 @@ mmc6 = "/mmc6"; mmc7 = "/mmc7"; mmc8 = "/mmc8"; + mmc9 = "/mmc9"; pci0 = &pci0; pci1 = &pci1; pci2 = &pci2; @@ -1230,6 +1231,13 @@ filename = "mmc8.img"; }; + /* This is used for RAUC boot tests */ + mmc9 { + status = "disabled"; + compatible = "sandbox,mmc"; + filename = "mmc9.img"; + }; + pch { compatible = "sandbox,pch"; }; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 23651ec764dbc2d83be62cc0d91216eebd91acc5..b7a5d81bb71d9dd58ba20c33049d1b4534ea21f3 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -23,6 +23,7 @@ CONFIG_FIT_RSASSA_PSS=y CONFIG_FIT_CIPHER=y CONFIG_FIT_VERBOSE=y CONFIG_BOOTMETH_ANDROID=y +CONFIG_BOOTMETH_RAUC=y CONFIG_UPL=y CONFIG_LEGACY_IMAGE_FORMAT=y CONFIG_MEASURED_BOOT=y diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 8de5a310add38798f3a52ea6cd796bdffdb16bd6..d3d1d410a6de349f880a4fb06cbc41804dc6a9a8 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -31,6 +31,7 @@ DECLARE_GLOBAL_DATA_PTR; extern U_BOOT_DRIVER(bootmeth_android); extern U_BOOT_DRIVER(bootmeth_cros); +extern U_BOOT_DRIVER(bootmeth_rauc); extern U_BOOT_DRIVER(bootmeth_2script); /* Use this as the vendor for EFI to tell the app to exit boot services */ @@ -1332,6 +1333,62 @@ static int bootflow_efi(struct unit_test_state *uts) } BOOTSTD_TEST(bootflow_efi, UTF_CONSOLE); +/* Test RAUC bootmeth */ +static int bootflow_rauc(struct unit_test_state *uts) +{ + const char *mmc_dev = "mmc9"; + struct bootstd_priv *std; + struct udevice *bootstd; + static const char *order[] = {NULL, NULL}; + const char **old_order; + ofnode root; + ofnode node; + + order[0] = mmc_dev; + + if (!CONFIG_IS_ENABLED(BOOTMETH_RAUC)) + return -EAGAIN; + + /* Enable the requested mmc node since we need a different bootflow */ + root = oftree_root(oftree_default()); + node = ofnode_find_subnode(root, mmc_dev); + ut_assert(ofnode_valid(node)); + ut_assertok(lists_bind_fdt(gd->dm_root, node, NULL, NULL, false)); + + /* Enable the rauc bootmeth */ + ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); + ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_rauc), + "rauc", 0, ofnode_null(), NULL)); + + /* Change the device and bootmeth order */ + std = dev_get_priv(bootstd); + old_order = std->bootdev_order; + std->bootdev_order = order; + + ut_assertok(bootmeth_set_order("rauc")); + + /* Run scan and list */ + ut_assertok(run_command("bootflow scan", 0)); + ut_assert_console_end(); + + ut_assertok(run_command("bootflow list", 0)); + + ut_assert_nextlinen("Showing all"); + ut_assert_nextlinen("Seq"); + ut_assert_nextlinen("---"); + ut_assert_nextlinen(" 0 rauc ready mmc 0 mmc9.bootdev.whole "); + ut_assert_nextlinen("---"); + ut_assert_skip_to_line("(1 bootflow, 1 valid)"); + + ut_assert_console_end(); + + /* Restore the order used by the device tree */ + std->bootdev_order = old_order; + + return 0; +} +BOOTSTD_TEST(bootflow_rauc, UTF_CONSOLE | UTF_DM | UTF_SCAN_FDT); + /* Check 'bootflow scan' provides a list of images */ static int bootstd_images(struct unit_test_state *uts) { diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index cdf54adc6008c21d67eec466c3245c75208a2ec9..2e6b050ec2bafc765d1f7bb0c94a553b51ea69c2 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -547,6 +547,50 @@ def setup_efi_image(ubman): utils.run_and_log(ubman, f'rm -rf {mnt}') utils.run_and_log(ubman, f'rm -f {fsfile}') +def setup_rauc_image(ubman): + """Create a 40MB disk image with an A/B RAUC system on it""" + mmc_dev = 9 + fname = os.path.join(ubman.config.source_dir, f'mmc{mmc_dev}.img') + mnt = os.path.join(ubman.config.persistent_data_dir, 'scratch') + + spec = 'type=c, size=8M, start=1M, bootable\n' \ + 'type=c, size=10M\n' \ + 'type=c, size=8M, bootable\n' \ + 'type=c, size=10M' + + utils.run_and_log(ubman, f'qemu-img create {fname} 40M') + utils.run_and_log(ubman, ['sh', '-c', f'printf "{spec}" | sfdisk {fname}']) + + # Generate boot script + script = '# dummy boot script' + bootdir = os.path.join(mnt, 'boot') + utils.run_and_log(ubman, f'mkdir -p {bootdir}') + cmd_fname = os.path.join(bootdir, 'boot.cmd') + scr_fname = os.path.join(bootdir, 'boot.scr') + with open(cmd_fname, 'w', encoding='ascii') as outf: + print(script, file=outf) + + mkimage = os.path.join(ubman.config.build_dir, 'tools/mkimage') + utils.run_and_log( + ubman, f'{mkimage} -C none -A arm -T script -d {cmd_fname} {scr_fname}') + + # Create boot filesystem image with boot script in it and copy to disk image + fsfile = 'rauc_boot.img' + utils.run_and_log(ubman, f'fallocate -l 8M {fsfile}') + utils.run_and_log(ubman, f'mkfs.vfat {fsfile}') + utils.run_and_log(ubman, ['sh', '-c', f'mcopy -vs -i {fsfile} {scr_fname} ::/']) + utils.run_and_log(ubman, f'dd if={fsfile} of=mmc{mmc_dev}.img bs=1M seek=1 conv=notrunc') + utils.run_and_log(ubman, f'dd if={fsfile} of=mmc{mmc_dev}.img bs=1M seek=19 conv=notrunc') + utils.run_and_log(ubman, f'rm -f {fsfile} {cmd_fname} {scr_fname}') + + # Create empty root filesystem image and copy to disk image + fsfile = 'rauc_root.img' + utils.run_and_log(ubman, f'fallocate -l 10M {fsfile}') + utils.run_and_log(ubman, f'mkfs.vfat {fsfile}') + utils.run_and_log(ubman, f'dd if={fsfile} of=mmc{mmc_dev}.img bs=1M seek=9 conv=notrunc') + utils.run_and_log(ubman, f'dd if={fsfile} of=mmc{mmc_dev}.img bs=1M seek=27 conv=notrunc') + utils.run_and_log(ubman, f'rm -f {fsfile}') + @pytest.mark.buildconfigspec('cmd_bootflow') @pytest.mark.buildconfigspec('sandbox') def test_ut_dm_init_bootstd(ubman): @@ -558,6 +602,7 @@ def test_ut_dm_init_bootstd(ubman): setup_cros_image(ubman) setup_android_image(ubman) setup_efi_image(ubman) + setup_rauc_image(ubman) # Restart so that the new mmc1.img is picked up ubman.restart_uboot() --- base-commit: dc6c80056e21a27e28e1be62ac724302ef526841 change-id: 20251006-wip-bootmeth-rauc-tests-a8ed5deb7486 Best regards, -- Martin Schwan <[email protected]>

