[Concept,14/16] boot: pxe: Extract FDT fallback logic and add test

Message ID 20260109015323.3411528-15-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>

Extract the FDT address fallback logic from label_boot() into a new
exported function pxe_get_fdt_fallback(). This determines the FDT
address when a label doesn't specify an FDT file:

  1. First tries fdt_addr environment variable
  2. Falls back to fdtcontroladdr (unless kernel is FIT format)

Add a test that verifies the fallback priority and behaviour.

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

 boot/pxe_utils.c    | 55 +++++++++++++++++++++++++++------------------
 include/pxe_utils.h | 14 ++++++++++++
 test/boot/pxe.c     | 49 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 96 insertions(+), 22 deletions(-)
  

Patch

diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c
index fbe4a111453..9034c3d86e7 100644
--- a/boot/pxe_utils.c
+++ b/boot/pxe_utils.c
@@ -396,6 +396,37 @@  skip_overlay:
 }
 #endif
 
+const char *pxe_get_fdt_fallback(struct pxe_label *label, ulong kern_addr)
+{
+	const char *conf_fdt_str = NULL;
+	void *buf;
+
+	/*
+	 * Fallback to fdt_addr env var if label doesn't specify FDT
+	 * and it's not ATAG mode (fdt="-")
+	 */
+	if (!IS_ENABLED(CONFIG_SUPPORT_PASSING_ATAGS) ||
+	    !label->fdt || strcmp("-", label->fdt)) {
+		conf_fdt_str = env_get("fdt_addr");
+		if (conf_fdt_str)
+			return conf_fdt_str;
+	}
+
+	/*
+	 * Fallback to fdtcontroladdr if not a FIT image and not ATAG mode
+	 */
+	buf = map_sysmem(kern_addr, 0);
+	if (genimg_get_format(buf) != IMAGE_FORMAT_FIT) {
+		if (!IS_ENABLED(CONFIG_SUPPORT_PASSING_ATAGS) ||
+		    !label->fdt || strcmp("-", label->fdt)) {
+			conf_fdt_str = env_get("fdtcontroladdr");
+		}
+	}
+	unmap_sysmem(buf);
+
+	return conf_fdt_str;
+}
+
 /*
  * label_process_fdt() - Process FDT for the label
  *
@@ -789,28 +820,8 @@  static int label_boot(struct pxe_context *ctx, struct pxe_label *label)
 	if (ret)
 		return ret;
 
-	if (!conf_fdt_str) {
-		if (!IS_ENABLED(CONFIG_SUPPORT_PASSING_ATAGS) ||
-		    strcmp("-", label->fdt)) {
-			conf_fdt_str = env_get("fdt_addr");
-			log_debug("using fdt_addr '%s'\n", conf_fdt_str);
-		}
-	}
-
-	if (!conf_fdt_str) {
-		void *buf;
-
-		buf = map_sysmem(kern_addr, 0);
-		if (genimg_get_format(buf) != IMAGE_FORMAT_FIT) {
-			if (!IS_ENABLED(CONFIG_SUPPORT_PASSING_ATAGS) ||
-			    strcmp("-", label->fdt)) {
-				conf_fdt_str = env_get("fdtcontroladdr");
-				log_debug("using fdtcontroladdr '%s'\n",
-					  conf_fdt_str);
-			}
-		}
-		unmap_sysmem(buf);
-	}
+	if (!conf_fdt_str)
+		conf_fdt_str = pxe_get_fdt_fallback(label, kern_addr);
 	if (conf_fdt_str)
 		conf_fdt = hextoul(conf_fdt_str, NULL);
 	log_debug("conf_fdt %lx\n", conf_fdt);
diff --git a/include/pxe_utils.h b/include/pxe_utils.h
index 7ecb5788d0b..9629f051a91 100644
--- a/include/pxe_utils.h
+++ b/include/pxe_utils.h
@@ -306,6 +306,20 @@  int pxe_process(struct pxe_context *ctx, ulong pxefile_addr_r, bool prompt);
  */
 int pxe_get_file_size(ulong *sizep);
 
+/**
+ * pxe_get_fdt_fallback() - Get the FDT address using fallback logic
+ *
+ * When a label doesn't specify an FDT file (via 'fdt' or 'fdtdir'), this
+ * function determines the FDT address using fallback environment variables:
+ *   1. fdt_addr - if set, use this address
+ *   2. fdtcontroladdr - if set and kernel is not FIT format
+ *
+ * @label: Label being processed
+ * @kern_addr: Address where kernel is loaded
+ * Return: FDT address string from environment, or NULL if no fallback available
+ */
+const char *pxe_get_fdt_fallback(struct pxe_label *label, ulong kern_addr);
+
 /**
  * pxe_get() - Get the PXE file from the server
  *
diff --git a/test/boot/pxe.c b/test/boot/pxe.c
index fec2361c27f..505628ab92d 100644
--- a/test/boot/pxe.c
+++ b/test/boot/pxe.c
@@ -660,3 +660,52 @@  static int pxe_test_ipappend_norun(struct unit_test_state *uts)
 PXE_TEST_ARGS(pxe_test_ipappend_norun, UTF_CONSOLE | UTF_MANUAL | UTF_ETH_BOOTDEV,
 	{ "fs_image", UT_ARG_STR },
 	{ "cfg_path", UT_ARG_STR });
+
+/**
+ * Test pxe_get_fdt_fallback() function
+ *
+ * This tests the FDT address fallback logic when a label doesn't specify
+ * an FDT file via 'fdt' or 'fdtdir' keywords.
+ */
+static int pxe_test_fdt_fallback(struct unit_test_state *uts)
+{
+	const char *orig_fdt_addr, *orig_fdtcontroladdr;
+	ulong kern_addr = 0x1000000;
+	struct pxe_label label;
+	void *kern_buf;
+
+	/* Create a dummy kernel buffer (not FIT format) */
+	kern_buf = map_sysmem(kern_addr, 64);
+	memset(kern_buf, '\0', 64);
+	unmap_sysmem(kern_buf);
+
+	memset(&label, '\0', sizeof(label));
+
+	/* Save and clear env vars (fdtcontroladdr is set by U-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));
+
+	/* Test 1: No fallback env vars set - should return NULL */
+	ut_assertnull(pxe_get_fdt_fallback(&label, kern_addr));
+
+	/* Test 2: fdt_addr set - should return fdt_addr */
+	ut_assertok(env_set_hex("fdt_addr", 0x2000000));
+	ut_asserteq_str("2000000", pxe_get_fdt_fallback(&label, kern_addr));
+
+	/* Test 3: Both set - fdt_addr takes priority */
+	ut_assertok(env_set_hex("fdtcontroladdr", 0x3000000));
+	ut_asserteq_str("2000000", pxe_get_fdt_fallback(&label, kern_addr));
+
+	/* Test 4: Only fdtcontroladdr set - should return fdtcontroladdr */
+	ut_assertok(env_set("fdt_addr", NULL));
+	ut_asserteq_str("3000000", pxe_get_fdt_fallback(&label, kern_addr));
+
+	/* Restore env vars */
+	ut_assertok(env_set("fdt_addr", orig_fdt_addr));
+	ut_assertok(env_set("fdtcontroladdr", orig_fdtcontroladdr));
+
+	return 0;
+}
+PXE_TEST(pxe_test_fdt_fallback, 0);