diff options
author | Jens Wiklander <jens.wiklander@linaro.org> | 2018-09-25 16:40:09 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-10-07 10:47:38 -0400 |
commit | 9ff4a31175deb892cf5ea2976c213fb6c6dda080 (patch) | |
tree | 62a85a3de807d32a26da66c59b85b9e55f134234 /drivers/tee | |
parent | 4853ad3e13e21462a86e09caee4ea27ae68e764b (diff) | |
download | u-boot-9ff4a31175deb892cf5ea2976c213fb6c6dda080.tar.gz |
Add UCLASS_TEE for Trusted Execution Environment
Adds a uclass to interface with a TEE (Trusted Execution Environment).
A TEE driver is a driver that interfaces with a trusted OS running in
some secure environment, for example, TrustZone on ARM cpus, or a
separate secure co-processor etc.
The TEE subsystem can serve a TEE driver for a Global Platform compliant
TEE, but it's not limited to only Global Platform TEEs.
The over all design is based on the TEE subsystem in the Linux kernel,
tailored for U-Boot.
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Igor Opaniuk <igor.opaniuk@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Diffstat (limited to 'drivers/tee')
-rw-r--r-- | drivers/tee/Kconfig | 11 | ||||
-rw-r--r-- | drivers/tee/Makefile | 3 | ||||
-rw-r--r-- | drivers/tee/tee-uclass.c | 209 |
3 files changed, 223 insertions, 0 deletions
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig new file mode 100644 index 0000000000..f3fc3c2ca7 --- /dev/null +++ b/drivers/tee/Kconfig @@ -0,0 +1,11 @@ +# Generic Trusted Execution Environment Configuration +config TEE + bool "Trusted Execution Environment support" + depends on ARM && (ARM64 || CPU_V7A) + select ARM_SMCCC + help + This implements a generic interface towards a Trusted Execution + Environment (TEE). A TEE is a trusted OS running in some secure + environment, for example, TrustZone on ARM cpus, or a separate + secure co-processor etc. See also: + https://en.wikipedia.org/wiki/Trusted_execution_environment diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile new file mode 100644 index 0000000000..b6d8e16e62 --- /dev/null +++ b/drivers/tee/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += tee-uclass.o diff --git a/drivers/tee/tee-uclass.c b/drivers/tee/tee-uclass.c new file mode 100644 index 0000000000..1bee54ebf4 --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Linaro Limited + */ + +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <tee.h> + +/** + * struct tee_uclass_priv - information of a TEE, stored by the uclass + * + * @list_shm: list of structe tee_shm representing memory blocks shared + * with the TEE. + */ +struct tee_uclass_priv { + struct list_head list_shm; +}; + +static const struct tee_driver_ops *tee_get_ops(struct udevice *dev) +{ + return device_get_ops(dev); +} + +void tee_get_version(struct udevice *dev, struct tee_version_data *vers) +{ + tee_get_ops(dev)->get_version(dev, vers); +} + +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg, + uint num_param, struct tee_param *param) +{ + return tee_get_ops(dev)->open_session(dev, arg, num_param, param); +} + +int tee_close_session(struct udevice *dev, u32 session) +{ + return tee_get_ops(dev)->close_session(dev, session); +} + +int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg, + uint num_param, struct tee_param *param) +{ + return tee_get_ops(dev)->invoke_func(dev, arg, num_param, param); +} + +int __tee_shm_add(struct udevice *dev, ulong align, void *addr, ulong size, + u32 flags, struct tee_shm **shmp) +{ + struct tee_shm *shm; + void *p = addr; + int rc; + + if (flags & TEE_SHM_ALLOC) { + if (align) + p = memalign(align, size); + else + p = malloc(size); + } + if (!p) + return -ENOMEM; + + shm = calloc(1, sizeof(*shm)); + if (!shm) { + rc = -ENOMEM; + goto err; + } + + shm->dev = dev; + shm->addr = p; + shm->size = size; + shm->flags = flags; + + if (flags & TEE_SHM_SEC_REGISTER) { + rc = tee_get_ops(dev)->shm_register(dev, shm); + if (rc) + goto err; + } + + if (flags & TEE_SHM_REGISTER) { + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + + list_add(&shm->link, &priv->list_shm); + } + + *shmp = shm; + + return 0; +err: + free(shm); + if (flags & TEE_SHM_ALLOC) + free(p); + + return rc; +} + +int tee_shm_alloc(struct udevice *dev, ulong size, u32 flags, + struct tee_shm **shmp) +{ + u32 f = flags; + + f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER | TEE_SHM_ALLOC; + + return __tee_shm_add(dev, 0, NULL, size, f, shmp); +} + +int tee_shm_register(struct udevice *dev, void *addr, ulong size, u32 flags, + struct tee_shm **shmp) +{ + u32 f = flags & ~TEE_SHM_ALLOC; + + f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER; + + return __tee_shm_add(dev, 0, addr, size, f, shmp); +} + +void tee_shm_free(struct tee_shm *shm) +{ + if (!shm) + return; + + if (shm->flags & TEE_SHM_SEC_REGISTER) + tee_get_ops(shm->dev)->shm_unregister(shm->dev, shm); + + if (shm->flags & TEE_SHM_REGISTER) + list_del(&shm->link); + + if (shm->flags & TEE_SHM_ALLOC) + free(shm->addr); + + free(shm); +} + +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev) +{ + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + struct tee_shm *s; + + list_for_each_entry(s, &priv->list_shm, link) + if (s == shm) + return true; + + return false; +} + +struct udevice *tee_find_device(struct udevice *start, + int (*match)(struct tee_version_data *vers, + const void *data), + const void *data, + struct tee_version_data *vers) +{ + struct udevice *dev = start; + struct tee_version_data lv; + struct tee_version_data *v = vers ? vers : &lv; + + if (!dev) + uclass_find_first_device(UCLASS_TEE, &dev); + else + uclass_find_next_device(&dev); + + for (; dev; uclass_find_next_device(&dev)) { + if (device_probe(dev)) + continue; + tee_get_ops(dev)->get_version(dev, v); + if (!match || match(v, data)) + return dev; + } + + return NULL; +} + +static int tee_pre_probe(struct udevice *dev) +{ + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + + INIT_LIST_HEAD(&priv->list_shm); + + return 0; +} + +static int tee_pre_remove(struct udevice *dev) +{ + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + struct tee_shm *shm; + + /* + * Any remaining shared memory must be unregistered now as U-Boot + * is about to hand over to the next stage and that memory will be + * reused. + */ + while (!list_empty(&priv->list_shm)) { + shm = list_first_entry(&priv->list_shm, struct tee_shm, link); + debug("%s: freeing leftover shm %p (size %lu, flags %#x)\n", + __func__, (void *)shm, shm->size, shm->flags); + tee_shm_free(shm); + } + + return 0; +} + +UCLASS_DRIVER(tee) = { + .id = UCLASS_TEE, + .name = "tee", + .per_device_auto_alloc_size = sizeof(struct tee_uclass_priv), + .pre_probe = tee_pre_probe, + .pre_remove = tee_pre_remove, +}; |