[Concept,16/16] test: pxe: Add test for the address-allocation path

Message ID 20260109015323.3411528-17-sjg@u-boot.org
State New
Headers
Series test: pxe: Add some decent tests for the PXE/extlinux parser |

Commit Message

Simon Glass Jan. 9, 2026, 1:53 a.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add a test that verifies the LMB allocation path works correctly when no
address environment variables are set. The test uses a custom getfile
callback that:

- Checks if *addrp is 0 (no env var was provided)
- Assigns incrementing addresses (0x100, 0x200, etc.) to simulate LMB
  allocation

The test verifies all pxe_context fields are set correctly after
pxe_probe():

- Context setup fields (getfile, userdata, allow_abs_path, etc.)
- Label selection (correct label chosen)
- Kernel address and size
- Initrd address and size
- FDT fields (NULL/0 since fdt_addr_r not set)
- Boot flags (restart, fake_go)

Also reduce test fixture file sizes to minimal (just the identifier
strings, without padding).

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

 test/boot/pxe.c                  | 162 +++++++++++++++++++++++++++++++
 test/py/tests/test_pxe_parser.py |   9 +-
 2 files changed, 169 insertions(+), 2 deletions(-)
  

Patch

diff --git a/test/boot/pxe.c b/test/boot/pxe.c
index a6761ecc447..9398cf5757f 100644
--- a/test/boot/pxe.c
+++ b/test/boot/pxe.c
@@ -16,6 +16,7 @@ 
 #include <env.h>
 #include <fdt_support.h>
 #include <fs_legacy.h>
+#include <image.h>
 #include <linux/libfdt.h>
 #include <mapmem.h>
 #include <net-common.h>
@@ -760,3 +761,164 @@  static int pxe_test_label_override_norun(struct unit_test_state *uts)
 PXE_TEST_ARGS(pxe_test_label_override_norun, UTF_CONSOLE | UTF_MANUAL,
 	{ "fs_image", UT_ARG_STR },
 	{ "cfg_path", UT_ARG_STR });
