mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: chalianis1@gmail.com
To: s.hauer@pengutronix.de
Cc: barebox@lists.infradead.org, Chali Anis <chalianis1@gmail.com>,
	Claude Sonnet 5 <noreply@anthropic.com>
Subject: [PATCH v2 1/1] efi: payload: apply barebox fixups to the devicetree passed to Linux
Date: Tue, 25 Aug 2026 01:43:45 +0200	[thread overview]
Message-ID: <20260824234345.84068-2-chalianis1@gmail.com> (raw)
In-Reply-To: <20260824234345.84068-1-chalianis1@gmail.com>

From: Chali Anis <chalianis1@gmail.com>

efi_load_fdt() never ran barebox's fixup/overlay pipeline on the
devicetree it handed to the kernel. When bootm.oftree was set, the
raw file bytes were extracted straight into EFI pages and installed
as-is via loadable_extract_into_buf_full(); of_unflatten_dtb(),
of_fix_tree() and of_flatten_dtb() were never called. When
bootm.oftree was unset, the function just returned early, so there
was no path at all for adopting (and fixing up) a devicetree already
exposed by firmware. Either way, none of barebox's usual fixups --
memory nodes, bootargs, state, overlays, and everything else hung
off of_register_fixup() -- ever reached the tree Linux booted with.

Fix this for both sources: add efi_fdt_find() to locate the FDT
firmware exposes via its EFI configuration table, and have
efi_load_fdt() fall back to it when no bootm.oftree is set. Whichever
tree is in play, unflatten it into data->of_root_node, run it through
the standard bootm_set_pending_oftree_overlays()/of_fix_tree()
sequence, reflatten it, and install the fixed-up result as the UEFI
configuration table -- matching what every other barebox boot path
already guarantees.

While here, size the FDT allocation/free from the actual flattened
tree instead of a fixed 2 MiB buffer, and reuse efi_fdt_find() in the
existing efi_fdt_probe() initcall so both callers share one
EFI-configuration-table lookup instead of duplicating it.

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Chali Anis <chalianis1@gmail.com>
---
 efi/payload/bootm.c   | 60 ++++++++++++++++++++++++++++++-------
 efi/payload/fdt.c     | 34 ++++++++++++++-------
 efi/payload/init.c    | 69 +++++++++++++++++++++++++++++++++++++++++++
 include/efi/payload.h | 10 +++++++
 4 files changed, 152 insertions(+), 21 deletions(-)

diff --git a/efi/payload/bootm.c b/efi/payload/bootm.c
index 2f9cc3cbf76b..b9e1d9438e1c 100644
--- a/efi/payload/bootm.c
+++ b/efi/payload/bootm.c
@@ -111,27 +111,61 @@ static int efi_load_ramdisk(struct image_data *data,
 static int efi_load_fdt(struct image_data *data, void **fdt)
 {
 	efi_physical_addr_t mem;
+	struct fdt_header *oftree;
+	bool is_loadable = true;
 	efi_status_t efiret;
+	size_t size;
 	void *vmem;
-	size_t bufsize = DIV_ROUND_UP(SZ_2M, EFI_PAGE_SIZE);
-	ssize_t ret;
+	int ret;
+
+	if (!data->oftree) {
+		/*
+		 * No devicetree requested; fall back to the one provided by
+		 * firmware (if any) so barebox's fixups still get applied to it.
+		 */
+		oftree = efi_fdt_find(&size);
+		is_loadable = false;
+	} else {
+		oftree = loadable_extract(data->oftree, &size) ?: ERR_PTR(-ENODATA);
+		if (IS_ERR(oftree))
+			pr_warn("Failed to extract oftree\n");
+	}
 
-	if (!data->oftree)
+	if (IS_ERR(oftree))
 		return 0;
 
+	data->of_root_node = of_unflatten_dtb(oftree, size);
+	if (IS_ERR(data->of_root_node)) {
+		data->of_root_node = NULL;
+		pr_err("unable to unflatten devicetree\n");
+		return -EINVAL;
+	}
+
+	if (is_loadable)
+		free(oftree);
+
+	bootm_set_pending_oftree_overlays(data->oftree);
+	of_fix_tree(data->of_root_node);
+	bootm_clear_pending_oftree_overlays();
+
+	oftree = of_flatten_dtb(data->of_root_node);
+	if (!oftree)
+		return -EINVAL;
+
+	fdt_add_reserve_map(oftree);
+
+	size = efi_size_in_pages(fdt_totalsize(oftree) + 0x3000);
 	efiret = BS->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_ACPI_RECLAIM_MEMORY,
-				    bufsize, &mem);
+				    size, &mem);
 	if (EFI_ERROR(efiret)) {
 		pr_err("Failed to allocate pages for FDT: %s\n", efi_strerror(efiret));
-		return -efi_errno(efiret);
+		ret = -efi_errno(efiret);
+		goto free_oftree;
 	}
 
 	vmem = efi_phys_to_virt(mem);
 
