[U-Boot] [PATCH 3/3] image: fdt: copy possible optee nodes to a loaded devicetree

Jens Wiklander
2019-10-22T06:16:40Z

Hi Heiko,

[+Igor]

On Tue, Oct 8, 2019 at 2:22 AM Heiko Stuebner <heiko@sntech.de> wrote:
>
> 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@sntech.de>
> ---
> This goes together with my bl32 work for the spl_atf loader in
> https://patchwork.ozlabs.org/patch/1172565/
>
> common/image-fdt.c | 8 ++++
> include/tee/optee.h | 9 ++++
> lib/optee/optee.c | 112 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 129 insertions(+)
>
> diff --git a/common/image-fdt.c b/common/image-fdt.c
> index 4247dcee0c..48388488d9 100644
> --- a/common/image-fdt.c
> +++ b/common/image-fdt.c
> @@ -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,
> diff --git a/include/tee/optee.h b/include/tee/optee.h
> index 9446928fd4..121b30a303 100644
> --- a/include/tee/optee.h
> +++ b/include/tee/optee.h
> @@ -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 */
> diff --git a/lib/optee/optee.c b/lib/optee/optee.c
> index db92cd9af2..f484b12e67 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,113 @@ error:
>
> return ret;
> }
> +
> +#if defined(CONFIG_OF_LIBFDT)
> +static int optee_add_firmware_node(void *fdt_blob)
> +{
> + int offs, ret;
> +
> + if (fdt_path_offset(fdt_blob, "/firmware/optee") >= 0) {
> + debug("OP-TEE Device Tree node already exists");
> + return 0;
> + }
> +
> + 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;
> +
> + ret = fdt_setprop_string(fdt_blob, offs, "compatible",
> + "linaro,optee-tz");
> + if (ret < 0)
> + return ret;
> +
> + ret = fdt_setprop_string(fdt_blob, offs, "method", "smc");
> + 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;
> +
> + /* 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;
> + }
> +
> + ret = optee_add_firmware_node(new_blob);

I think it's more safe to copy the OP-TEE node instead. The values of
"compatible" and "method" may be different on other platforms, "hvc"
instead of "smc" for instance.

Thanks,
Jens

> + 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
> --
> 2.23.0
>