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 Signed-off-by: Chali Anis <[email protected]> --- efi/payload/bootm.c | 58 +++++++++++++++++++++++++++++++++++-------- efi/payload/fdt.c | 34 +++++++++++++++++-------- include/efi/payload.h | 10 ++++++++ 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/efi/payload/bootm.c b/efi/payload/bootm.c index 2f9cc3cbf76b..094b2e9c6466 100644 --- a/efi/payload/bootm.c +++ b/efi/payload/bootm.c @@ -111,27 +111,59 @@ 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; + + size = DIV_ROUND_UP(fdt_totalsize(oftree), EFI_PAGE_SIZE); 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 +172,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 +188,9 @@ static void efi_unload_fdt(void *fdt) if (!fdt) return; + size_t size = DIV_ROUND_UP(fdt_totalsize(fdt), EFI_PAGE_SIZE); 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/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