-	ret = loadable_extract_into_buf_full(data->oftree, vmem,
-					     bufsize * EFI_PAGE_SIZE);
-	if (ret < 0)
-		goto free_efi_mem;
+	memcpy(vmem, oftree, fdt_totalsize(oftree));
 
 	efiret = BS->install_configuration_table(&efi_fdt_guid, vmem);
 	if (EFI_ERROR(efiret)) {
@@ -140,11 +174,14 @@ static int efi_load_fdt(struct image_data *data, void **fdt)
 		goto free_efi_mem;
 	}
 
+	free(oftree);
 	*fdt = vmem;
 	return 0;
 
 free_efi_mem:
-	BS->free_pages(mem, bufsize);
+	BS->free_pages(mem, size);
+free_oftree:
+	free(oftree);
 	return ret;
 }
 
@@ -153,8 +190,9 @@ static void efi_unload_fdt(void *fdt)
 	if (!fdt)
 		return;
 
+	size_t size = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000);
 	BS->install_configuration_table(&efi_fdt_guid, NULL);
-	BS->free_pages(efi_virt_to_phys(fdt), DIV_ROUND_UP(SZ_2M, EFI_PAGE_SIZE));
+	BS->free_pages(efi_virt_to_phys(fdt), size);
 }
 
 static int do_bootm_efi_stub(struct image_data *data)
diff --git a/efi/payload/fdt.c b/efi/payload/fdt.c
index 9cdb32370f22..80e6a1fcec21 100644
--- a/efi/payload/fdt.c
+++ b/efi/payload/fdt.c
@@ -9,14 +9,14 @@
 #include <efi/payload/init.h>
 #include <efi/guid.h>
 
-static int efi_fdt_probe(void)
+void *efi_fdt_find(size_t *size)
 {
 	struct efi_config_table *ect;
+	*size = 0;
 
 	for_each_efi_config_table(ect) {
 		struct fdt_header *oftree;
-		u32 magic, size;
-		int ret;
+		u32 magic;
 
 		if (efi_guidcmp(ect->guid, EFI_DEVICE_TREE_GUID))
 			continue;
@@ -26,17 +26,31 @@ static int efi_fdt_probe(void)
 
 		if (magic != FDT_MAGIC) {
 			pr_err("table has invalid magic 0x%08x\n", magic);
-			return -EILSEQ;
+			return ERR_PTR(-EILSEQ);
 		}
 
-		size = be32_to_cpu(oftree->totalsize);
-		ret = write_file("/efi.dtb", oftree, size);
-		if (ret) {
-			pr_err("error saving /efi.dtb: %pe\n", ERR_PTR(ret));
-			return ret;
-		}
+		*size = fdt_totalsize(oftree);
+		return oftree;
+	}
+
+	pr_warn("No FDT found in EFI configuration tables\n");
+	return ERR_PTR(-ENODATA);
+}
 
+static int efi_fdt_probe(void)
+{
+	struct fdt_header *oftree;
+	size_t size;
+	int ret;
+
+	oftree = efi_fdt_find(&size);
+	if (IS_ERR(oftree) || !size)
 		return 0;
+
+	ret = write_file("/efi.dtb", oftree, size);
+	if (ret) {
+		pr_err("error saving /efi.dtb: %pe\n", ERR_PTR(ret));
+		return ret;
 	}
 
 	return 0;
diff --git a/efi/payload/init.c b/efi/payload/init.c
index 6d2bd046e4c2..f0ce2a82cefc 100644
--- a/efi/payload/init.c
+++ b/efi/payload/init.c
@@ -203,6 +203,75 @@ static int efi_core_init(void)
 }
 core_efi_initcall(efi_core_init);
 