+
+/**
+ * struct pxe_alloc_info - context for the alloc test getfile callback
+ *
+ * @uts: Unit test state for assertions
+ * @next_addr: Next address to allocate (increments by 0x100 each call)
+ */
+struct pxe_alloc_info {
+	struct unit_test_state *uts;
+	ulong next_addr;
+};
+
+/**
+ * pxe_alloc_getfile() - Read a file, allocating address if not provided
+ *
+ * For files loaded via env vars (kernel, initrd, fdt), this verifies that
+ * *addrp is 0 (no environment variable set), then assigns an incrementing
+ * address to simulate LMB allocation. For the config file (which is loaded
+ * with a direct address), it just uses the provided address.
+ */
+static int pxe_alloc_getfile(struct pxe_context *ctx, const char *file_path,
+			     ulong *addrp, ulong align,
+			     enum bootflow_img_t type, ulong *sizep)
+{
+	struct pxe_alloc_info *info = ctx->userdata;
+	loff_t len_read;
+	int ret;
+
+	/*
+	 * Config file is loaded with direct address (non-zero).
+	 * Kernel/initrd/fdt/overlays come through env vars - if not set,
+	 * addrp will be 0 and we need to allocate.
+	 */
+	if (!*addrp) {
+		*addrp = info->next_addr;
+		info->next_addr += 0x100;
+	}
+
+	ret = fs_set_blk_dev("host", "0:0", FS_TYPE_ANY);
+	if (ret)
+		return ret;
+	ret = fs_legacy_read(file_path, *addrp, 0, 0, &len_read);
+	if (ret)
+		return ret;
+	*sizep = len_read;
+
+	return 0;
+}
+
+/**
+ * Test file loading with no address environment variables
+ *
+ * This tests the LMB allocation path where if no address env var is set,
+ * the getfile callback receives *addrp == 0 and must allocate memory.
+ * Our test callback assigns incrementing addresses (0x100, 0x200, etc.)
+ * and verifies the addresses are then stored in ctx.
+ */
+static int pxe_test_alloc_norun(struct unit_test_state *uts)
+{
+	const char *orig_fdt_addr, *orig_fdtcontroladdr;
+	const char *fs_image = ut_str(PXE_ARG_FS_IMAGE);
+	const char *cfg_path = ut_str(PXE_ARG_CFG_PATH);
+	struct pxe_alloc_info info;
+	struct pxe_context ctx;
+	ulong addr;
+	int ret;
+
+	ut_assertnonnull(fs_image);
+	ut_assertnonnull(cfg_path);
+
+	info.uts = uts;
+	info.next_addr = 0x100;
+
+	/* Bind the filesystem image */
+	ut_assertok(run_commandf("host bind 0 %s", fs_image));
+
+	/* Save and clear FDT fallback env vars (fdtcontroladdr is set at boot) */
+	orig_fdt_addr = env_get("fdt_addr");
+	orig_fdtcontroladdr = env_get("fdtcontroladdr");
+	ut_assertok(env_set("fdt_addr", NULL));
+	ut_assertok(env_set("fdtcontroladdr", NULL));
+
+	/* Ensure address env vars are NOT set */
+	ut_assertok(env_set("kernel_addr_r", NULL));
+	ut_assertok(env_set("ramdisk_addr_r", NULL));
+	ut_assertok(env_set("fdt_addr_r", NULL));
+	ut_assertok(env_set("fdtoverlay_addr_r", NULL));
+	ut_assertok(env_set("pxe_timeout", "1"));
+
+	/* Set up the PXE context with our allocating getfile */
+	ut_assertok(pxe_setup_ctx(&ctx, pxe_alloc_getfile, &info, true,
+				  cfg_path, false, false, NULL));
+
+	/* Read the config file - use a fixed address for parsing */
+	addr = PXE_LOAD_ADDR;
+	ret = get_pxe_file(&ctx, cfg_path, addr);
+	ut_asserteq(1, ret);
+
+	/* Parse and probe - this triggers file loading */
+	ut_assertok(pxe_probe(&ctx, addr, false));
+
+	/*
+	 * Verify all pxe_context fields are set correctly.
+	 *
+	 * The background BMP is loaded first (0x100), then the default
+	 * label 'linux' loads: kernel (0x200), initrd (0x300).
+	 *
+	 * Note: FDT loading requires fdt_addr_r to be set (checked in
+	 * label_process_fdt before attempting to load), so conf_fdt_str
+	 * and conf_fdt are NULL/0.
+	 */
+
+	/* Context setup fields */
+	ut_asserteq_ptr(pxe_alloc_getfile, ctx.getfile);
+	ut_asserteq_ptr(&info, ctx.userdata);
+	ut_asserteq(true, ctx.allow_abs_path);
+	ut_assertnonnull(ctx.bootdir);
+	ut_asserteq(0, ctx.pxe_file_size);  /* only set by cmd/pxe.c */
+	ut_asserteq(false, ctx.use_ipv6);
+	ut_asserteq(false, ctx.use_fallback);
+	ut_asserteq(true, ctx.no_boot);
+	ut_assertnull(ctx.bflow);
+	ut_assertnonnull(ctx.cfg);
+
+	/* BMP loaded first */
+	ut_asserteq(0x100, image_load_addr);
+
+	/* Label selection */
+	ut_assertnonnull(ctx.label);
+	ut_asserteq_str("linux", ctx.label->name);
+
+	/* Kernel */
+	ut_asserteq_str("200", ctx.kern_addr_str);
+	ut_asserteq(0x200, ctx.kern_addr);
+	ut_asserteq(6, ctx.kern_size);
+
+	/* Initrd */
+	ut_asserteq(0x300, ctx.initrd_addr);
+	ut_asserteq(7, ctx.initrd_size);
+	ut_asserteq_str("300:7", ctx.initrd_str);
+
+	/* FDT (not loaded - no fdt_addr_r env var) */
+	ut_assertnull(ctx.conf_fdt_str);
+	ut_asserteq(0, ctx.conf_fdt);
+
+	/* Boot flags */
+	ut_asserteq(false, ctx.restart);
+	ut_asserteq(false, ctx.fake_go);
+
+	/* Clean up */
+	destroy_pxe_menu(ctx.cfg);
+	pxe_destroy_ctx(&ctx);
+	ut_assertok(env_set("pxe_timeout", NULL));
+	ut_assertok(env_set("fdt_addr", orig_fdt_addr));
+	ut_assertok(env_set("fdtcontroladdr", orig_fdtcontroladdr));
+
+	return 0;
+}
+PXE_TEST_ARGS(pxe_test_alloc_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 8462b53c8d8..de01f219244 100644
--- a/test/py/tests/test_pxe_parser.py
+++ b/test/py/tests/test_pxe_parser.py
@@ -320,7 +320,6 @@  def pxe_fdtdir_image(u_boot_config):
     # Create dummy kernel
     with open(os.path.join(fsh.srcdir, 'vmlinuz'), 'wb') as fd:
         fd.write(b'kernel')
-        fd.write(b'\x00' * (1024 - 6))
 
     fsh.mk_fs()
 
@@ -381,7 +380,6 @@  def pxe_error_image(u_boot_config):
     # Create dummy kernel
     with open(os.path.join(fsh.srcdir, 'vmlinuz'), 'wb') as fd:
         fd.write(b'kernel')
-        fd.write(b'\x00' * (1024 - 6))
 
     fsh.mk_fs()
 
@@ -443,3 +441,10 @@  class TestPxeParser:
         with ubman.log.section('Test PXE label override'):
             ubman.run_ut('pxe', 'pxe_test_label_override',
                          fs_image=fs_img, cfg_path=cfg_path)
+
+    def test_pxe_alloc(self, ubman, pxe_image):
+        """Test file loading with no address env vars (LMB allocation path)"""
+        fs_img, cfg_path = pxe_image
+        with ubman.log.section('Test PXE alloc'):
+            ubman.run_ut('pxe', 'pxe_test_alloc',
+                         fs_image=fs_img, cfg_path=cfg_path)