[Concept,08/32] test: pxe: Add test for FIT images with embedded FDT

Message ID 20260109231151.4056804-9-sjg@u-boot.org
State New
Headers
Series boot: pxe: Refactor into separate load/setup phases |

Commit Message

Simon Glass Jan. 9, 2026, 11:11 p.m. UTC
  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(+)
  

Patch

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)