+/**
+ * efi_of_fixup_firmware_nodes() - import firmware-injected nodes onto @root
+ *
+ * A devicetree supplied out-of-band (e.g. embedded in a FIT image) was
+ * flattened at build time, so it can never carry nodes that firmware only
+ * adds to its own devicetree at runtime - most importantly the
+ * /reserved-memory carve-outs TF-A/OP-TEE add to mark their own
+ * secure-world memory off-limits (as child nodes, e.g. tegra-carveouts on
+ * Tegra), and the /firmware node describing the secure monitor call
+ * conduit. /firmware is copied wholesale, replacing whatever @root already
+ * had there. /reserved-memory is merged child by child instead, since
+ * @root may already carry its own reservations alongside firmware's:
+ * each child firmware's tree has is copied into @root's /reserved-memory
+ * (created if necessary), replacing any child of the same name @root
+ * already had.
+ */
+static int efi_of_fixup_firmware_nodes(struct device_node *root, void *unused)
+{
+	struct fdt_header *fw_oftree;
+	struct device_node *fw_root, *fw_firmware, *fw_resmem, *dst_resmem;
+	struct device_node *dst;
+	size_t fw_size;
+
+	fw_oftree = efi_fdt_find(&fw_size);
+	if (IS_ERR(fw_oftree))
+		return 0;
+
+	fw_root = of_unflatten_dtb(fw_oftree, fw_size);
+	if (IS_ERR(fw_root))
+		return 0;
+
+	fw_firmware = of_get_child_by_name(fw_root, "firmware");
+	if (fw_firmware) {
+		dst = of_get_child_by_name(root, "firmware");
+		if (dst)
+			of_delete_node(dst);
+
+		of_copy_node(root, fw_firmware);
+	}
+
+	fw_resmem = of_get_child_by_name(fw_root, "reserved-memory");
+	if (fw_resmem) {
+		dst_resmem = of_get_child_by_name(root, "reserved-memory");
+		if (!dst_resmem) {
+			of_copy_node(root, fw_resmem);
+		} else {
+			struct device_node *child;
+
+			for_each_child_of_node(fw_resmem, child) {
+				dst = of_get_child_by_name(dst_resmem, child->name);
+				if (dst)
+					of_delete_node(dst);
+
+				of_copy_node(dst_resmem, child);
+			}
+		}
+	}
+
+	of_delete_node(fw_root);
+
+	return 0;
+}
+
+static int efi_register_firmware_nodes_fixup(void)
+{
+	return of_register_fixup(efi_of_fixup_firmware_nodes, NULL);
+}
+core_efi_initcall(efi_register_firmware_nodes_fixup);
+
 /* Features of the loader, i.e. systemd-boot, barebox (imported from systemd) */
 #define EFI_LOADER_FEATURE_CONFIG_TIMEOUT          (1LL << 0)
 #define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (1LL << 1)
diff --git a/include/efi/payload.h b/include/efi/payload.h
index 381598ba59f2..8dc09dd5acdc 100644
--- a/include/efi/payload.h
+++ b/include/efi/payload.h
@@ -34,4 +34,14 @@ __attribute__((noreturn)) void efi_main(efi_handle_t, struct efi_system_table *)
 	     t - efi_sys_table->tables < efi_sys_table->nr_tables; \
 	     t++)
 
+#if IS_ENABLED(CONFIG_OFTREE)
+void *efi_fdt_find(size_t *size);
+#else
+static inline void *efi_fdt_find(size_t *size)
+{
+	*size = 0;
+	return NULL;
+}
+#endif
+
 #endif



      reply	other threads:[~2026-08-24 23:45 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-24 23:43 [PATCH v2 0/1] " chalianis1
2026-08-24 23:43 ` chalianis1 [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260824234345.84068-2-chalianis1@gmail.com \
    --to=chalianis1@gmail.com \
    --cc=barebox@lists.infradead.org \
    --cc=noreply@anthropic.com \
    --cc=s.hauer@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox