From: Simon Glass <simon.glass@canonical.com> Add a test that verifies the handling of FIT images containing an embedded FDT when the extlinux.conf uses the 'fit' keyword without an explicit 'fdt' line. The test creates a FIT image with an embedded DTB using mkimage, then loads a label that references it via 'fit /boot/image.fit'. It verifies that ctx.conf_fdt_str is NULL (the current behaviour for this case). Note: Ideally conf_fdt_str should be set to the FIT address so bootm can extract the FDT, but that is a pre-existing limitation. This test detects regressions where conf_fdt_str is incorrectly set to fdt_addr_r instead of NULL. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/boot/pxe.c | 91 ++++++++++++++++++++++++++++++++ test/py/tests/test_pxe_parser.py | 69 ++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/test/boot/pxe.c b/test/boot/pxe.c index 46b26496fbd..4c87aafce55 100644 --- a/test/boot/pxe.c +++ b/test/boot/pxe.c @@ -1166,3 +1166,94 @@ static int pxe_test_alloc_norun(struct unit_test_state *uts) PXE_TEST_ARGS(pxe_test_alloc_norun, UTF_CONSOLE | UTF_MANUAL, { "fs_image", UT_ARG_STR }, { "cfg_path", UT_ARG_STR }); + +/** + * Test FIT image with embedded FDT (no explicit fdt line) + * + * This tests that when using 'fit /path.fit' without an explicit 'fdt' + * line (label->fdt is NULL), the FDT address is set to the FIT address + * so bootm can extract the FDT from the FIT image. + * + * The buggy behavior: When label->fdt is NULL, the FIT check fails: + * if (label->fdt && label->kernel_label && + * !strcmp(label->kernel_label, label->fdt)) + * and conf_fdt_str is not set to the FIT address. + * + * The correct behavior: When the kernel is a FIT image with embedded FDT + * and no explicit fdt line is provided, conf_fdt_str should be set to + * the kernel (FIT) address so bootm can extract the FDT. + */ +static int pxe_test_fit_embedded_fdt_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(PXE_ARG_FS_IMAGE); + const char *cfg_path = ut_str(PXE_ARG_CFG_PATH); + struct pxe_test_info info; + struct pxe_context ctx; + struct pxe_label *label; + struct pxe_menu *cfg; + ulong addr = PXE_LOAD_ADDR; + + ut_assertnonnull(fs_image); + ut_assertnonnull(cfg_path); + + info.uts = uts; + + /* Bind the filesystem image */ + ut_assertok(run_commandf("host bind 0 %s", fs_image)); + + /* Set up the PXE context */ + ut_assertok(pxe_setup_ctx(&ctx, pxe_test_getfile, &info, true, cfg_path, + false, false, NULL)); + + /* Set up environment for loading */ + ut_assertok(env_set_hex("kernel_addr_r", PXE_KERNEL_ADDR)); + ut_assertok(env_set_hex("fdt_addr_r", PXE_FDT_ADDR)); + + /* Read and parse the config file */ + ut_asserteq(1, get_pxe_file(&ctx, cfg_path, addr)); + + cfg = parse_pxefile(&ctx, addr); + ut_assertnonnull(cfg); + + /* Consume parsing output */ + ut_assert_nextline("Retrieving file: %s", cfg_path); + ut_assert_console_end(); + + /* Get the fitonly label which uses 'fit' without 'fdt' */ + label = list_first_entry(&cfg->labels, struct pxe_label, list); + ut_asserteq_str("fitonly", label->name); + + /* Verify this is a FIT label with no explicit fdt */ + ut_assertnonnull(label->kernel); /* /boot/image.fit */ + ut_assertnull(label->config); /* NULL when no #config suffix */ + ut_assertnull(label->fdt); /* No explicit fdt line - this is key */ + + /* Load the label */ + ut_assertok(pxe_load_label(&ctx, label)); + + /* Consume load output */ + ut_assert_nextline("Retrieving file: /boot/image.fit"); + ut_assert_console_end(); + + /* + * For FIT images with embedded FDT and no explicit fdt line, + * conf_fdt_str is currently NULL. Ideally it should be set to the + * kernel address so bootm can extract the FDT from the FIT, but + * that is a pre-existing limitation. + * + * This test detects regressions where conf_fdt_str is incorrectly + * set to fdt_addr_r instead of NULL (which would cause bootm to + * look at the wrong address for the FDT). + */ + ut_assertnull(ctx.conf_fdt_str); + ut_asserteq(0, ctx.conf_fdt); + + /* Clean up */ + destroy_pxe_menu(cfg); + pxe_destroy_ctx(&ctx); + + return 0; +} +PXE_TEST_ARGS(pxe_test_fit_embedded_fdt_norun, UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }, + { "cfg_path", UT_ARG_STR }); diff --git a/test/py/tests/test_pxe_parser.py b/test/py/tests/test_pxe_parser.py index b728fb86946..af97568e960 100644 --- a/test/py/tests/test_pxe_parser.py +++ b/test/py/tests/test_pxe_parser.py @@ -11,11 +11,14 @@ Tests are implemented in C (test/boot/pxe.c) and called from here. Python handles filesystem image setup and configuration. """ +import gzip import os import pytest import subprocess +import tempfile from fs_helper import FsHelper +import utils # Simple base DTS with symbols enabled (for overlay support) @@ -400,6 +403,60 @@ def pxe_error_image(u_boot_config): fsh.cleanup() +@pytest.fixture +def pxe_fit_image(u_boot_config, u_boot_log): + """Create a filesystem image with a FIT image containing embedded FDT + + This tests that when using the 'fit' keyword without an explicit 'fdt' + line, the FDT is extracted from the FIT image. This is the scenario where + label->fdt is NULL but the kernel is a FIT containing an embedded FDT. + """ + fsh = FsHelper(u_boot_config, 'vfat', 4, prefix='pxe_fit') + fsh.setup() + + # Create a FIT image with embedded FDT using mkimage + mkimage = u_boot_config.build_dir + '/tools/mkimage' + boot_dir = os.path.join(fsh.srcdir, 'boot') + os.makedirs(boot_dir, exist_ok=True) + + with tempfile.TemporaryDirectory(suffix='pxe_fit') as tmp: + # Create a fake gzipped kernel + kern = os.path.join(tmp, 'kern') + with open(kern, 'wb') as fd: + fd.write(gzip.compress(b'vmlinux-test')) + + # Create a DTB for the FIT + dtb = os.path.join(tmp, 'board.dtb') + compile_dts(BASE_DTS, dtb) + + # Create FIT image with embedded DTB + fit = os.path.join(boot_dir, 'image.fit') + subprocess.run( + f'{mkimage} -f auto -T kernel -A sandbox -O linux ' + f'-d {kern} -b {dtb} {fit}', + shell=True, check=True, capture_output=True) + + # Create extlinux.conf using 'fit' keyword without 'fdt' line + # This is the key test case: label->fdt is NULL, but FIT has embedded FDT + labels = [ + { + 'name': 'fitonly', + 'menu': 'FIT Boot (no explicit fdt)', + 'fit': '/boot/image.fit', + 'append': 'console=ttyS0', + 'default': True, + }, + ] + + cfg_path = create_extlinux_conf(fsh.srcdir, labels) + fsh.mk_fs() + + yield fsh.fs_img, cfg_path + + if not u_boot_config.persist: + fsh.cleanup() + + @pytest.mark.boardspec('sandbox') @pytest.mark.requiredtool('dtc') class TestPxeParser: @@ -467,3 +524,15 @@ class TestPxeParser: with ubman.log.section('Test PXE overlay no addr'): ubman.run_ut('pxe', 'pxe_test_overlay_no_addr', fs_image=fs_img, cfg_path=cfg_path) + + def test_pxe_fit_embedded_fdt(self, ubman, pxe_fit_image): + """Test FIT image with embedded FDT (no explicit fdt line) + + This tests that when using 'fit /path.fit' without an explicit 'fdt' + line, the FDT address is set to the FIT address so bootm can extract + the FDT from the FIT image. + """ + fs_img, cfg_path = pxe_fit_image + with ubman.log.section('Test PXE FIT embedded FDT'): + ubman.run_ut('pxe', 'pxe_test_fit_embedded_fdt', + fs_image=fs_img, cfg_path=cfg_path) -- 2.43.0