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

Reply via email to