When a firmware-owned devicetree source is configured (EBBR / Arm SystemReady IR), assemble the devicetree from the FIT manifest on the firmware partition instead of reading a DTB from the EFI System Partition, and hand it to the EFI application through the normal bflow->fdt_addr path (efi_install_fdt()).
Signed-off-by: Carlo Caione <[email protected]> --- boot/bootmeth_efi.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c index e187dc39912..ae3f1c9b91e 100644 --- a/boot/bootmeth_efi.c +++ b/boot/bootmeth_efi.c @@ -17,6 +17,7 @@ #include <efi_loader.h> #include <env.h> #include <extension_board.h> +#include <firmware_fdt.h> #include <fs.h> #include <malloc.h> #include <mapmem.h> @@ -73,6 +74,61 @@ static int efiload_read_file(struct bootflow *bflow, ulong addr) return 0; } +/** + * distro_efi_firmware_fdt() - try to stage the firmware-owned devicetree + * + * Prefer a firmware-owned devicetree if one is configured: assemble it + * from the FIT manifest on the firmware partition, stage it at 'fdt_addr_r' + * and fill the bootflow, so it reaches the EFI app through the normal + * bflow->fdt_addr path (efi_install_fdt()). + * + * The semantics are fail closed: only -ENOENT (no firmware-FDT source + * configured) lets the caller fall back to its normal devicetree source, so + * a missing or broken firmware devicetree is never silently replaced. + * + * @bflow: bootflow to update + * Return: 0 if the firmware devicetree was staged (the bootflow devicetree + * fields are set), -ENOENT if no source is configured, other -ve error if a + * configured source failed to assemble (the bootflow must fail) + */ +static int distro_efi_firmware_fdt(struct bootflow *bflow) +{ + struct firmware_fdt fw; + ulong fdt_addr; + int ret; + + if (!CONFIG_IS_ENABLED(BOOTSTD_FIRMWARE_FDT)) + return -ENOENT; + + ret = firmware_fdt_load(&fw); + if (ret) { + if (ret != -ENOENT) + log_err("Failed to assemble the firmware devicetree (err %d)\n", + ret); + return ret; + } + + /* stage it at fdt_addr_r, like any other devicetree */ + fdt_addr = env_get_hex("fdt_addr_r", 0); + if (!fdt_addr || fw.size > SZ_4M) { + firmware_fdt_free(&fw); + return log_msg_ret("fwa", -EINVAL); + } + + memcpy(map_sysmem(fdt_addr, fw.size), fw.fdt, fw.size); + + bflow->fdt_fname = strdup(fw.name); + bflow->fdt_size = fw.size; + bflow->fdt_addr = fdt_addr; + firmware_fdt_free(&fw); + if (!bflow->fdt_fname) + return log_msg_ret("fwn", -ENOMEM); + + log_debug("Using firmware-owned devicetree\n"); + + return 0; +} + static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter) { /* This only works on block and network devices */ @@ -130,6 +186,18 @@ static int distro_efi_try_bootflow_files(struct udevice *dev, fdt_addr = env_get_hex("fdt_addr_r", 0); + /* + * A staged firmware-owned devicetree is complete and authoritative, + * so return without considering any other devicetree source. The + * extension overlays below are deliberately not applied on top: + * such combinations belong in the manifest as configurations. + */ + ret = distro_efi_firmware_fdt(bflow); + if (!ret) + return 0; + if (ret != -ENOENT) + return log_msg_ret("fwf", ret); + /* try the various available names */ ret = -ENOENT; *fname = '\0'; @@ -268,6 +336,19 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow) if (!bflow->fname) return log_msg_ret("fi0", -ENOMEM); + /* + * A configured firmware-owned devicetree outranks the network-provided + * one (and the prior-stage / built-in devicetree below): a DHCP/TFTP + * server must not be able to replace it. + */ + ret = distro_efi_firmware_fdt(bflow); + if (!ret) { + bflow->state = BOOTFLOWST_READY; + return 0; + } + if (ret != -ENOENT) + return log_msg_ret("fwf", ret); + /* read the DT file also */ ret = efi_get_distro_fdt_name(fname, sizeof(fname), 0); if (ret == -EALREADY) { -- 2.55.0

