image: fdt: copy possible optee nodes to a loaded devicetree
authorHeiko Stuebner <heiko.stuebner@theobroma-systems.com>
Wed, 23 Oct 2019 14:46:40 +0000 (16:46 +0200)
committerSimon Glass <sjg@chromium.org>
Thu, 14 Nov 2019 13:09:34 +0000 (07:09 -0600)
The loading convention for optee or any other tee on arm64 is as bl32
parameter to the trusted-firmware. So TF-A gets invoked with the TEE as
bl32 and main u-boot as bl33. Once it has done its startup TF-A jumps
into the bl32 for the TEE startup, returns to TF-A and then jumps to bl33.

All of them get passed a devicetree as parameter and all components often
get loaded from a FIT image.

OP-TEE will create additional nodes in that devicetree namely a firmware
node and possibly multiple reserved-memory nodes.

While this devicetree is used in main u-boot, in most cases it won't be
the one passed to the actual kernel. Instead most boot commands will load
a new devicetree from somewhere like mass storage of the network, so if
that happens u-boot should transfer the optee nodes to that new devicetree.

To make that happen introduce optee_copy_fdt_nodes() called from the dt
setup function in image-fdt which after checking for the optee presence
in the u-boot dt will make sure a optee node is present in the kernel dt
and transfer any reserved-memory regions it can find.

Signed-off-by: Heiko Stuebner <heiko.stuebner@theobroma-systems.com>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
common/image-fdt.c
include/tee/optee.h
lib/optee/optee.c

index 4247dcee0c4f71d6626ffa31337c9313ff45b335..48388488d98defeeabd7641000d68f517aaf6a1f 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/libfdt.h>
 #include <mapmem.h>
 #include <asm/io.h>
+#include <tee/optee.h>
 
 #ifndef CONFIG_SYS_FDT_PAD
 #define CONFIG_SYS_FDT_PAD 0x3000
@@ -561,6 +562,13 @@ int image_setup_libfdt(bootm_headers_t *images, void *blob,
                }
        }
 
+       fdt_ret = optee_copy_fdt_nodes(gd->fdt_blob, blob);
+       if (fdt_ret) {
+               printf("ERROR: transfer of optee nodes to new fdt failed: %s\n",
+                      fdt_strerror(fdt_ret));
+               goto err;
+       }
+
        /* Delete the old LMB reservation */
        if (lmb)
                lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob,
index 9446928fd4896de4e728a3940c586e47315cad8f..121b30a3030e89750c410b3780a04040aebb565f 100644 (file)
@@ -67,4 +67,13 @@ static inline int optee_verify_bootm_image(unsigned long image_addr,
 }
 #endif
 
+#if defined(CONFIG_OPTEE) && defined(CONFIG_OF_LIBFDT)
+int optee_copy_fdt_nodes(const void *old_blob, void *new_blob);
+#else
+static inline int optee_copy_fdt_nodes(const void *old_blob, void *new_blob)
+{
+       return 0;
+}
+#endif
+
 #endif /* _OPTEE_H */
index db92cd9af293ccf94b7c1aa0d316a0eb6f4501c9..c883c498e1d4ce80f0d59d219caf9af503ee93a9 100644 (file)
@@ -5,6 +5,8 @@
  */
 
 #include <common.h>
+#include <malloc.h>
+#include <linux/libfdt.h>
 #include <tee/optee.h>
 
 #define optee_hdr_err_msg \
@@ -63,3 +65,141 @@ error:
 
        return ret;
 }
