When a firmware-owned devicetree source is configured, stage it at the
same fdt_addr_r value used by the normal EFI bootmeth fallback and pass it
through bflow->fdt_addr to efi_install_fdt().

Use the shared helper in both disk and network paths. Only -ENOENT, meaning
that no source is configured, permits the existing ESP, network or
prior-stage fallback. Any failure after a source is configured ends that
bootflow.

The firmware-owned devicetree is complete and authoritative, so do not
apply extension-board overlays on top of it; those combinations belong in
the FIT.

Signed-off-by: Carlo Caione <[email protected]>
---
 boot/bootmeth_efi.c | 45 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 39 insertions(+), 6 deletions(-)

diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
index e187dc39912..469a07e9540 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>
@@ -100,7 +101,7 @@ static int distro_efi_check(struct udevice *dev, struct 
bootflow_iter *iter)
 static int distro_efi_try_bootflow_files(struct udevice *dev,
                                         struct bootflow *bflow)
 {
-       ulong fdt_addr, size, overlay_addr;
+       ulong fdt_addr, fw_fdt_size, size, overlay_addr;
        const struct extension *extension;
        struct fdt_header *working_fdt;
        struct blk_desc *desc = NULL;
@@ -130,6 +131,22 @@ 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 FIT as configurations.
+        */
+       ret = efi_stage_firmware_fdt(fdt_addr, &fw_fdt_size,
+                                    &bflow->fdt_fname);
+       if (!ret) {
+               bflow->fdt_size = fw_fdt_size;
+               bflow->fdt_addr = fdt_addr;
+               return 0;
+       }
+       if (ret != -ENOENT)
+               return log_msg_ret("fwf", ret);
+
        /* try the various available names */
        ret = -ENOENT;
        *fname = '\0';
@@ -222,9 +239,9 @@ static int distro_efi_read_bootflow_net(struct bootflow 
*bflow)
        char file_addr[17], fname[256];
        char *tftp_argv[] = {"tftp", file_addr, fname, NULL};
        struct cmd_tbl cmdtp = {};      /* dummy */
-       const char *addr_str, *fdt_addr_str, *bootfile_name;
+       const char *addr_str, *bootfile_name;
        int ret, arch, size;
-       ulong addr, fdt_addr;
+       ulong addr, fdt_addr, fw_fdt_size;
        char str[36];
 
        ret = get_efi_pxe_vci(str, sizeof(str));
@@ -268,6 +285,24 @@ static int distro_efi_read_bootflow_net(struct bootflow 
*bflow)
        if (!bflow->fname)
                return log_msg_ret("fi0", -ENOMEM);
 
+       /*
+        * Read fdt_addr_r once so the firmware-FDT source and network fallback
+        * below stage at the same address. A configured firmware-owned
+        * devicetree outranks the network-provided one (and the prior-stage /
+        * built-in devicetree below), so a DHCP/TFTP server cannot replace it.
+        */
+       fdt_addr = env_get_hex("fdt_addr_r", 0);
+       ret = efi_stage_firmware_fdt(fdt_addr, &fw_fdt_size,
+                                    &bflow->fdt_fname);
+       if (!ret) {
+               bflow->fdt_size = fw_fdt_size;
+               bflow->fdt_addr = fdt_addr;
+               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) {
@@ -279,10 +314,8 @@ static int distro_efi_read_bootflow_net(struct bootflow 
*bflow)
                return log_msg_ret("nam", ret);
        }
 
-       fdt_addr_str = env_get("fdt_addr_r");
-       if (!fdt_addr_str)
+       if (!fdt_addr)
                return log_msg_ret("fdt", -EINVAL);
-       fdt_addr = hextoul(fdt_addr_str, NULL);
        sprintf(file_addr, "%lx", fdt_addr);
 
        bflow->fdt_fname = strdup(fname);

-- 
2.55.0

Reply via email to