aboutsummaryrefslogtreecommitdiff
path: root/lib/optee
diff options
context:
space:
mode:
authorHeiko Stuebner2019-10-23 16:46:40 +0200
committerSimon Glass2019-11-14 07:09:34 -0600
commit6ccb05eae01b660b0585accf338302af1069f419 (patch)
tree19f20b96462bff2528bef0e675bdc8080242ab8f /lib/optee
parent357d2ceba0354e29462ac25924f5e42623c22b5b (diff)
image: fdt: copy possible optee nodes to a loaded devicetree
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>
Diffstat (limited to 'lib/optee')
-rw-r--r--lib/optee/optee.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/lib/optee/optee.c b/lib/optee/optee.c
index db92cd9af29..c883c498e1d 100644
--- a/lib/optee/optee.c
+++ b/lib/optee/optee.c
@@ -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