Skip to contentSkip to conversation

efi: payload: apply barebox fixups to the devicetree passed to Linux

PATCH New [email protected] 1 participant 2 messages Aug 24

lore
Share & tools
lore
#0 lore raw

v2 cover efi: payload: apply barebox fixups to the devicetree passed to Linux

From: Chali Anis <[email protected]>

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; 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.

This single patch fixes both paths: efi_load_fdt() now falls back to the FDT firmware exposes via its EFI configuration table when no bootm.oftree is set, and either way runs the resulting tree through the standard bootm_set_pending_oftree_overlays()/of_fix_tree() sequence before installing it, matching what every other barebox boot path already guarantees. This is what makes fixups registered via of_register_fixup() actually reach the devicetree the EFI payload hands off to Linux.

On real hardware (as opposed to QEMU, where none of this is normally present or needed) this matters concretely for two node types: the /memory node, since of_memory_fixup() (common/memory.c) is one of the fixups of_fix_tree() runs, and without this patch it never got a chance to correct the tree Linux actually booted with; and the /firmware and /reserved-memory nodes, since a devicetree supplied out-of-band (e.g. embedded in a FIT image) was flattened at build time and so can never carry the secure-world carve-outs and secure monitor call conduit firmware such as TF-A/OP-TEE only add to its own copy of the tree at runtime. This patch adds efi_of_fixup_firmware_nodes() to import exactly those two nodes from firmware's own devicetree onto the one Linux boots with, registered as another of_register_fixup() callback so it runs as part of the same pipeline.

Changes since v1: none, resent as v2.

Chali Anis (1):
efi: payload: apply barebox fixups to the devicetree passed to Linux

 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(-)
#1 lore raw

PATCH v2 1/1 efi: payload: apply barebox fixups to the devicetree passed to Linux

From: Chali Anis <[email protected]>

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 <[email protected]>
---
 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(-)
efi/payload/bootm.c +49 -11
index 2f9cc3cbf76b..b9e1d9438e1c 100644
@@ -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)
efi/payload/fdt.c +24 -10
index 9cdb32370f22..80e6a1fcec21 100644
@@ -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;
efi/payload/init.c +69 -0

75 lines not on this page · raw message

include/efi/payload.h +10 -0
index 381598ba59f2..8dc09dd5acdc 100644
@@ -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