+
+#if defined(CONFIG_OF_LIBFDT)
+static int optee_copy_firmware_node(const void *old_blob, void *fdt_blob)
+{
+       int old_offs, offs, ret, len;
+       const void *prop;
+
+       old_offs = fdt_path_offset(old_blob, "/firmware/optee");
+       if (old_offs < 0) {
+               debug("Original OP-TEE Device Tree node not found");
+               return old_offs;
+       }
+
+       offs = fdt_path_offset(fdt_blob, "/firmware");
+       if (offs < 0) {
+               offs = fdt_path_offset(fdt_blob, "/");
+               if (offs < 0)
+                       return offs;
+
+               offs = fdt_add_subnode(fdt_blob, offs, "firmware");
+               if (offs < 0)
+                       return offs;
+       }
+
+       offs = fdt_add_subnode(fdt_blob, offs, "optee");
+       if (offs < 0)
+               return ret;
+
+       /* copy the compatible property */
+       prop = fdt_getprop(old_blob, old_offs, "compatible", &len);
+       if (!prop) {
+               debug("missing OP-TEE compatible property");
+               return -EINVAL;
+       }
+
+       ret = fdt_setprop(fdt_blob, offs, "compatible", prop, len);
+       if (ret < 0)
+               return ret;
+
+       /* copy the method property */
+       prop = fdt_getprop(old_blob, old_offs, "method", &len);
+       if (!prop) {
+               debug("missing OP-TEE method property");
+               return -EINVAL;
+       }
+
+       ret = fdt_setprop(fdt_blob, offs, "method", prop, len);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+int optee_copy_fdt_nodes(const void *old_blob, void *new_blob)
+{
+       int nodeoffset, subnode, ret;
+       struct fdt_resource res;
+
+       if (fdt_check_header(old_blob))
+               return -EINVAL;
+
+       if (fdt_check_header(new_blob))
+               return -EINVAL;
+
+       /* only proceed if there is an /firmware/optee node */
+       if (fdt_path_offset(old_blob, "/firmware/optee") < 0) {
+               debug("No OP-TEE firmware node in old fdt, nothing to do");
+               return 0;
+       }
+
+       /*
+        * Do not proceed if the target dt already has an OP-TEE node.
+        * In this case assume that the system knows better somehow,
+        * so do not interfere.
+        */
+       if (fdt_path_offset(new_blob, "/firmware/optee") >= 0) {
+               debug("OP-TEE Device Tree node already exists in target");
+               return 0;
+       }
+
+       ret = optee_copy_firmware_node(old_blob, new_blob);
+       if (ret < 0) {
+               printf("Failed to add OP-TEE firmware node\n");
+               return ret;
+       }
+
+       /* optee inserts its memory regions as reserved-memory nodes */
+       nodeoffset = fdt_subnode_offset(old_blob, 0, "reserved-memory");
+       if (nodeoffset >= 0) {
+               subnode = fdt_first_subnode(old_blob, nodeoffset);
+               while (subnode >= 0) {
+                       const char *name = fdt_get_name(old_blob,
+                                                       subnode, NULL);
+                       if (!name)
+                               return -EINVAL;
+
+                       /* only handle optee reservations */
+                       if (strncmp(name, "optee", 5))
+                               continue;
+
+                       /* check if this subnode has a reg property */
+                       ret = fdt_get_resource(old_blob, subnode, "reg", 0,
+                                              &res);
+                       if (!ret) {
+                               struct fdt_memory carveout = {
+                                       .start = res.start,
+                                       .end = res.end,
+                               };
+                               char *oldname, *nodename, *tmp;
+
+                               oldname = strdup(name);
+                               if (!oldname)
+                                       return -ENOMEM;
+
+                               tmp = oldname;
+                               nodename = strsep(&tmp, "@");
+                               if (!nodename) {
+                                       free(oldname);
+                                       return -EINVAL;
+                               }
+
+                               ret = fdtdec_add_reserved_memory(new_blob,
+                                                                nodename,
+                                                                &carveout,
+                                                                NULL);
+                               free(oldname);
+
+                               if (ret < 0)
+                                       return ret;
+                       }
+
+                       subnode = fdt_next_subnode(old_blob, subnode);
+               }
+       }
+
+       return 0;
+}
+#endif