Message ID | 1431671667-11219-3-git-send-email-jens.wiklander@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi, On Fri, May 15, 2015 at 07:34:27AM +0100, Jens Wiklander wrote: > Adds a OP-TEE driver which also can be compiled as a loadable module. > > * Targets ARM and ARM64 > * Supports using reserved memory from OP-TEE as shared memory > * CMA as shared memory is optional and only tried if OP-TEE doesn't > supply a reserved shared memory region How does OP-TEE provide that reserved memory? How is that described in DT/UEFI to the OS (e.g. is there a memreserve, or is the memory not described at all)? > * Probes OP-TEE version using SMCs > * Accepts requests on privileged and unprivileged device > * Uses OPTEE message protocol version 2 > > Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org> > --- > Documentation/devicetree/bindings/optee/optee.txt | 17 + I'm concerned that there's no documentation regarding the interface exposed to userspace, for neither rationale nor usage. I'm also very concerned that the interface exposed to userspace is hideously low-level. Surely we'd expect kernel-side drivers to be doing the bulk of direct communication to the OP-TEE instance? In the lack of a provided rationale I don't see why the current messaging interface would make sense. > .../devicetree/bindings/vendor-prefixes.txt | 1 + > MAINTAINERS | 6 + > drivers/tee/Kconfig | 10 + > drivers/tee/Makefile | 1 + > drivers/tee/optee/Kconfig | 19 + > drivers/tee/optee/Makefile | 13 + > drivers/tee/optee/call.c | 294 ++++++++++++ > drivers/tee/optee/core.c | 509 ++++++++++++++++++++ > drivers/tee/optee/optee_private.h | 138 ++++++ > drivers/tee/optee/optee_smc.h | 510 +++++++++++++++++++++ > drivers/tee/optee/rpc.c | 282 ++++++++++++ > drivers/tee/optee/smc_a32.S | 30 ++ > drivers/tee/optee/smc_a64.S | 37 ++ > drivers/tee/optee/supp.c | 327 +++++++++++++ > include/uapi/linux/optee_msg.h | 368 +++++++++++++++ > 16 files changed, 2562 insertions(+) > create mode 100644 Documentation/devicetree/bindings/optee/optee.txt > create mode 100644 drivers/tee/optee/Kconfig > create mode 100644 drivers/tee/optee/Makefile > create mode 100644 drivers/tee/optee/call.c > create mode 100644 drivers/tee/optee/core.c > create mode 100644 drivers/tee/optee/optee_private.h > create mode 100644 drivers/tee/optee/optee_smc.h > create mode 100644 drivers/tee/optee/rpc.c > create mode 100644 drivers/tee/optee/smc_a32.S > create mode 100644 drivers/tee/optee/smc_a64.S > create mode 100644 drivers/tee/optee/supp.c > create mode 100644 include/uapi/linux/optee_msg.h > > diff --git a/Documentation/devicetree/bindings/optee/optee.txt b/Documentation/devicetree/bindings/optee/optee.txt > new file mode 100644 > index 0000000..8cea829 > --- /dev/null > +++ b/Documentation/devicetree/bindings/optee/optee.txt > @@ -0,0 +1,17 @@ > +OP-TEE Device Tree Bindings > + > +OP-TEE is a piece of software using hardware features to provide a Trusted > +Execution Environment. The security can be provided with ARM TrustZone, but > +also by virtualization or a separate chip. As there's no single OP-TEE > +vendor we're using "optee" as the first part of compatible propterty, s/propterty/property/ > +indicating the OP-TEE protocol is used when communicating with the secure > +world. > + > +* OP-TEE based on ARM TrustZone required properties: > + > +- compatible="optee,optee-tz" > + > +Example: > + optee { > + compatible="optee,optee-tz"; > + }; What does the OP-TEE protocol give in the way of discoverability? Is it expected that the specific implementation and/or features will be detected dynamically? Where can I find documentation on the protocol? > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt > index 8033919..17c2a7e 100644 > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt > @@ -141,6 +141,7 @@ nvidia NVIDIA > nxp NXP Semiconductors > onnn ON Semiconductor Corp. > opencores OpenCores.org > +optee OP-TEE, Open Portable Trusted Execution Environment > ortustech Ortus Technology Co., Ltd. > ovti OmniVision Technologies > panasonic Panasonic Corporation > diff --git a/MAINTAINERS b/MAINTAINERS > index dfcc9cc..1234695 100644 Please split the DT binding parts into a separate patch, at the start of the series. > diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile > new file mode 100644 > index 0000000..096651d > --- /dev/null > +++ b/drivers/tee/optee/Makefile > @@ -0,0 +1,13 @@ > +obj-$(CONFIG_OPTEE) += optee.o > +optee-objs += core.o > +optee-objs += call.o > +ifdef CONFIG_ARM > +plus_sec := $(call as-instr,.arch_extension sec,+sec) > +AFLAGS_smc_a32.o := -Wa,-march=armv7-a$(plus_sec) > +optee-objs += smc_a32.o > +endif > +ifdef CONFIG_ARM64 > +optee-objs += smc_a64.o > +endif The assembly objects should probably live under the relevant arch/ folders, and can probably be shared with clients for other services compliant with the SMC Calling Conventions. > +static void optee_call_lock(struct optee_call_sync *callsync) > +{ > + mutex_lock(&callsync->mutex); > +} > + > +static void optee_call_lock_wait_completion(struct optee_call_sync *callsync) > +{ > + /* > + * Release the lock until "something happens" and then reacquire it > + * again. When you say you're waiting until "something happens", you really are waiting until something happens. The quotes aren't helpful, please drop them. > + * > + * This is needed when TEE returns "busy" and we need to try again > + * later. > + */ > + callsync->c_waiters++; > + mutex_unlock(&callsync->mutex); > + /* > + * Wait at most one second. Secure world is normally never busy > + * more than that so we should normally never timeout. > + */ > + wait_for_completion_timeout(&callsync->c, HZ); > + mutex_lock(&callsync->mutex); > + callsync->c_waiters--; > +} > + > +static void optee_call_unlock(struct optee_call_sync *callsync) > +{ > + /* > + * If at least one thread is waiting for "something to happen" let > + * one thread know that "something has happened". > + */ > + if (callsync->c_waiters) > + complete(&callsync->c); > + mutex_unlock(&callsync->mutex); > +} > + You can get rid of the c_waiters variable entirely, as complete() is safe to call when the completion has an empty waiters list (and the manipulation of c_waiters is racy anyway...). Also, I think you need complete_all(&callsync->c) if more than two concurrent calls are possible. Otherwise one call might block another indefinitely. > +static int optee_arg_from_user(struct opteem_arg *arg, size_t size, > + struct tee_shm **put_shm) > +{ > + struct opteem_param *param; > + size_t n; > + > + if (!arg->num_params || !put_shm) > + return -EINVAL; > + > + param = OPTEEM_GET_PARAMS(arg); OPTEEM is a little opaque. OPTEE_MSG would be easier to grasp. [...] > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c > new file mode 100644 > index 0000000..b3f8b92d > --- /dev/null > +++ b/drivers/tee/optee/core.c > @@ -0,0 +1,509 @@ > +/* > + * Copyright (c) 2015, Linaro Limited > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > +#include <linux/types.h> > +#include <linux/string.h> > +#include <linux/errno.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/uaccess.h> > +#include <linux/dma-contiguous.h> > +#ifdef CONFIG_OPTEE_USE_CMA > +#include <linux/cma.h> > +#endif Surely this ifdeffery isn't necessary? [...] > +static struct tee_shm_pool *optee_config_shm_ioremap(struct device *dev, > + void __iomem **ioremaped_shm) > +{ > + struct optee_smc_param param = { .a0 = OPTEE_SMC_GET_SHM_CONFIG }; > + struct tee_shm_pool *pool; > + u_long vaddr; Why not plain unsigned long? [...] > +/* > + * This file is exported by OP-TEE and is in kept in sync between secure > + * world and normal world kernel driver. We're following ARM SMC Calling > + * Convention as specified in > + * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html The values defined in the SMC Calling Conventions (which have nothing to do with OP-TEE as such), we should probably prefix with SMCCC_, and have in a shared file. > + * > + * This file depends on optee_msg.h being included to expand the SMC id > + * macros below. > + */ > + > +#define OPTEE_SMC_32 0 > +#define OPTEE_SMC_64 0x40000000 > +#define OPTEE_SMC_FAST_CALL 0x80000000 > +#define OPTEE_SMC_STD_CALL 0 The zero cases look a bit odd here. They might be better-documented if you defined them with shifts, e.g. #define SMCCC_SMC_32 (0 << 30) #define SMCCC_SMC_64 (1 << 30) #define SMCCC_FAST_CALL (1 << 31) #define SMCCC_STD_CALL (0 << 31) [...] > +/* > + * Cache settings for shared memory > + */ > +#define OPTEE_SMC_SHM_NONCACHED 0ULL > +#define OPTEE_SMC_SHM_CACHED 1ULL What precise set of memory attributes do these imply? [...] > +/* > + * Same values as TEE_PARAM_* from TEE Internal API > + */ > +#define OPTEEM_ATTR_TYPE_NONE 0 > +#define OPTEEM_ATTR_TYPE_VALUE_INPUT 1 > +#define OPTEEM_ATTR_TYPE_VALUE_OUTPUT 2 > +#define OPTEEM_ATTR_TYPE_VALUE_INOUT 3 > +#define OPTEEM_ATTR_TYPE_MEMREF_INPUT 5 > +#define OPTEEM_ATTR_TYPE_MEMREF_OUTPUT 6 > +#define OPTEEM_ATTR_TYPE_MEMREF_INOUT 7 Are these well-defined ABI values? As mentioned previously, OPTEEM_* is opaque, and OPTEE_MSG_* would be far clearer. > +/** > + * struct opteem_param_memref - memory reference > + * @buf_ptr: Address of the buffer > + * @size: Size of the buffer > + * @shm_ref: Shared memory reference only used by normal world > + * > + * Secure and normal world communicates pointers as physical address > + * instead of the virtual address. This is because secure and normal world > + * have completely independent memory mapping. Normal world can even have a > + * hypervisor which need to translate the guest physical address (AKA IPA > + * in ARM documentation) to a real physical address before passing the > + * structure to secure world. > + */ > +struct opteem_param_memref { > + __u64 buf_ptr; > + __u64 size; > + __u64 shm_ref; > +}; Why does this mention physical addresses at all? What does that have to do with userspace? What is the shm_ref, and who allocates it? There should really be some documentation for this. > +/** > + * struct opteem_param_value - values > + * @a: first value > + * @b: second value > + * @c: third value > + */ > +struct opteem_param_value { > + __u64 a; > + __u64 b; > + __u64 c; > +}; Are OP-TEE messages defined to only ever take three parameters? [...] > +/** > + * struct optee_cmd_prefix - initial header for all user space buffers > + * @func_id: Function Id OPTEEM_FUNCID_* below > + * @pad: padding to make the struct size a multiple of 8 bytes > + * > + * This struct is 8 byte aligned since it's always followed by a struct > + * opteem_arg which requires 8 byte alignment. > + */ > +struct opteem_cmd_prefix { > + __u32 func_id; > + __u32 pad __aligned(8); > +}; Shouldn't the alignment be applied to the struct as a whole rather than the final field? > +/* > + * Sleep mutex, helper for secure world to implement a sleeping mutex. > + * struct opteem_arg::func one of OPTEEM_RPC_SLEEP_MUTEX_* below > + * > + * OPTEEM_RPC_SLEEP_MUTEX_WAIT > + * [in] param[0].value .a sleep mutex key > + * .b wait tick > + * [not used] param[1] > + * > + * OPTEEM_RPC_SLEEP_MUTEX_WAKEUP > + * [in] param[0].value .a sleep mutex key > + * .b wait after > + * [not used] param[1] > + * > + * OPTEEM_RPC_SLEEP_MUTEX_DELETE > + * [in] param[0].value .a sleep mutex key > + * [not used] param[1] > + */ > +#define OPTEEM_RPC_SLEEP_MUTEX_WAIT 0 > +#define OPTEEM_RPC_SLEEP_MUTEX_WAKEUP 1 > +#define OPTEEM_RPC_SLEEP_MUTEX_DELETE 2 > +#define OPTEEM_RPC_CMD_SLEEP_MUTEX 4 I'm lost. Why are mutexes exposed to userspace or the secure world in such a manner? Thanks, Mark.
Hi, On Mon, May 18, 2015 at 02:18:50PM +0100, Mark Rutland wrote: > Hi, > > On Fri, May 15, 2015 at 07:34:27AM +0100, Jens Wiklander wrote: > > Adds a OP-TEE driver which also can be compiled as a loadable module. > > > > * Targets ARM and ARM64 > > * Supports using reserved memory from OP-TEE as shared memory > > * CMA as shared memory is optional and only tried if OP-TEE doesn't > > supply a reserved shared memory region > > How does OP-TEE provide that reserved memory? How is that described in > DT/UEFI to the OS (e.g. is there a memreserve, or is the memory not > described at all)? It's either memreserve or not described at all. This should only be needed when secure world is limited in which memory it can use for shared memory. Currently all OP-TEE ports uses reserved shared memory, but we're moving away from it to avoid the problem with updating DT. > > > * Probes OP-TEE version using SMCs > > * Accepts requests on privileged and unprivileged device > > * Uses OPTEE message protocol version 2 > > > > Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org> > > --- > > Documentation/devicetree/bindings/optee/optee.txt | 17 + > > I'm concerned that there's no documentation regarding the interface > exposed to userspace, for neither rationale nor usage. OK, I'll add something. > > I'm also very concerned that the interface exposed to userspace is > hideously low-level. Surely we'd expect kernel-side drivers to be doing > the bulk of direct communication to the OP-TEE instance? In the lack of > a provided rationale I don't see why the current messaging interface > would make sense. The kernel-side does all the direct communication since there's where the SMC is done, but one level above most of the communication is terminated in user space. Loading of Trusted Applications and other file system access is done in by a helper process in user space, tee-supplicant. A large part of the OP-TEE message protocol is transparent to the kernel. We're trying to not exclude any TEE implementation by having this low level TEE_IOC_CMD instead of a high level interface. The problem with the different TEEs is that they are designed differently and we haven't been able to find a high level interface that suits all. Applications aren't expected to use TEE_IOC_CMD directly, instead there's supposed to be a client lib wich wraps the kernel interface and provides a higher level interface, for instance a Global Platform Client API in the case of a GP TEE. For OP-TEE we're using the same protocol all the way down to user space, the advantage is that it's only one protocol to keep track of and we don't need to translate the entire message (we do need to copy it, excluding the payload) each time the message is passed to the next memory space. In the presence of a hypervisor we have user space -> kernel -> hypervisor -> secure world Unfortunately some fields has a different meaning in user space and kernel space. I'll address this in the documentation. The OP-TEE helper process, tee-supplicant, is specific to only OP-TEE. Other TEEs uses helper processes too, but what they do depend on the design of the TEE. As a consequence more or less all TEEs needs something specific for that particular TEE in user space to be fully functional. To summarize, the current assumption is that all TEEs can't use the same high level interface. Instead we need to provide a way for each TEE to have their own low level interface which should be wrapped in a user space library to provide a more reasonable interface to the client application. > > > .../devicetree/bindings/vendor-prefixes.txt | 1 + > > MAINTAINERS | 6 + > > drivers/tee/Kconfig | 10 + > > drivers/tee/Makefile | 1 + > > drivers/tee/optee/Kconfig | 19 + > > drivers/tee/optee/Makefile | 13 + > > drivers/tee/optee/call.c | 294 ++++++++++++ > > drivers/tee/optee/core.c | 509 ++++++++++++++++++++ > > drivers/tee/optee/optee_private.h | 138 ++++++ > > drivers/tee/optee/optee_smc.h | 510 +++++++++++++++++++++ > > drivers/tee/optee/rpc.c | 282 ++++++++++++ > > drivers/tee/optee/smc_a32.S | 30 ++ > > drivers/tee/optee/smc_a64.S | 37 ++ > > drivers/tee/optee/supp.c | 327 +++++++++++++ > > include/uapi/linux/optee_msg.h | 368 +++++++++++++++ > > 16 files changed, 2562 insertions(+) > > create mode 100644 Documentation/devicetree/bindings/optee/optee.txt > > create mode 100644 drivers/tee/optee/Kconfig > > create mode 100644 drivers/tee/optee/Makefile > > create mode 100644 drivers/tee/optee/call.c > > create mode 100644 drivers/tee/optee/core.c > > create mode 100644 drivers/tee/optee/optee_private.h > > create mode 100644 drivers/tee/optee/optee_smc.h > > create mode 100644 drivers/tee/optee/rpc.c > > create mode 100644 drivers/tee/optee/smc_a32.S > > create mode 100644 drivers/tee/optee/smc_a64.S > > create mode 100644 drivers/tee/optee/supp.c > > create mode 100644 include/uapi/linux/optee_msg.h > > > > diff --git a/Documentation/devicetree/bindings/optee/optee.txt b/Documentation/devicetree/bindings/optee/optee.txt > > new file mode 100644 > > index 0000000..8cea829 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/optee/optee.txt > > @@ -0,0 +1,17 @@ > > +OP-TEE Device Tree Bindings > > + > > +OP-TEE is a piece of software using hardware features to provide a Trusted > > +Execution Environment. The security can be provided with ARM TrustZone, but > > +also by virtualization or a separate chip. As there's no single OP-TEE > > +vendor we're using "optee" as the first part of compatible propterty, > > s/propterty/property/ > > > +indicating the OP-TEE protocol is used when communicating with the secure > > +world. > > + > > +* OP-TEE based on ARM TrustZone required properties: > > + > > +- compatible="optee,optee-tz" > > + > > +Example: > > + optee { > > + compatible="optee,optee-tz"; > > + }; > > What does the OP-TEE protocol give in the way of discoverability? Is it > expected that the specific implementation and/or features will be > detected dynamically? We have OPTEEM_FUNCID_GET_OS_UUID and OPTEEM_FUNCID_GET_OS_REVISION which the client can use to identify which particular OP-TEE it's talking to. This is not so interesting for the driver, but the client may care when there's more than one TEE using the OP-TEE message protocol in a single system. There's also OPTEEM_FUNCID_CALLS_UID and OPTEEM_FUNCID_CALLS_REVISION (required by SMC Calling Convention), but those are expected to return static values except OPTEEM_REVISION_MINOR which would be increased if some new message type is added in the future. To summarize, OPTEEM_FUNCID_CALLS_* identifies the OP-TEE message protocol and OPTEEM_FUNCID_GET_OS_* identifies the OP-TEE OS implementation. > > Where can I find documentation on the protocol? The documentation is currently include/uapi/linux/optee_msg.h and drivers/tee/optee/optee_smc.h. I'll add something under Documentation. There's more details at http://shorl.com/lubopribokygy , but that's not entirely updated with this driver. > > > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt > > index 8033919..17c2a7e 100644 > > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt > > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt > > @@ -141,6 +141,7 @@ nvidia NVIDIA > > nxp NXP Semiconductors > > onnn ON Semiconductor Corp. > > opencores OpenCores.org > > +optee OP-TEE, Open Portable Trusted Execution Environment > > ortustech Ortus Technology Co., Ltd. > > ovti OmniVision Technologies > > panasonic Panasonic Corporation > > diff --git a/MAINTAINERS b/MAINTAINERS > > index dfcc9cc..1234695 100644 > > Please split the DT binding parts into a separate patch, at the start of > the series. OK > > > diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile > > new file mode 100644 > > index 0000000..096651d > > --- /dev/null > > +++ b/drivers/tee/optee/Makefile > > @@ -0,0 +1,13 @@ > > +obj-$(CONFIG_OPTEE) += optee.o > > +optee-objs += core.o > > +optee-objs += call.o > > +ifdef CONFIG_ARM > > +plus_sec := $(call as-instr,.arch_extension sec,+sec) > > +AFLAGS_smc_a32.o := -Wa,-march=armv7-a$(plus_sec) > > +optee-objs += smc_a32.o > > +endif > > +ifdef CONFIG_ARM64 > > +optee-objs += smc_a64.o > > +endif > > The assembly objects should probably live under the relevant arch/ > folders, and can probably be shared with clients for other services > compliant with the SMC Calling Conventions. OK, sounds good. Where should I put the smccc.h file to be able to share it between arch/arm and arch/arm64, under include/asm-generic? > > > +static void optee_call_lock(struct optee_call_sync *callsync) > > +{ > > + mutex_lock(&callsync->mutex); > > +} > > + > > +static void optee_call_lock_wait_completion(struct optee_call_sync *callsync) > > +{ > > + /* > > + * Release the lock until "something happens" and then reacquire it > > + * again. > > When you say you're waiting until "something happens", you really are > waiting until something happens. The quotes aren't helpful, please drop > them. OK > > > + * > > + * This is needed when TEE returns "busy" and we need to try again > > + * later. > > + */ > > + callsync->c_waiters++; > > + mutex_unlock(&callsync->mutex); > > + /* > > + * Wait at most one second. Secure world is normally never busy > > + * more than that so we should normally never timeout. > > + */ > > + wait_for_completion_timeout(&callsync->c, HZ); > > + mutex_lock(&callsync->mutex); > > + callsync->c_waiters--; > > +} > > + > > +static void optee_call_unlock(struct optee_call_sync *callsync) > > +{ > > + /* > > + * If at least one thread is waiting for "something to happen" let > > + * one thread know that "something has happened". > > + */ > > + if (callsync->c_waiters) > > + complete(&callsync->c); > > + mutex_unlock(&callsync->mutex); > > +} > > + > > You can get rid of the c_waiters variable entirely, as complete() is > safe to call when the completion has an empty waiters list (and the > manipulation of c_waiters is racy anyway...). OK > > Also, I think you need complete_all(&callsync->c) if more than two > concurrent calls are possible. Otherwise one call might block another > indefinitely. Thanks, I'll do that. > > > +static int optee_arg_from_user(struct opteem_arg *arg, size_t size, > > + struct tee_shm **put_shm) > > +{ > > + struct opteem_param *param; > > + size_t n; > > + > > + if (!arg->num_params || !put_shm) > > + return -EINVAL; > > + > > + param = OPTEEM_GET_PARAMS(arg); > > OPTEEM is a little opaque. OPTEE_MSG would be easier to grasp. OK > > [...] > > > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c > > new file mode 100644 > > index 0000000..b3f8b92d > > --- /dev/null > > +++ b/drivers/tee/optee/core.c > > @@ -0,0 +1,509 @@ > > +/* > > + * Copyright (c) 2015, Linaro Limited > > + * > > + * This software is licensed under the terms of the GNU General Public > > + * License version 2, as published by the Free Software Foundation, and > > + * may be copied, distributed, and modified under those terms. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + * > > + */ > > +#include <linux/types.h> > > +#include <linux/string.h> > > +#include <linux/errno.h> > > +#include <linux/module.h> > > +#include <linux/platform_device.h> > > +#include <linux/slab.h> > > +#include <linux/uaccess.h> > > +#include <linux/dma-contiguous.h> > > +#ifdef CONFIG_OPTEE_USE_CMA > > +#include <linux/cma.h> > > +#endif > > Surely this ifdeffery isn't necessary? > > [...] > > > +static struct tee_shm_pool *optee_config_shm_ioremap(struct device *dev, > > + void __iomem **ioremaped_shm) > > +{ > > + struct optee_smc_param param = { .a0 = OPTEE_SMC_GET_SHM_CONFIG }; > > + struct tee_shm_pool *pool; > > + u_long vaddr; > > Why not plain unsigned long? OK, I'll change. > > [...] > > > +/* > > + * This file is exported by OP-TEE and is in kept in sync between secure > > + * world and normal world kernel driver. We're following ARM SMC Calling > > + * Convention as specified in > > + * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html > > The values defined in the SMC Calling Conventions (which have nothing to > do with OP-TEE as such), we should probably prefix with SMCCC_, and have > in a shared file. > > > + * > > + * This file depends on optee_msg.h being included to expand the SMC id > > + * macros below. > > + */ > > + > > +#define OPTEE_SMC_32 0 > > +#define OPTEE_SMC_64 0x40000000 > > +#define OPTEE_SMC_FAST_CALL 0x80000000 > > +#define OPTEE_SMC_STD_CALL 0 > > The zero cases look a bit odd here. They might be better-documented if > you defined them with shifts, e.g. > > #define SMCCC_SMC_32 (0 << 30) > #define SMCCC_SMC_64 (1 << 30) > #define SMCCC_FAST_CALL (1 << 31) > #define SMCCC_STD_CALL (0 << 31) OK > > [...] > > > +/* > > + * Cache settings for shared memory > > + */ > > +#define OPTEE_SMC_SHM_NONCACHED 0ULL > > +#define OPTEE_SMC_SHM_CACHED 1ULL > > What precise set of memory attributes do these imply? OPTEE_SMC_SHM_NONCACHED is generally not used, but supposed to match how the kernel maps noncached memory. OP-TEE maps this as Device-nGnRE Outer sharable memory (MAIR ATTR = 0x04) OPTEE_SMC_SHM_CACHED is cached memory with settings matching how the kernel maps cached memory. OP-TEE maps this as as Normal Memory, Outer Write-back non-transient Outer Read Allocate Outer Write Allocate Inner Write-back non-transient Inner Read Allocate Inner Write Allocate Inner sharable (MAIR ATTR = 0xff). OP-TEE is more or less always compiled for a specific platform so if the kernel uses some other mapping for a particular platform we'll change the OP-TEE settings to be compatible with the kernel on that platform. > > [...] > > > +/* > > + * Same values as TEE_PARAM_* from TEE Internal API > > + */ > > +#define OPTEEM_ATTR_TYPE_NONE 0 > > +#define OPTEEM_ATTR_TYPE_VALUE_INPUT 1 > > +#define OPTEEM_ATTR_TYPE_VALUE_OUTPUT 2 > > +#define OPTEEM_ATTR_TYPE_VALUE_INOUT 3 > > +#define OPTEEM_ATTR_TYPE_MEMREF_INPUT 5 > > +#define OPTEEM_ATTR_TYPE_MEMREF_OUTPUT 6 > > +#define OPTEEM_ATTR_TYPE_MEMREF_INOUT 7 > > Are these well-defined ABI values? Yes. > > As mentioned previously, OPTEEM_* is opaque, and OPTEE_MSG_* would be > far clearer. > > > +/** > > + * struct opteem_param_memref - memory reference > > + * @buf_ptr: Address of the buffer > > + * @size: Size of the buffer > > + * @shm_ref: Shared memory reference only used by normal world > > + * > > + * Secure and normal world communicates pointers as physical address > > + * instead of the virtual address. This is because secure and normal world > > + * have completely independent memory mapping. Normal world can even have a > > + * hypervisor which need to translate the guest physical address (AKA IPA > > + * in ARM documentation) to a real physical address before passing the > > + * structure to secure world. > > + */ > > +struct opteem_param_memref { > > + __u64 buf_ptr; > > + __u64 size; > > + __u64 shm_ref; > > +}; > > Why does this mention physical addresses at all? What does that have to > do with userspace? > > What is the shm_ref, and who allocates it? > > There should really be some documentation for this. Agree. buf_ptr is a physical address (IPA or PA depending on context) outside user space, in user space it's an offset into the shm_ref. shm_ref is a pointer to struct tee_shm in the kernel, an opaque handle in secure world, and a file descriptor (connected to a struct tee_shm) in user space. > > > +/** > > + * struct opteem_param_value - values > > + * @a: first value > > + * @b: second value > > + * @c: third value > > + */ > > +struct opteem_param_value { > > + __u64 a; > > + __u64 b; > > + __u64 c; > > +}; > > Are OP-TEE messages defined to only ever take three parameters? No, this is a value parameter, each value parameter can carry three value. An OP-TEE message carry the number of parameters specified with num_params in struct opteem_arg. > > [...] > > > +/** > > + * struct optee_cmd_prefix - initial header for all user space buffers > > + * @func_id: Function Id OPTEEM_FUNCID_* below > > + * @pad: padding to make the struct size a multiple of 8 bytes > > + * > > + * This struct is 8 byte aligned since it's always followed by a struct > > + * opteem_arg which requires 8 byte alignment. > > + */ > > +struct opteem_cmd_prefix { > > + __u32 func_id; > > + __u32 pad __aligned(8); > > +}; > > Shouldn't the alignment be applied to the struct as a whole rather than > the final field? OK, I didn't know which was the preferred location to apply the aligned attribute. > > > +/* > > + * Sleep mutex, helper for secure world to implement a sleeping mutex. > > + * struct opteem_arg::func one of OPTEEM_RPC_SLEEP_MUTEX_* below > > + * > > + * OPTEEM_RPC_SLEEP_MUTEX_WAIT > > + * [in] param[0].value .a sleep mutex key > > + * .b wait tick > > + * [not used] param[1] > > + * > > + * OPTEEM_RPC_SLEEP_MUTEX_WAKEUP > > + * [in] param[0].value .a sleep mutex key > > + * .b wait after > > + * [not used] param[1] > > + * > > + * OPTEEM_RPC_SLEEP_MUTEX_DELETE > > + * [in] param[0].value .a sleep mutex key > > + * [not used] param[1] > > + */ > > +#define OPTEEM_RPC_SLEEP_MUTEX_WAIT 0 > > +#define OPTEEM_RPC_SLEEP_MUTEX_WAKEUP 1 > > +#define OPTEEM_RPC_SLEEP_MUTEX_DELETE 2 > > +#define OPTEEM_RPC_CMD_SLEEP_MUTEX 4 > > I'm lost. Why are mutexes exposed to userspace or the secure world in > such a manner? You're right it should go into another file not exposed to user space. Thanks for taking the time to review this. Regards, Jens
Hi, Just for completeness, > On 20 May 2015, at 14:16, Jens Wiklander <jens.wiklander@linaro.org> wrote: > > Hi, > > On Mon, May 18, 2015 at 02:18:50PM +0100, Mark Rutland wrote: >> Hi, >> >> On Fri, May 15, 2015 at 07:34:27AM +0100, Jens Wiklander wrote: > […] >> >> I'm also very concerned that the interface exposed to userspace is >> hideously low-level. Surely we'd expect kernel-side drivers to be doing >> the bulk of direct communication to the OP-TEE instance? In the lack of >> a provided rationale I don't see why the current messaging interface >> would make sense. > The kernel-side does all the direct communication since there's where > the SMC is done, but one level above most of the communication is > terminated in user space. Loading of Trusted Applications and other file > system access is done in by a helper process in user space, > tee-supplicant. A large part of the OP-TEE message protocol is > transparent to the kernel. > > We're trying to not exclude any TEE implementation by having this low > level TEE_IOC_CMD instead of a high level interface. The problem with > the different TEEs is that they are designed differently and we haven't > been able to find a high level interface that suits all. Applications > aren't expected to use TEE_IOC_CMD directly, instead there's supposed to > be a client lib wich wraps the kernel interface and provides a higher > level interface, for instance a Global Platform Client API in the case > of a GP TEE. > > For OP-TEE we're using the same protocol all the way down to user space, > the advantage is that it's only one protocol to keep track of and we > don't need to translate the entire message (we do need to copy it, > excluding the payload) each time the message is passed to the next > memory space. In the presence of a hypervisor we have > user space -> kernel -> hypervisor -> secure world > Unfortunately some fields has a different meaning in user space and > kernel space. I'll address this in the documentation. > > The OP-TEE helper process, tee-supplicant, is specific to only OP-TEE. > Other TEEs uses helper processes too, but what they do depend on the > design of the TEE. As a consequence more or less all TEEs needs > something specific for that particular TEE in user space to be fully > functional. > > To summarize, the current assumption is that all TEEs can't use the same > high level interface. Instead we need to provide a way for each TEE to > have their own low level interface which should be wrapped in a user > space library to provide a more reasonable interface to the client > application. > > This design aims to be TEE agnostic. As Jens mentioned, user space applications are supposed to use client-side API’s (e.g., Global Platform). It is convenient that these APIs reside in user space together with the wrapper using the kernel TEE interface. Note that GP’s APIs have changed much since they were first released. It would be a lot of work to keep them updated in the kernel and we would eventually run into the issue that they are not backwards compatible, thus making user space applications break. Also, we want to eventually support kernel submodules to use the TEE subsystem. In this case, having a simple in-kernel interface makes things easier. Again, having a rich API such as GP in the kernel would not add any value. […] Javier.
On Wed, May 20, 2015 at 01:16:48PM +0100, Jens Wiklander wrote: > Hi, > > On Mon, May 18, 2015 at 02:18:50PM +0100, Mark Rutland wrote: > > Hi, > > > > On Fri, May 15, 2015 at 07:34:27AM +0100, Jens Wiklander wrote: > > > Adds a OP-TEE driver which also can be compiled as a loadable module. > > > > > > * Targets ARM and ARM64 > > > * Supports using reserved memory from OP-TEE as shared memory > > > * CMA as shared memory is optional and only tried if OP-TEE doesn't > > > supply a reserved shared memory region > > > > How does OP-TEE provide that reserved memory? How is that described in > > DT/UEFI to the OS (e.g. is there a memreserve, or is the memory not > > described at all)? > It's either memreserve or not described at all. This should only be > needed when secure world is limited in which memory it can use for > shared memory. Currently all OP-TEE ports uses reserved shared memory, > but we're moving away from it to avoid the problem with updating DT. Ok. It's worth noting that memreserves allow the kernel to map the memory with cacheable attributes, which can result in coherency problems if it's expected to access any buffer with non-cacheable attributes. > > > * Probes OP-TEE version using SMCs > > > * Accepts requests on privileged and unprivileged device > > > * Uses OPTEE message protocol version 2 > > > > > > Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org> > > > --- > > > Documentation/devicetree/bindings/optee/optee.txt | 17 + > > > > I'm concerned that there's no documentation regarding the interface > > exposed to userspace, for neither rationale nor usage. > OK, I'll add something. > > > > > I'm also very concerned that the interface exposed to userspace is > > hideously low-level. Surely we'd expect kernel-side drivers to be doing > > the bulk of direct communication to the OP-TEE instance? In the lack of > > a provided rationale I don't see why the current messaging interface > > would make sense. > The kernel-side does all the direct communication since there's where > the SMC is done, but one level above most of the communication is > terminated in user space. Loading of Trusted Applications and other file > system access is done in by a helper process in user space, > tee-supplicant. A large part of the OP-TEE message protocol is > transparent to the kernel. So you expect userspace clients rather than kernel drivers plugging into this framework? > We're trying to not exclude any TEE implementation by having this low > level TEE_IOC_CMD instead of a high level interface. The problem with > the different TEEs is that they are designed differently and we haven't > been able to find a high level interface that suits all. Applications > aren't expected to use TEE_IOC_CMD directly, instead there's supposed to > be a client lib wich wraps the kernel interface and provides a higher > level interface, for instance a Global Platform Client API in the case > of a GP TEE. > > For OP-TEE we're using the same protocol all the way down to user space, > the advantage is that it's only one protocol to keep track of and we > don't need to translate the entire message (we do need to copy it, > excluding the payload) each time the message is passed to the next > memory space. In the presence of a hypervisor we have > user space -> kernel -> hypervisor -> secure world > Unfortunately some fields has a different meaning in user space and > kernel space. I'll address this in the documentation. > > The OP-TEE helper process, tee-supplicant, is specific to only OP-TEE. > Other TEEs uses helper processes too, but what they do depend on the > design of the TEE. As a consequence more or less all TEEs needs > something specific for that particular TEE in user space to be fully > functional. I'm not sure that your proposed kernel/user split is ideal. How does userspace determine the appropriate TEE client to use? What's required in the way of arbitration between clients? > To summarize, the current assumption is that all TEEs can't use the same > high level interface. Instead we need to provide a way for each TEE to > have their own low level interface which should be wrapped in a user > space library to provide a more reasonable interface to the client > application. [...] > > > +* OP-TEE based on ARM TrustZone required properties: > > > + > > > +- compatible="optee,optee-tz" > > > + > > > +Example: > > > + optee { > > > + compatible="optee,optee-tz"; > > > + }; > > > > What does the OP-TEE protocol give in the way of discoverability? Is it > > expected that the specific implementation and/or features will be > > detected dynamically? > We have OPTEEM_FUNCID_GET_OS_UUID and OPTEEM_FUNCID_GET_OS_REVISION > which the client can use to identify which particular OP-TEE it's > talking to. Ok. > This is not so interesting for the driver, but the client may care > when there's more than one TEE using the OP-TEE message protocol in a > single system. How does having more than one TEE work given there's a single conduit? > There's also OPTEEM_FUNCID_CALLS_UID and OPTEEM_FUNCID_CALLS_REVISION > (required by SMC Calling Convention), but those are expected to return > static values except OPTEEM_REVISION_MINOR which would be increased if > some new message type is added in the future. > > To summarize, OPTEEM_FUNCID_CALLS_* identifies the OP-TEE message > protocol and OPTEEM_FUNCID_GET_OS_* identifies the OP-TEE OS > implementation. > > > > > Where can I find documentation on the protocol? > The documentation is currently include/uapi/linux/optee_msg.h and > drivers/tee/optee/optee_smc.h. I'll add something under Documentation. > > There's more details at http://shorl.com/lubopribokygy , but that's not > entirely updated with this driver. > > > > > > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt > > > index 8033919..17c2a7e 100644 > > > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt > > > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt > > > @@ -141,6 +141,7 @@ nvidia NVIDIA > > > nxp NXP Semiconductors > > > onnn ON Semiconductor Corp. > > > opencores OpenCores.org > > > +optee OP-TEE, Open Portable Trusted Execution Environment > > > ortustech Ortus Technology Co., Ltd. > > > ovti OmniVision Technologies > > > panasonic Panasonic Corporation > > > diff --git a/MAINTAINERS b/MAINTAINERS > > > index dfcc9cc..1234695 100644 > > > > Please split the DT binding parts into a separate patch, at the start of > > the series. > OK > > > > > > diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile > > > new file mode 100644 > > > index 0000000..096651d > > > --- /dev/null > > > +++ b/drivers/tee/optee/Makefile > > > @@ -0,0 +1,13 @@ > > > +obj-$(CONFIG_OPTEE) += optee.o > > > +optee-objs += core.o > > > +optee-objs += call.o > > > +ifdef CONFIG_ARM > > > +plus_sec := $(call as-instr,.arch_extension sec,+sec) > > > +AFLAGS_smc_a32.o := -Wa,-march=armv7-a$(plus_sec) > > > +optee-objs += smc_a32.o > > > +endif > > > +ifdef CONFIG_ARM64 > > > +optee-objs += smc_a64.o > > > +endif > > > > The assembly objects should probably live under the relevant arch/ > > folders, and can probably be shared with clients for other services > > compliant with the SMC Calling Conventions. > OK, sounds good. Where should I put the smccc.h file to be able to share > it between arch/arm and arch/arm64, under include/asm-generic? I'd imagine the SMCCC stuff could live at include/linux/arm_smccc.h (following the example of efi.h). > > > +/* > > > + * Cache settings for shared memory > > > + */ > > > +#define OPTEE_SMC_SHM_NONCACHED 0ULL > > > +#define OPTEE_SMC_SHM_CACHED 1ULL > > > > What precise set of memory attributes do these imply? > OPTEE_SMC_SHM_NONCACHED is generally not used, but supposed to match how > the kernel maps noncached memory. OP-TEE maps this as Device-nGnRE > Outer sharable memory (MAIR ATTR = 0x04) > > OPTEE_SMC_SHM_CACHED is cached memory with settings matching how the > kernel maps cached memory. OP-TEE maps this as as Normal Memory, > Outer Write-back non-transient Outer Read Allocate Outer Write Allocate > Inner Write-back non-transient Inner Read Allocate Inner Write Allocate > Inner sharable (MAIR ATTR = 0xff). > > OP-TEE is more or less always compiled for a specific platform so if the > kernel uses some other mapping for a particular platform we'll change the > OP-TEE settings to be compatible with the kernel on that platform. That assumes that the TEE has to know about any kernel that might run. It also implies that a kernel needs to know what each TEE thinks the kernel will be mapping memory as, so it can work around whatever decision has been made by the TEE. So as it stands I think that's a broken design. The attributes you need should be strictly specified. It's perfectly valid for that strict specification to be the same attributes the kernel uses now, but the spec can't change later. Otherwise mismatched attributes will get in the way on some platform, and it's going to be close to impossible to fix things up. > > > +/** > > > + * struct opteem_param_memref - memory reference > > > + * @buf_ptr: Address of the buffer > > > + * @size: Size of the buffer > > > + * @shm_ref: Shared memory reference only used by normal world > > > + * > > > + * Secure and normal world communicates pointers as physical address > > > + * instead of the virtual address. This is because secure and normal world > > > + * have completely independent memory mapping. Normal world can even have a > > > + * hypervisor which need to translate the guest physical address (AKA IPA > > > + * in ARM documentation) to a real physical address before passing the > > > + * structure to secure world. > > > + */ > > > +struct opteem_param_memref { > > > + __u64 buf_ptr; > > > + __u64 size; > > > + __u64 shm_ref; > > > +}; > > > > Why does this mention physical addresses at all? What does that have to > > do with userspace? > > > > What is the shm_ref, and who allocates it? > > > > There should really be some documentation for this. > Agree. > > buf_ptr is a physical address (IPA or PA depending on context) outside > user space, in user space it's an offset into the shm_ref. > > shm_ref is a pointer to struct tee_shm in the kernel, an opaque handle > in secure world, and a file descriptor (connected to a struct tee_shm) > in user space. If this is the header for userspace, the comments should be useful to userspace. Surely you can have a different structure kernel-side if you need to encode different values? Thanks, Mark.
On Fri, May 22, 2015 at 05:27:59PM +0100, Mark Rutland wrote: > On Wed, May 20, 2015 at 01:16:48PM +0100, Jens Wiklander wrote: > > Hi, > > > > On Mon, May 18, 2015 at 02:18:50PM +0100, Mark Rutland wrote: > > > Hi, > > > > > > On Fri, May 15, 2015 at 07:34:27AM +0100, Jens Wiklander wrote: > > > > Adds a OP-TEE driver which also can be compiled as a loadable module. > > > > > > > > * Targets ARM and ARM64 > > > > * Supports using reserved memory from OP-TEE as shared memory > > > > * CMA as shared memory is optional and only tried if OP-TEE doesn't > > > > supply a reserved shared memory region > > > > > > How does OP-TEE provide that reserved memory? How is that described in > > > DT/UEFI to the OS (e.g. is there a memreserve, or is the memory not > > > described at all)? > > It's either memreserve or not described at all. This should only be > > needed when secure world is limited in which memory it can use for > > shared memory. Currently all OP-TEE ports uses reserved shared memory, > > but we're moving away from it to avoid the problem with updating DT. > > Ok. It's worth noting that memreserves allow the kernel to map the > memory with cacheable attributes, which can result in coherency problems > if it's expected to access any buffer with non-cacheable attributes. Thanks, that rules out memreserve for this. > > > > * Probes OP-TEE version using SMCs > > > > * Accepts requests on privileged and unprivileged device > > > > * Uses OPTEE message protocol version 2 > > > > > > > > Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org> > > > > --- > > > > Documentation/devicetree/bindings/optee/optee.txt | 17 + > > > > > > I'm concerned that there's no documentation regarding the interface > > > exposed to userspace, for neither rationale nor usage. > > OK, I'll add something. > > > > > > > > I'm also very concerned that the interface exposed to userspace is > > > hideously low-level. Surely we'd expect kernel-side drivers to be doing > > > the bulk of direct communication to the OP-TEE instance? In the lack of > > > a provided rationale I don't see why the current messaging interface > > > would make sense. > > The kernel-side does all the direct communication since there's where > > the SMC is done, but one level above most of the communication is > > terminated in user space. Loading of Trusted Applications and other file > > system access is done in by a helper process in user space, > > tee-supplicant. A large part of the OP-TEE message protocol is > > transparent to the kernel. > > So you expect userspace clients rather than kernel drivers plugging into > this framework? The OP-TEE message protocol is primarily for the OP-TEE driver. Other TEE drivers plugging into this framwork may use this protocol too, but I guess that most will use their own message protocol. Provided that each TEE driver rolls their own protocol I'm expecting one counter part in user space for each TEE driver. The user space client will know which kind of TEE it's talking to through TEE_IOC_VERSION. > > > We're trying to not exclude any TEE implementation by having this low > > level TEE_IOC_CMD instead of a high level interface. The problem with > > the different TEEs is that they are designed differently and we haven't > > been able to find a high level interface that suits all. Applications > > aren't expected to use TEE_IOC_CMD directly, instead there's supposed to > > be a client lib wich wraps the kernel interface and provides a higher > > level interface, for instance a Global Platform Client API in the case > > of a GP TEE. > > > > For OP-TEE we're using the same protocol all the way down to user space, > > the advantage is that it's only one protocol to keep track of and we > > don't need to translate the entire message (we do need to copy it, > > excluding the payload) each time the message is passed to the next > > memory space. In the presence of a hypervisor we have > > user space -> kernel -> hypervisor -> secure world > > Unfortunately some fields has a different meaning in user space and > > kernel space. I'll address this in the documentation. > > > > The OP-TEE helper process, tee-supplicant, is specific to only OP-TEE. > > Other TEEs uses helper processes too, but what they do depend on the > > design of the TEE. As a consequence more or less all TEEs needs > > something specific for that particular TEE in user space to be fully > > functional. > > I'm not sure that your proposed kernel/user split is ideal. How does > userspace determine the appropriate TEE client to use? What's required > in the way of arbitration between clients? Each client loops through /dev/tee[0-9]* until it finds a TEE it can communicate with, or if the client is looking for a specific TEE until it's found. TEE_IOC_VERSION is used to tell which kind of TEE the client is talking to. For a library that implements Global Platforms TEE Client API I imagine that in TEEC_InitializeContext() the lib will detect which TEE it's talking to and initialize the TEEC_Context appropriately. For clients that doesn't care about Global Platform APIs I guess that they will search for a specific TEE and give up if it's not found. tee-supplicant is a special case since it's a helper process for the TEE. The will likely be one tee-supplicant implementation (tee-supplicant-optee, tee-supplicant-xyz, etc) for each TEE that user space can support. tee-supplicant is looking for a TEE to connect to through /dev/teepriv[0-9]*. The reason for having /dev/teeX for normal clients and /dev/teeprivX for tee-supplicants we'd like to have any easy way to set different permission on the devices. > > > > +* OP-TEE based on ARM TrustZone required properties: > > > > + > > > > +- compatible="optee,optee-tz" > > > > + > > > > +Example: > > > > + optee { > > > > + compatible="optee,optee-tz"; > > > > + }; > > > > > > What does the OP-TEE protocol give in the way of discoverability? Is it > > > expected that the specific implementation and/or features will be > > > detected dynamically? > > We have OPTEEM_FUNCID_GET_OS_UUID and OPTEEM_FUNCID_GET_OS_REVISION > > which the client can use to identify which particular OP-TEE it's > > talking to. > > Ok. > > > This is not so interesting for the driver, but the client may care > > when there's more than one TEE using the OP-TEE message protocol in a > > single system. > > How does having more than one TEE work given there's a single conduit? Each TEE gets its own /dev/teeXX when the specific TEE driver registers in the framework. [...] > > > > diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile > > > > new file mode 100644 > > > > index 0000000..096651d > > > > --- /dev/null > > > > +++ b/drivers/tee/optee/Makefile > > > > @@ -0,0 +1,13 @@ > > > > +obj-$(CONFIG_OPTEE) += optee.o > > > > +optee-objs += core.o > > > > +optee-objs += call.o > > > > +ifdef CONFIG_ARM > > > > +plus_sec := $(call as-instr,.arch_extension sec,+sec) > > > > +AFLAGS_smc_a32.o := -Wa,-march=armv7-a$(plus_sec) > > > > +optee-objs += smc_a32.o > > > > +endif > > > > +ifdef CONFIG_ARM64 > > > > +optee-objs += smc_a64.o > > > > +endif > > > > > > The assembly objects should probably live under the relevant arch/ > > > folders, and can probably be shared with clients for other services > > > compliant with the SMC Calling Conventions. > > OK, sounds good. Where should I put the smccc.h file to be able to share > > it between arch/arm and arch/arm64, under include/asm-generic? > > I'd imagine the SMCCC stuff could live at include/linux/arm_smccc.h > (following the example of efi.h). Thanks. > > > > +/* > > > > + * Cache settings for shared memory > > > > + */ > > > > +#define OPTEE_SMC_SHM_NONCACHED 0ULL > > > > +#define OPTEE_SMC_SHM_CACHED 1ULL > > > > > > What precise set of memory attributes do these imply? > > OPTEE_SMC_SHM_NONCACHED is generally not used, but supposed to match how > > the kernel maps noncached memory. OP-TEE maps this as Device-nGnRE > > Outer sharable memory (MAIR ATTR = 0x04) > > > > OPTEE_SMC_SHM_CACHED is cached memory with settings matching how the > > kernel maps cached memory. OP-TEE maps this as as Normal Memory, > > Outer Write-back non-transient Outer Read Allocate Outer Write Allocate > > Inner Write-back non-transient Inner Read Allocate Inner Write Allocate > > Inner sharable (MAIR ATTR = 0xff). > > > > OP-TEE is more or less always compiled for a specific platform so if the > > kernel uses some other mapping for a particular platform we'll change the > > OP-TEE settings to be compatible with the kernel on that platform. > > That assumes that the TEE has to know about any kernel that might run. > It also implies that a kernel needs to know what each TEE thinks the > kernel will be mapping memory as, so it can work around whatever > decision has been made by the TEE. > > So as it stands I think that's a broken design. The attributes you need > should be strictly specified. It's perfectly valid for that strict > specification to be the same attributes the kernel uses now, but the > spec can't change later. > > Otherwise mismatched attributes will get in the way on some platform, > and it's going to be close to impossible to fix things up. OK, I see the problem. Is it OK only specify the attributes that need to be compatible like: #define OPTEE_SMC_SHM_ICACHED (1 << 0) #define OPTEE_SMC_SHM_IWRITE_THROUGH (1 << 1) #define OPTEE_SMC_SHM_IWRITE_BACK (1 << 2) #define OPTEE_SMC_SHM_ISHARABLE (1 << 3) #define OPTEE_SMC_SHM_OCACHED (1 << 4) #define OPTEE_SMC_SHM_OWRITE_THROUGH (1 << 5) #define OPTEE_SMC_SHM_OWRITE_BACK (1 << 6) #define OPTEE_SMC_SHM_OSHARABLE (1 << 7) #define OPTEE_SMC_SHM_CACHED \ (OPTEE_SMC_SHM_ICACHED | OPTEE_SMC_SHM_IWRITE_BACK | \ OPTEE_SMC_SHM_ISHARABLE | OPTEE_SMC_SHM_OCACHED | \ OPTEE_SMC_SHM_OWRITE_BACK) I'll drop the OPTEE_SMC_SHM_NONCACHED define as it's currently not used. > > > > +/** > > > > + * struct opteem_param_memref - memory reference > > > > + * @buf_ptr: Address of the buffer > > > > + * @size: Size of the buffer > > > > + * @shm_ref: Shared memory reference only used by normal world > > > > + * > > > > + * Secure and normal world communicates pointers as physical address > > > > + * instead of the virtual address. This is because secure and normal world > > > > + * have completely independent memory mapping. Normal world can even have a > > > > + * hypervisor which need to translate the guest physical address (AKA IPA > > > > + * in ARM documentation) to a real physical address before passing the > > > > + * structure to secure world. > > > > + */ > > > > +struct opteem_param_memref { > > > > + __u64 buf_ptr; > > > > + __u64 size; > > > > + __u64 shm_ref; > > > > +}; > > > > > > Why does this mention physical addresses at all? What does that have to > > > do with userspace? > > > > > > What is the shm_ref, and who allocates it? > > > > > > There should really be some documentation for this. > > Agree. > > > > buf_ptr is a physical address (IPA or PA depending on context) outside > > user space, in user space it's an offset into the shm_ref. > > > > shm_ref is a pointer to struct tee_shm in the kernel, an opaque handle > > in secure world, and a file descriptor (connected to a struct tee_shm) > > in user space. > > If this is the header for userspace, the comments should be useful to > userspace. Surely you can have a different structure kernel-side if you > need to encode different values? OK, I'll make separate structures. Thanks, Jens
On Wed, May 20, 2015 at 02:16:48PM +0200, Jens Wiklander wrote: > > > +#define OPTEE_SMC_SHM_NONCACHED 0ULL > > > +#define OPTEE_SMC_SHM_CACHED 1ULL > > What precise set of memory attributes do these imply? ... > OP-TEE is more or less always compiled for a specific platform so if the > kernel uses some other mapping for a particular platform we'll change the > OP-TEE settings to be compatible with the kernel on that platform. I'm not convinced that's a realistic assumption going forwards - I certainly hope it isn't. Currently TEE code tends to be only built for verticial systems because of this lack of standardiation (and the fact that so much is proprietary at the minute) but hopefully one of the results of having a reusable solution in the standard kernel with an open platform for the secure side will be that people will be more able to use this, perhaps in applications where things like single kernel support are much more important than they are for the environments where TEEs are currently deployed.
> > > > I'm also very concerned that the interface exposed to userspace is > > > > hideously low-level. Surely we'd expect kernel-side drivers to be doing > > > > the bulk of direct communication to the OP-TEE instance? In the lack of > > > > a provided rationale I don't see why the current messaging interface > > > > would make sense. > > > The kernel-side does all the direct communication since there's where > > > the SMC is done, but one level above most of the communication is > > > terminated in user space. Loading of Trusted Applications and other file > > > system access is done in by a helper process in user space, > > > tee-supplicant. A large part of the OP-TEE message protocol is > > > transparent to the kernel. > > > > So you expect userspace clients rather than kernel drivers plugging into > > this framework? > > The OP-TEE message protocol is primarily for the OP-TEE driver. Other > TEE drivers plugging into this framwork may use this protocol too, but I > guess that most will use their own message protocol. > > Provided that each TEE driver rolls their own protocol I'm expecting one > counter part in user space for each TEE driver. The user space client > will know which kind of TEE it's talking to through TEE_IOC_VERSION. Surely that means you need to have every possible user-space client present in a given filesystem, and you need to have all of them try to probe the FW to figure out whether appropriate FW is present? That sounds somewhat heavyweight. To me it would seem a lot better to have the minimal drivers in the kernel that get probed based on information from the FW. The main TEE driver would query the generic APIs to discover which features are exposed, then instantiate the relevant set of TEE-specific drivers based on TEE_IOC_VERSION and friends. To handle a need for userspace components you could emit uevents as necessary, though I'm still unclear on what the userspace components would do. > > > We're trying to not exclude any TEE implementation by having this low > > > level TEE_IOC_CMD instead of a high level interface. The problem with > > > the different TEEs is that they are designed differently and we haven't > > > been able to find a high level interface that suits all. Applications > > > aren't expected to use TEE_IOC_CMD directly, instead there's supposed to > > > be a client lib wich wraps the kernel interface and provides a higher > > > level interface, for instance a Global Platform Client API in the case > > > of a GP TEE. > > > > > > For OP-TEE we're using the same protocol all the way down to user space, > > > the advantage is that it's only one protocol to keep track of and we > > > don't need to translate the entire message (we do need to copy it, > > > excluding the payload) each time the message is passed to the next > > > memory space. In the presence of a hypervisor we have > > > user space -> kernel -> hypervisor -> secure world > > > Unfortunately some fields has a different meaning in user space and > > > kernel space. I'll address this in the documentation. > > > > > > The OP-TEE helper process, tee-supplicant, is specific to only OP-TEE. > > > Other TEEs uses helper processes too, but what they do depend on the > > > design of the TEE. As a consequence more or less all TEEs needs > > > something specific for that particular TEE in user space to be fully > > > functional. > > > > I'm not sure that your proposed kernel/user split is ideal. How does > > userspace determine the appropriate TEE client to use? What's required > > in the way of arbitration between clients? > > Each client loops through /dev/tee[0-9]* until it finds a TEE it can > communicate with, or if the client is looking for a specific TEE until > it's found. > > TEE_IOC_VERSION is used to tell which kind of TEE the client is talking > to. For a library that implements Global Platforms TEE Client API I > imagine that in TEEC_InitializeContext() the lib will detect which TEE > it's talking to and initialize the TEEC_Context appropriately. > > For clients that doesn't care about Global Platform APIs I guess that > they will search for a specific TEE and give up if it's not found. That covers detection, but what about arbitrartion? What happens when I have multiple clients which want to communicate with the same TEE simultaneously? > tee-supplicant is a special case since it's a helper process for the > TEE. The will likely be one tee-supplicant implementation > (tee-supplicant-optee, tee-supplicant-xyz, etc) for each TEE that user > space can support. tee-supplicant is looking for a TEE to connect to > through /dev/teepriv[0-9]*. > > The reason for having /dev/teeX for normal clients and /dev/teeprivX for > tee-supplicants we'd like to have any easy way to set different permission > on the devices. What do TEE supplicants do? [...] > > How does having more than one TEE work given there's a single conduit? > > Each TEE gets its own /dev/teeXX when the specific TEE driver registers > in the framework. Ok. > > > > > +/* > > > > > + * Cache settings for shared memory > > > > > + */ > > > > > +#define OPTEE_SMC_SHM_NONCACHED 0ULL > > > > > +#define OPTEE_SMC_SHM_CACHED 1ULL > > > > > > > > What precise set of memory attributes do these imply? > > > OPTEE_SMC_SHM_NONCACHED is generally not used, but supposed to match how > > > the kernel maps noncached memory. OP-TEE maps this as Device-nGnRE > > > Outer sharable memory (MAIR ATTR = 0x04) > > > > > > OPTEE_SMC_SHM_CACHED is cached memory with settings matching how the > > > kernel maps cached memory. OP-TEE maps this as as Normal Memory, > > > Outer Write-back non-transient Outer Read Allocate Outer Write Allocate > > > Inner Write-back non-transient Inner Read Allocate Inner Write Allocate > > > Inner sharable (MAIR ATTR = 0xff). > > > > > > OP-TEE is more or less always compiled for a specific platform so if the > > > kernel uses some other mapping for a particular platform we'll change the > > > OP-TEE settings to be compatible with the kernel on that platform. > > > > That assumes that the TEE has to know about any kernel that might run. > > It also implies that a kernel needs to know what each TEE thinks the > > kernel will be mapping memory as, so it can work around whatever > > decision has been made by the TEE. > > > > So as it stands I think that's a broken design. The attributes you need > > should be strictly specified. It's perfectly valid for that strict > > specification to be the same attributes the kernel uses now, but the > > spec can't change later. > > > > Otherwise mismatched attributes will get in the way on some platform, > > and it's going to be close to impossible to fix things up. > > OK, I see the problem. Is it OK only specify the attributes that need to > be compatible like: > #define OPTEE_SMC_SHM_ICACHED (1 << 0) > #define OPTEE_SMC_SHM_IWRITE_THROUGH (1 << 1) > #define OPTEE_SMC_SHM_IWRITE_BACK (1 << 2) > #define OPTEE_SMC_SHM_ISHARABLE (1 << 3) > #define OPTEE_SMC_SHM_OCACHED (1 << 4) > #define OPTEE_SMC_SHM_OWRITE_THROUGH (1 << 5) > #define OPTEE_SMC_SHM_OWRITE_BACK (1 << 6) > #define OPTEE_SMC_SHM_OSHARABLE (1 << 7) > > #define OPTEE_SMC_SHM_CACHED \ > (OPTEE_SMC_SHM_ICACHED | OPTEE_SMC_SHM_IWRITE_BACK | \ > OPTEE_SMC_SHM_ISHARABLE | OPTEE_SMC_SHM_OCACHED | \ > OPTEE_SMC_SHM_OWRITE_BACK) I'm not sure I follow the question. Will these specific attributes be mandated by the OP-TEE spec? The set of attributes above are certainly better specified than simply "CACHED", though it would be nice to have an architectural definition rather than just a bag of bits. The architecture maintainers will need to look at the memory attributes too. I don't think that current APIs offer fine-grained control over attributes and a UP kernel may not map memory as shareable, for example. Thanks, Mark.
On Fri, Jun 05, 2015 at 11:48:14AM +0100, Mark Rutland wrote: [...] > > The OP-TEE message protocol is primarily for the OP-TEE driver. Other > > TEE drivers plugging into this framwork may use this protocol too, but I > > guess that most will use their own message protocol. > > > > Provided that each TEE driver rolls their own protocol I'm expecting one > > counter part in user space for each TEE driver. The user space client > > will know which kind of TEE it's talking to through TEE_IOC_VERSION. > > Surely that means you need to have every possible user-space client > present in a given filesystem, and you need to have all of them try to > probe the FW to figure out whether appropriate FW is present? That > sounds somewhat heavyweight. > > To me it would seem a lot better to have the minimal drivers in the > kernel that get probed based on information from the FW. The main TEE > driver would query the generic APIs to discover which features are > exposed, then instantiate the relevant set of TEE-specific drivers based > on TEE_IOC_VERSION and friends. To handle a need for userspace > components you could emit uevents as necessary, though I'm still unclear > on what the userspace components would do. I'm not 100% sure what you mean. Given this and other comments on TEE_IOC_CMD, I give up on TEE_IOC_CMD. I'll replace it with several more specific TEE_IOC_* that will give a less complex and unified interface to user space. [...] > > > I'm not sure that your proposed kernel/user split is ideal. How does > > > userspace determine the appropriate TEE client to use? What's required > > > in the way of arbitration between clients? > > > > Each client loops through /dev/tee[0-9]* until it finds a TEE it can > > communicate with, or if the client is looking for a specific TEE until > > it's found. > > > > TEE_IOC_VERSION is used to tell which kind of TEE the client is talking > > to. For a library that implements Global Platforms TEE Client API I > > imagine that in TEEC_InitializeContext() the lib will detect which TEE > > it's talking to and initialize the TEEC_Context appropriately. > > > > For clients that doesn't care about Global Platform APIs I guess that > > they will search for a specific TEE and give up if it's not found. > > That covers detection, but what about arbitrartion? > > What happens when I have multiple clients which want to communicate with > the same TEE simultaneously? Each client opens a the same /dev/teeX and communicates over their own file descriptor. > > > tee-supplicant is a special case since it's a helper process for the > > TEE. The will likely be one tee-supplicant implementation > > (tee-supplicant-optee, tee-supplicant-xyz, etc) for each TEE that user > > space can support. tee-supplicant is looking for a TEE to connect to > > through /dev/teepriv[0-9]*. > > > > The reason for having /dev/teeX for normal clients and /dev/teeprivX for > > tee-supplicants we'd like to have any easy way to set different permission > > on the devices. > > What do TEE supplicants do? For OP-TEE (and I guess most other TEEs) it handles file system access. Having a separate user for tee-supplicant makes it easier to have strict permissions for created files etc. [...] > > > > > > +/* > > > > > > + * Cache settings for shared memory > > > > > > + */ > > > > > > +#define OPTEE_SMC_SHM_NONCACHED 0ULL > > > > > > +#define OPTEE_SMC_SHM_CACHED 1ULL > > > > > > > > > > What precise set of memory attributes do these imply? > > > > OPTEE_SMC_SHM_NONCACHED is generally not used, but supposed to match how > > > > the kernel maps noncached memory. OP-TEE maps this as Device-nGnRE > > > > Outer sharable memory (MAIR ATTR = 0x04) > > > > > > > > OPTEE_SMC_SHM_CACHED is cached memory with settings matching how the > > > > kernel maps cached memory. OP-TEE maps this as as Normal Memory, > > > > Outer Write-back non-transient Outer Read Allocate Outer Write Allocate > > > > Inner Write-back non-transient Inner Read Allocate Inner Write Allocate > > > > Inner sharable (MAIR ATTR = 0xff). > > > > > > > > OP-TEE is more or less always compiled for a specific platform so if the > > > > kernel uses some other mapping for a particular platform we'll change the > > > > OP-TEE settings to be compatible with the kernel on that platform. > > > > > > That assumes that the TEE has to know about any kernel that might run. > > > It also implies that a kernel needs to know what each TEE thinks the > > > kernel will be mapping memory as, so it can work around whatever > > > decision has been made by the TEE. > > > > > > So as it stands I think that's a broken design. The attributes you need > > > should be strictly specified. It's perfectly valid for that strict > > > specification to be the same attributes the kernel uses now, but the > > > spec can't change later. > > > > > > Otherwise mismatched attributes will get in the way on some platform, > > > and it's going to be close to impossible to fix things up. > > > > OK, I see the problem. Is it OK only specify the attributes that need to > > be compatible like: > > #define OPTEE_SMC_SHM_ICACHED (1 << 0) > > #define OPTEE_SMC_SHM_IWRITE_THROUGH (1 << 1) > > #define OPTEE_SMC_SHM_IWRITE_BACK (1 << 2) > > #define OPTEE_SMC_SHM_ISHARABLE (1 << 3) > > #define OPTEE_SMC_SHM_OCACHED (1 << 4) > > #define OPTEE_SMC_SHM_OWRITE_THROUGH (1 << 5) > > #define OPTEE_SMC_SHM_OWRITE_BACK (1 << 6) > > #define OPTEE_SMC_SHM_OSHARABLE (1 << 7) > > > > #define OPTEE_SMC_SHM_CACHED \ > > (OPTEE_SMC_SHM_ICACHED | OPTEE_SMC_SHM_IWRITE_BACK | \ > > OPTEE_SMC_SHM_ISHARABLE | OPTEE_SMC_SHM_OCACHED | \ > > OPTEE_SMC_SHM_OWRITE_BACK) > > I'm not sure I follow the question. Will these specific attributes be > mandated by the OP-TEE spec? The set of attributes above are certainly > better specified than simply "CACHED", though it would be nice to have > an architectural definition rather than just a bag of bits. > > The architecture maintainers will need to look at the memory attributes > too. I don't think that current APIs offer fine-grained control over > attributes and a UP kernel may not map memory as shareable, for example. Defining all those bits for OPTEE_SMC_SHM_CACHED didn't help much. I took the liberty to contact Catalin directly on this and my interpretation of his advice is: /* * Normal cached memory (write-back), shareable for SMP systems and not * shareable for UP systems. */ #define OPTEE_SMC_SHM_CACHED 1 This is closer to my original proposal, but with the crucial difference that OP-TEE doesn't need to know how the kernel maps other memory. OP-TEE requires the kernel to map memory shared with secure world with the attributes specified in the comment. -- Thanks, Jens
diff --git a/Documentation/devicetree/bindings/optee/optee.txt b/Documentation/devicetree/bindings/optee/optee.txt new file mode 100644 index 0000000..8cea829 --- /dev/null +++ b/Documentation/devicetree/bindings/optee/optee.txt @@ -0,0 +1,17 @@ +OP-TEE Device Tree Bindings + +OP-TEE is a piece of software using hardware features to provide a Trusted +Execution Environment. The security can be provided with ARM TrustZone, but +also by virtualization or a separate chip. As there's no single OP-TEE +vendor we're using "optee" as the first part of compatible propterty, +indicating the OP-TEE protocol is used when communicating with the secure +world. + +* OP-TEE based on ARM TrustZone required properties: + +- compatible="optee,optee-tz" + +Example: + optee { + compatible="optee,optee-tz"; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 8033919..17c2a7e 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -141,6 +141,7 @@ nvidia NVIDIA nxp NXP Semiconductors onnn ON Semiconductor Corp. opencores OpenCores.org +optee OP-TEE, Open Portable Trusted Execution Environment ortustech Ortus Technology Co., Ltd. ovti OmniVision Technologies panasonic Panasonic Corporation diff --git a/MAINTAINERS b/MAINTAINERS index dfcc9cc..1234695 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7295,6 +7295,12 @@ F: arch/*/oprofile/ F: drivers/oprofile/ F: include/linux/oprofile.h +OP-TEE DRIVER +M: Jens Wiklander <jens.wiklander@linaro.org> +S: Maintained +F: include/uapi/linux/optee_msg.h +F: drivers/tee/optee/ + ORACLE CLUSTER FILESYSTEM 2 (OCFS2) M: Mark Fasheh <mfasheh@suse.com> M: Joel Becker <jlbec@evilplan.org> diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 64a8cd7..b269276 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -6,3 +6,13 @@ config TEE help This implements a generic interface towards a Trusted Execution Environment (TEE). + +if TEE + +menu "TEE drivers" + +source "drivers/tee/optee/Kconfig" + +endmenu + +endif diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index 60d2dab..53f3c76 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -1,3 +1,4 @@ obj-y += tee.o obj-y += tee_shm.o obj-y += tee_shm_pool.o +obj-$(CONFIG_OPTEE) += optee/ diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig new file mode 100644 index 0000000..3faa855 --- /dev/null +++ b/drivers/tee/optee/Kconfig @@ -0,0 +1,19 @@ +# OP-TEE Trusted Execution Environment Configuration +config OPTEE + tristate "OP-TEE" + default n + depends on ARM || ARM64 + help + This implements the OP-TEE Trusted Execution Environment (TEE) + driver. + +if OPTEE +menu "OP-TEE options" +config OPTEE_USE_CMA + bool "Use CMA" + default n + select DMA_CMA + help + Configures OP-TEE driver to use CMA for shared memory allocations. +endmenu +endif diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile new file mode 100644 index 0000000..096651d --- /dev/null +++ b/drivers/tee/optee/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_OPTEE) += optee.o +optee-objs += core.o +optee-objs += call.o +ifdef CONFIG_ARM +plus_sec := $(call as-instr,.arch_extension sec,+sec) +AFLAGS_smc_a32.o := -Wa,-march=armv7-a$(plus_sec) +optee-objs += smc_a32.o +endif +ifdef CONFIG_ARM64 +optee-objs += smc_a64.o +endif +optee-objs += rpc.o +optee-objs += supp.o diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c new file mode 100644 index 0000000..b4c583b --- /dev/null +++ b/drivers/tee/optee/call.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/device.h> +#include <linux/tee_drv.h> +#include "optee_private.h" +#include "optee_smc.h" + +static void optee_call_lock(struct optee_call_sync *callsync) +{ + mutex_lock(&callsync->mutex); +} + +static void optee_call_lock_wait_completion(struct optee_call_sync *callsync) +{ + /* + * Release the lock until "something happens" and then reacquire it + * again. + * + * This is needed when TEE returns "busy" and we need to try again + * later. + */ + callsync->c_waiters++; + mutex_unlock(&callsync->mutex); + /* + * Wait at most one second. Secure world is normally never busy + * more than that so we should normally never timeout. + */ + wait_for_completion_timeout(&callsync->c, HZ); + mutex_lock(&callsync->mutex); + callsync->c_waiters--; +} + +static void optee_call_unlock(struct optee_call_sync *callsync) +{ + /* + * If at least one thread is waiting for "something to happen" let + * one thread know that "something has happened". + */ + if (callsync->c_waiters) + complete(&callsync->c); + mutex_unlock(&callsync->mutex); +} + +static int optee_arg_from_user(struct opteem_arg *arg, size_t size, + struct tee_shm **put_shm) +{ + struct opteem_param *param; + size_t n; + + if (!arg->num_params || !put_shm) + return -EINVAL; + + param = OPTEEM_GET_PARAMS(arg); + + for (n = 0; n < arg->num_params; n++) { + struct tee_shm *shm; + u32 shm_offs; + phys_addr_t pa; + int ret; + + if (param[n].attr & ~(OPTEEM_ATTR_TYPE_MASK | OPTEEM_ATTR_META)) + return -EINVAL; + + if (optee_param_is(param + n, PARAM_MEMREF | PARAM_INOUT)) { + shm_offs = param[n].u.memref.buf_ptr; + shm = tee_shm_get_from_fd( + (int)param[n].u.memref.shm_ref); + if (IS_ERR(shm)) + return PTR_ERR(shm); + put_shm[n] = shm; + ret = tee_shm_get_pa(shm, shm_offs, &pa); + if (ret) + return ret; + param[n].u.memref.buf_ptr = pa; + } + } + + return 0; +} + +static int optee_arg_to_user(struct opteem_arg *arg, + struct opteem_arg __user *uarg) +{ + struct opteem_param *param = OPTEEM_GET_PARAMS(arg); + struct opteem_param __user *uparam = (void __user *)(uarg + 1); + size_t n; + + if (arg->cmd == OPTEEM_CMD_OPEN_SESSION && + put_user(arg->session, &uarg->session)) + return -EINVAL; + if (put_user(arg->ret, &uarg->ret) || + put_user(arg->ret_origin, &uarg->ret_origin)) + return -EINVAL; + + for (n = 0; n < arg->num_params; n++) { + struct opteem_param *p = param + n; + struct opteem_param __user *up = uparam + n; + + if (optee_param_is(p, PARAM_VALUE | PARAM_OUT)) { + if (put_user(p->u.value.a, &up->u.value.a) || + put_user(p->u.value.b, &up->u.value.b)) + return -EINVAL; + } else if (optee_param_is(p, PARAM_MEMREF | PARAM_OUT)) { + if (put_user(p->u.memref.size, &up->u.memref.size)) + return -EINVAL; + } + } + return 0; +} + +/* Requires the filpstate mutex to be held */ +static struct optee_session *find_session(struct optee_context_data *ctxdata, + u32 session_id) +{ + struct optee_session *sess; + + list_for_each_entry(sess, &ctxdata->sess_list, list_node) + if (sess->session_id == session_id) + return sess; + return NULL; +} + +u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_smc_param param = { }; + u32 ret; + u32 cmdid = OPTEE_SMC_CALL_WITH_ARG; + + reg_pair_from_64(¶m.a1, ¶m.a2, parg); + optee_call_lock(&optee->callsync); + while (true) { + param.a0 = cmdid; + + optee_smc(¶m); + ret = param.a0; + + if (ret == OPTEE_SMC_RETURN_EBUSY) { + /* + * Since secure world returned busy, release the + * lock we had when entering this function and wait + * for "something to happen" (something else to + * exit from secure world and needed resources may + * have become available). + */ + optee_call_lock_wait_completion(&optee->callsync); + } else if (OPTEE_SMC_RETURN_IS_RPC(ret)) { + /* + * Process the RPC. We're unlocking the path to + * secure world to allow another request while + * processing the RPC. + */ + optee_call_unlock(&optee->callsync); + cmdid = optee_handle_rpc(ctx, ¶m); + optee_call_lock(&optee->callsync); + } else { + break; + } + } + optee_call_unlock(&optee->callsync); + return ret; +} + +int optee_cmd_call_with_arg(struct tee_context *ctx, struct tee_shm *shm, + struct opteem_cmd_prefix *arg, + struct opteem_cmd_prefix __user *uarg, size_t len) +{ + struct optee_context_data *ctxdata = ctx->data; + struct tee_shm **put_shm = NULL; + struct opteem_arg *opteem_arg; + struct opteem_arg __user *opteem_uarg; + struct optee_session *sess = NULL; + phys_addr_t opteem_parg; + size_t opteem_arg_size; + int rc; + size_t n; + + opteem_arg = (struct opteem_arg *)(arg + 1); + opteem_uarg = (struct opteem_arg __user *)(uarg + 1); + + opteem_arg_size = len - sizeof(*arg); + + /* Check that the header is complete */ + if (opteem_arg_size < sizeof(struct opteem_arg)) + return -EINVAL; + /* Check that there's room for the specified number of params */ + if (opteem_arg_size != OPTEEM_GET_ARG_SIZE(opteem_arg->num_params)) + return -EINVAL; + + if (opteem_arg->num_params) { + put_shm = kcalloc(opteem_arg->num_params, + sizeof(struct tee_shm *), GFP_KERNEL); + if (!put_shm) + return -ENOMEM; + /* + * The params are updated with physical addresses and the ref + * counters on the shared memory is increased. The shms to + * decreased ref counts on when the call is over are stored in + * put_shm. + */ + rc = optee_arg_from_user(opteem_arg, opteem_arg_size, put_shm); + if (rc) + goto out; + } + + rc = tee_shm_va2pa(shm, opteem_arg, &opteem_parg); + if (rc) + goto out; + + switch (opteem_arg->cmd) { + case OPTEEM_CMD_OPEN_SESSION: + /* + * Allocate memory now to be able to store the new session + * below. + */ + sess = kzalloc(sizeof(struct optee_session), GFP_KERNEL); + if (!sess) { + rc = -ENOMEM; + goto out; + } + break; + case OPTEEM_CMD_CLOSE_SESSION: + /* A session is about to be closed, remove it from the list */ + mutex_lock(&ctxdata->mutex); + sess = find_session(ctxdata, opteem_arg->session); + if (sess) + list_del(&sess->list_node); + mutex_unlock(&ctxdata->mutex); + if (!sess) { + rc = -EINVAL; + goto out; + } + kfree(sess); + sess = NULL; + break; + + case OPTEEM_CMD_INVOKE_COMMAND: + case OPTEEM_CMD_CANCEL: + mutex_lock(&ctxdata->mutex); + sess = find_session(ctxdata, opteem_arg->session); + mutex_unlock(&ctxdata->mutex); + if (!sess) { + rc = -EINVAL; + goto out; + } + sess = NULL; + break; + + default: + rc = -EINVAL; + goto out; + } + + if (optee_do_call_with_arg(ctx, opteem_parg)) { + opteem_arg->ret = TEEC_ERROR_COMMUNICATION; + opteem_arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + rc = optee_arg_to_user(opteem_arg, opteem_uarg); + + if (sess && opteem_arg->ret == TEEC_SUCCESS) { + /* A new session has been created, add it to the list. */ + sess->session_id = opteem_arg->session; + mutex_lock(&ctxdata->mutex); + list_add(&sess->list_node, &ctxdata->sess_list); + mutex_unlock(&ctxdata->mutex); + sess = NULL; + } +out: + kfree(sess); + if (put_shm) { + for (n = 0; n < opteem_arg->num_params; n++) + if (put_shm[n]) + tee_shm_put(put_shm[n]); + kfree(put_shm); + } + return rc; +} diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c new file mode 100644 index 0000000..b3f8b92d --- /dev/null +++ b/drivers/tee/optee/core.c @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/types.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/dma-contiguous.h> +#ifdef CONFIG_OPTEE_USE_CMA +#include <linux/cma.h> +#endif +#include <linux/io.h> +#include <linux/tee_drv.h> +#include "optee_private.h" +#include "optee_smc.h" + +#define DRIVER_NAME "optee" + +bool optee_param_is(struct opteem_param *param, uint32_t flags) +{ + static const u8 attr_flags[] = { + [OPTEEM_ATTR_TYPE_NONE] = 0, + [OPTEEM_ATTR_TYPE_VALUE_INPUT] = PARAM_VALUE | PARAM_IN, + [OPTEEM_ATTR_TYPE_VALUE_OUTPUT] = PARAM_VALUE | PARAM_OUT, + [OPTEEM_ATTR_TYPE_VALUE_INOUT] = PARAM_VALUE | PARAM_IN | + PARAM_OUT, + [OPTEEM_ATTR_TYPE_MEMREF_INPUT] = PARAM_MEMREF | PARAM_IN, + [OPTEEM_ATTR_TYPE_MEMREF_OUTPUT] = PARAM_MEMREF | PARAM_OUT, + [OPTEEM_ATTR_TYPE_MEMREF_INOUT] = PARAM_MEMREF | PARAM_IN | + PARAM_OUT, + }; + int idx = param->attr & OPTEEM_ATTR_TYPE_MASK; + u32 masked; + + if (idx >= sizeof(attr_flags)) + return false; + + masked = attr_flags[idx] & flags; + return (masked & PARAM_ANY) && (masked & PARAM_INOUT); +} + +static void optee_get_smc_version(struct optee_smc_param *param) +{ + param->a0 = OPTEE_SMC_CALLS_UID; + optee_smc(param); +} + +static int optee_get_version(struct tee_context *ctx, + struct tee_ioctl_version_data __user *vers) +{ + struct optee_smc_param param; + + optee_get_smc_version(¶m); + /* The first 4 words in param are the UUID of protocol */ + return copy_to_user(vers, ¶m, sizeof(*vers)); +} + +static int optee_open(struct tee_context *ctx) +{ + struct optee_context_data *ctxdata; + + ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); + if (!ctxdata) + return -ENOMEM; + + mutex_init(&ctxdata->mutex); + INIT_LIST_HEAD(&ctxdata->sess_list); + + ctx->data = ctxdata; + return 0; +} + +static void optee_release(struct tee_context *ctx) +{ + struct optee_context_data *ctxdata = ctx->data; + struct tee_shm *shm; + struct opteem_arg *arg = NULL; + phys_addr_t parg; + + if (!ctxdata) + return; + + shm = tee_shm_alloc(ctx->teedev, sizeof(struct opteem_arg), + TEE_SHM_MAPPED); + if (!IS_ERR(shm)) { + arg = tee_shm_get_va(shm, 0); + /* + * If va2pa fails for some reason, we can't call + * optee_close_session(), only free the memory. Secure OS + * will leak sessions and finally refuse more session, but + * we will at least let normal world reclaim its memory. + */ + if (!IS_ERR(arg)) + tee_shm_va2pa(shm, arg, &parg); + } + + while (true) { + struct optee_session *sess; + + sess = list_first_entry_or_null(&ctxdata->sess_list, + struct optee_session, + list_node); + if (!sess) + break; + list_del(&sess->list_node); + if (!IS_ERR_OR_NULL(arg)) { + memset(arg, 0, sizeof(*arg)); + arg->cmd = OPTEEM_CMD_CLOSE_SESSION; + arg->session = sess->session_id; + optee_do_call_with_arg(ctx, parg); + } + kfree(sess); + } + kfree(ctxdata); + + if (!IS_ERR(shm)) + tee_shm_free(shm); + + ctx->data = NULL; +} + +static int optee_cmd_raw_fastcall(u32 smc_id, struct opteem_cmd_prefix *arg, + size_t len) +{ + struct optee_smc_param param = { .a0 = smc_id }; + u32 *data = (u32 *)(arg + 1); + size_t data_len = len - sizeof(*arg); + + if (data_len < 4 * sizeof(u32)) + return -EINVAL; + + /* This is a fast-call no need to take a mutex */ + + optee_smc(¶m); + data[0] = param.a0; + data[1] = param.a1; + data[2] = param.a2; + data[3] = param.a3; + return 0; +} + +static int optee_cmd(struct tee_context *ctx, void __user *buf, size_t len) +{ + struct opteem_cmd_prefix *arg; + struct tee_shm *shm; + int ret; + + if (len > OPTEE_MAX_ARG_SIZE || len < sizeof(*arg)) + return -EINVAL; + + shm = tee_shm_alloc(ctx->teedev, len, TEE_SHM_MAPPED); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg) || copy_from_user(arg, buf, len)) { + ret = -EINVAL; + goto out; + } + + switch (arg->func_id) { + case OPTEEM_FUNCID_CALLS_UID: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_UID, arg, len); + break; + case OPTEEM_FUNCID_GET_OS_UUID: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_UUID, + arg, len); + break; + case OPTEEM_FUNCID_CALLS_REVISION: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_REVISION, + arg, len); + break; + case OPTEEM_FUNCID_GET_OS_REVISION: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_REVISION, + arg, len); + break; + case OPTEEM_FUNCID_CALL_WITH_ARG: + ret = optee_cmd_call_with_arg(ctx, shm, arg, buf, len); + goto out_from_call; + default: + ret = -EINVAL; + goto out; + } + +out: + if (!ret) { + if (copy_to_user(buf, arg, len)) + ret = -EINVAL; + } +out_from_call: + tee_shm_free(shm); + return ret; +} + +static struct tee_driver_ops optee_ops = { + .get_version = optee_get_version, + .open = optee_open, + .release = optee_release, + .cmd = optee_cmd, +}; + +static struct tee_desc optee_desc = { + .name = DRIVER_NAME "-clnt", + .ops = &optee_ops, + .owner = THIS_MODULE, +}; + +static int optee_supp_req(struct tee_context *ctx, void __user *buf, + size_t len) +{ + struct opteem_cmd_prefix *arg; + struct tee_shm *shm; + int ret; + + if (len > OPTEE_MAX_ARG_SIZE || len < sizeof(*arg)) + return -EINVAL; + + shm = tee_shm_alloc(ctx->teedev, len, TEE_SHM_MAPPED); + if (IS_ERR(shm)) { + ret = PTR_ERR(shm); + goto out; + } + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg) || copy_from_user(arg, buf, len)) { + ret = -EINVAL; + goto out; + } + + switch (arg->func_id) { + case OPTEEM_FUNCID_CALLS_UID: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_UID, arg, len); + break; + case OPTEEM_FUNCID_GET_OS_UUID: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_UUID, + arg, len); + break; + case OPTEEM_FUNCID_CALLS_REVISION: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_REVISION, + arg, len); + break; + case OPTEEM_FUNCID_GET_OS_REVISION: + ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_REVISION, + arg, len); + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto out; + + if (copy_to_user(buf, arg, len)) + ret = -EINVAL; +out: + if (!IS_ERR(shm)) + tee_shm_free(shm); + return ret; +} + +static int optee_supp_cmd(struct tee_context *ctx, void __user *buf, + size_t len) +{ + struct opteem_cmd_prefix arg; + + if (len < sizeof(arg) || copy_from_user(&arg, buf, sizeof(arg))) + return -EINVAL; + + switch (arg.func_id) { + case OPTEEM_FUNCID_SUPP_CMD_WRITE: + return optee_supp_write(ctx, buf + sizeof(arg), + len - sizeof(arg)); + case OPTEEM_FUNCID_SUPP_CMD_READ: + return optee_supp_read(ctx, buf + sizeof(arg), + len - sizeof(arg)); + default: + return optee_supp_req(ctx, buf, len); + } +} + +static struct tee_driver_ops optee_supp_ops = { + .get_version = optee_get_version, + .open = optee_open, + .release = optee_release, + .cmd = optee_supp_cmd, +}; + +static struct tee_desc optee_supp_desc = { + .name = DRIVER_NAME "-supp", + .ops = &optee_supp_ops, + .owner = THIS_MODULE, + .flags = TEE_DESC_PRIVILEGED, +}; + +static bool opteem_api_uid_is_optee_api(void) +{ + struct optee_smc_param param; + + optee_get_smc_version(¶m); + + if (param.a0 == OPTEEM_UID_0 && param.a1 == OPTEEM_UID_1 && + param.a2 == OPTEEM_UID_2 && param.a3 == OPTEEM_UID_3) + return true; + return false; +} + +static bool opteem_api_revision_is_compatible(void) +{ + struct optee_smc_param param = { .a0 = OPTEE_SMC_CALLS_REVISION }; + + optee_smc(¶m); + + if (param.a0 == OPTEEM_REVISION_MAJOR && + (int)param.a1 >= OPTEEM_REVISION_MINOR) + return true; + return false; +} + +static struct tee_shm_pool *optee_config_shm_ioremap(struct device *dev, + void __iomem **ioremaped_shm) +{ + struct optee_smc_param param = { .a0 = OPTEE_SMC_GET_SHM_CONFIG }; + struct tee_shm_pool *pool; + u_long vaddr; + phys_addr_t paddr; + size_t size; + phys_addr_t begin; + phys_addr_t end; + void __iomem *va; + + optee_smc(¶m); + if (param.a0 != OPTEE_SMC_RETURN_OK) { + dev_info(dev, "shm service not available\n"); + return ERR_PTR(-ENOENT); + } + + if (param.a3 != OPTEE_SMC_SHM_CACHED) { + dev_err(dev, "only normal cached shared memory supported\n"); + return ERR_PTR(-EINVAL); + } + + begin = roundup(param.a1, PAGE_SIZE); + end = rounddown(param.a1 + param.a2, PAGE_SIZE); + paddr = begin; + size = end - begin; + + va = ioremap_cache(paddr, size); + if (!va) { + dev_err(dev, "shared memory ioremap failed\n"); + return ERR_PTR(-EINVAL); + } + vaddr = (u_long)va; + + pool = tee_shm_pool_alloc_res_mem(dev, vaddr, paddr, size); + if (IS_ERR(pool)) + iounmap(va); + else + *ioremaped_shm = va; + return pool; +} + +#ifdef CONFIG_OPTEE_USE_CMA +static struct tee_shm_pool *optee_config_shm_cma(struct device *dev) +{ + struct optee_smc_param param = { .a0 = OPTEE_SMC_REGISTER_SHM }; + u_long vaddr; + phys_addr_t paddr; + size_t size; + struct tee_shm_pool *pool; + + pool = tee_shm_pool_alloc(dev, &vaddr, &paddr, &size); + if (IS_ERR(pool)) + return pool; + + reg_pair_from_64(¶m.a1, ¶m.a2, paddr); + param.a3 = size; + param.a4 = OPTEE_SMC_SHM_CACHED; + optee_smc(¶m); + if (param.a0 != OPTEE_SMC_RETURN_OK) { + dev_err(dev, "can't register shared memory\n"); + tee_shm_pool_free(pool); + return ERR_PTR(-EINVAL); + } + return pool; +} +#else +static struct tee_shm_pool *optee_config_shm_cma(struct device *dev) +{ + return ERR_PTR(-ENOENT); +} +#endif + +static int optee_probe(struct platform_device *pdev) +{ + struct tee_shm_pool *pool; + struct optee *optee = NULL; + void __iomem *ioremaped_shm = NULL; + int rc; + + if (!opteem_api_uid_is_optee_api() || + !opteem_api_revision_is_compatible()) + return -EINVAL; + + pool = optee_config_shm_ioremap(&pdev->dev, &ioremaped_shm); + if (IS_ERR(pool)) + pool = optee_config_shm_cma(&pdev->dev); + if (IS_ERR(pool)) + return PTR_ERR(pool); + + optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL); + if (!optee) { + rc = -ENOMEM; + goto err; + } + + optee->dev = &pdev->dev; + + optee->teedev = tee_device_alloc(&optee_desc, &pdev->dev, pool, optee); + if (IS_ERR(optee->teedev)) { + rc = PTR_ERR(optee->teedev); + goto err; + } + + optee->supp_teedev = tee_device_alloc(&optee_supp_desc, &pdev->dev, + pool, optee); + if (IS_ERR(optee->supp_teedev)) { + rc = PTR_ERR(optee->supp_teedev); + goto err; + } + + rc = tee_device_register(optee->teedev); + if (rc) + goto err; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err; + + mutex_init(&optee->callsync.mutex); + init_completion(&optee->callsync.c); + optee->callsync.c_waiters = 0; + optee_mutex_wait_init(&optee->mutex_wait); + optee_supp_init(&optee->supp); + optee->ioremaped_shm = ioremaped_shm; + optee->pool = pool; + + platform_set_drvdata(pdev, optee); + + dev_info(&pdev->dev, "initialized driver\n"); + return 0; +err: + tee_device_unregister(optee->teedev); + tee_device_unregister(optee->supp_teedev); + if (pool) + tee_shm_pool_free(pool); + if (ioremaped_shm) + iounmap(optee->ioremaped_shm); + return rc; +} + +static int optee_remove(struct platform_device *pdev) +{ + struct optee *optee = platform_get_drvdata(pdev); + + tee_device_unregister(optee->teedev); + tee_device_unregister(optee->supp_teedev); + tee_shm_pool_free(optee->pool); + if (optee->ioremaped_shm) + iounmap(optee->ioremaped_shm); + optee_mutex_wait_uninit(&optee->mutex_wait); + optee_supp_uninit(&optee->supp); + mutex_destroy(&optee->callsync.mutex); + return 0; +} + +static const struct of_device_id optee_match[] = { + { .compatible = "optee,optee-tz" }, + {}, +}; + +static struct platform_driver optee_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = optee_match, + }, + .probe = optee_probe, + .remove = optee_remove, +}; + +module_platform_driver(optee_driver); + +MODULE_AUTHOR("Linaro"); +MODULE_DESCRIPTION("OP-TEE driver"); +MODULE_SUPPORTED_DEVICE(""); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h new file mode 100644 index 0000000..86505ca --- /dev/null +++ b/drivers/tee/optee/optee_private.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef OPTEE_PRIVATE_H +#define OPTEE_PRIVATE_H + +#include <linux/types.h> +#include <linux/semaphore.h> +#include <linux/tee_drv.h> +#include <linux/optee_msg.h> + +#define OPTEE_MAX_ARG_SIZE 1024 + +/* Some Global Platform error codes used in this driver */ +#define TEEC_SUCCESS 0x00000000 +#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 +#define TEEC_ERROR_COMMUNICATION 0xFFFF000E +#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C + +#define TEEC_ORIGIN_COMMS 0x00000002 + +struct optee_call_sync { + struct mutex mutex; + struct completion c; + int c_waiters; +}; + +struct optee_mutex_wait { + struct mutex mu; + struct list_head db; +}; + +struct optee_supp { + bool supp_next_write; + size_t data_size; + const struct opteem_arg *data_to_supp; + struct opteem_arg *data_from_supp; + struct mutex thrd_mutex; + struct mutex supp_mutex; + struct semaphore data_to_supp_sem; + struct semaphore data_from_supp_sem; +}; + +struct optee { + struct tee_device *supp_teedev; + struct tee_device *teedev; + struct device *dev; + struct optee_call_sync callsync; + struct optee_mutex_wait mutex_wait; + struct optee_supp supp; + struct tee_shm_pool *pool; + void __iomem *ioremaped_shm; +}; + +struct optee_session { + struct list_head list_node; + u32 session_id; +}; + +struct optee_context_data { + struct mutex mutex; + struct list_head sess_list; +}; + +/* Note that 32bit arguments are passed also when running in 64bit */ +struct optee_smc_param { + u32 a0; + u32 a1; + u32 a2; + u32 a3; + u32 a4; + u32 a5; + u32 a6; + u32 a7; +}; + +void optee_smc(struct optee_smc_param *param); + +u32 optee_handle_rpc(struct tee_context *ctx, struct optee_smc_param *param); + +void optee_mutex_wait_init(struct optee_mutex_wait *muw); +void optee_mutex_wait_uninit(struct optee_mutex_wait *muw); + +void optee_supp_thrd_req(struct tee_context *ctx, struct opteem_arg *arg); +int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len); +int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len); +void optee_supp_init(struct optee_supp *supp); +void optee_supp_uninit(struct optee_supp *supp); + + +u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); +int optee_cmd_call_with_arg(struct tee_context *ctx, struct tee_shm *shm, + struct opteem_cmd_prefix *arg, + struct opteem_cmd_prefix __user *uarg, size_t len); + +/* + * Small helpers + */ +#define PARAM_VALUE 0x1 +#define PARAM_MEMREF 0x2 +#define PARAM_ANY (PARAM_VALUE | PARAM_MEMREF) +#define PARAM_IN 0x4 +#define PARAM_OUT 0x8 +#define PARAM_INOUT (PARAM_IN | PARAM_OUT) + +/** + * optee_param_is() - report kind of opteem parameter + * @param: the opteem parameter + * @flags: which properties of the parameter to check + * @returns true if any of PARAM_VALUE PARAM_MEMREF is satified _and_ + * any of the PARAM_IN PARAM_OUT is satisfied + */ +bool optee_param_is(struct opteem_param *param, uint32_t flags); + +static inline void *reg_pair_to_ptr(u32 reg0, u32 reg1) +{ + return (void *)(u_long)(((u64)reg0 << 32) | reg1); +} + +static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) +{ + *reg0 = val >> 32; + *reg1 = val; +} + + +#endif /*OPTEE_PRIVATE_H*/ diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h new file mode 100644 index 0000000..6254bcf --- /dev/null +++ b/drivers/tee/optee/optee_smc.h @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef OPTEE_SMC_H +#define OPTEE_SMC_H + +/* + * This file is exported by OP-TEE and is in kept in sync between secure + * world and normal world kernel driver. We're following ARM SMC Calling + * Convention as specified in + * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + * + * This file depends on optee_msg.h being included to expand the SMC id + * macros below. + */ + +#define OPTEE_SMC_32 0 +#define OPTEE_SMC_64 0x40000000 +#define OPTEE_SMC_FAST_CALL 0x80000000 +#define OPTEE_SMC_STD_CALL 0 + +#define OPTEE_SMC_OWNER_MASK 0x3F +#define OPTEE_SMC_OWNER_SHIFT 24 + +#define OPTEE_SMC_FUNC_MASK 0xFFFF + +#define OPTEE_SMC_IS_FAST_CALL(smc_val) ((smc_val) & OPTEE_SMC_FAST_CALL) +#define OPTEE_SMC_IS_64(smc_val) ((smc_val) & OPTEE_SMC_64) +#define OPTEE_SMC_FUNC_NUM(smc_val) ((smc_val) & OPTEE_SMC_FUNC_MASK) +#define OPTEE_SMC_OWNER_NUM(smc_val) \ + (((smc_val) >> OPTEE_SMC_OWNER_SHIFT) & OPTEE_SMC_OWNER_MASK) + +#define OPTEE_SMC_CALL_VAL(type, calling_convention, owner, func_num) \ + ((type) | (calling_convention) | \ + (((owner) & OPTEE_SMC_OWNER_MASK) << \ + OPTEE_SMC_OWNER_SHIFT) |\ + ((func_num) & OPTEE_SMC_FUNC_MASK)) + +#define OPTEE_SMC_STD_CALL_VAL(func_num) \ + OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_STD_CALL, \ + OPTEE_SMC_OWNER_TRUSTED_OS, (func_num)) +#define OPTEE_SMC_FAST_CALL_VAL(func_num) \ + OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \ + OPTEE_SMC_OWNER_TRUSTED_OS, (func_num)) + +#define OPTEE_SMC_OWNER_ARCH 0 +#define OPTEE_SMC_OWNER_CPU 1 +#define OPTEE_SMC_OWNER_SIP 2 +#define OPTEE_SMC_OWNER_OEM 3 +#define OPTEE_SMC_OWNER_STANDARD 4 +#define OPTEE_SMC_OWNER_TRUSTED_APP 48 +#define OPTEE_SMC_OWNER_TRUSTED_OS 50 + +#define OPTEE_SMC_OWNER_TRUSTED_OS_OPTEED 62 +#define OPTEE_SMC_OWNER_TRUSTED_OS_API 63 + +/* + * Function specified by SMC Calling convention. + */ +#define OPTEE_SMC_FUNCID_CALLS_COUNT 0xFF00 +#define OPTEE_SMC_CALLS_COUNT \ + OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \ + OPTEE_SMC_OWNER_TRUSTED_OS_API, \ + OPTEE_SMC_FUNCID_CALLS_COUNT) + + +/* + * Cache settings for shared memory + */ +#define OPTEE_SMC_SHM_NONCACHED 0ULL +#define OPTEE_SMC_SHM_CACHED 1ULL + +/* + * a0..a7 is used as register names in the descriptions below, on arm32 + * that translates to r0..r7 and on arm64 to w0..w7. In both cases it's + * 32-bit registers. + */ + +/* + * Function specified by SMC Calling convention + * + * Return one of the following UIDs if using API specified in this file + * without further extentions: + * 65cb6b93-af0c-4617-8ed6-644a8d1140f8 + * see also OPTEE_SMC_UID_* in optee_msg.h + */ +#define OPTEE_SMC_FUNCID_CALLS_UID OPTEEM_FUNCID_CALLS_UID +#define OPTEE_SMC_CALLS_UID \ + OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \ + OPTEE_SMC_OWNER_TRUSTED_OS_API, \ + OPTEE_SMC_FUNCID_CALLS_UID) + +/* + * Function specified by SMC Calling convention + * + * Returns 2.0 if using API specified in this file without further extentions. + * see also OPTEEM_REVISION_* in optee_msg.h + */ +#define OPTEE_SMC_FUNCID_CALLS_REVISION OPTEEM_FUNCID_CALLS_REVISION +#define OPTEE_SMC_CALLS_REVISION \ + OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \ + OPTEE_SMC_OWNER_TRUSTED_OS_API, \ + OPTEE_SMC_FUNCID_CALLS_REVISION) + +/* + * Get UUID of Trusted OS. + * + * Used by non-secure world to figure out which Trusted OS is installed. + * Note that returned UUID is the UUID of the Trusted OS, not of the API. + * + * Returns UUID in a0-4 in the same way as OPTEE_SMC_CALLS_UID + * described above. + */ +#define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEEM_FUNCID_GET_OS_UUID +#define OPTEE_SMC_CALL_GET_OS_UUID \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID) + +/* + * Get revision of Trusted OS. + * + * Used by non-secure world to figure out which version of the Trusted OS + * is installed. Note that the returned revision is the revision of the + * Trusted OS, not of the API. + * + * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION + * described above. + */ +#define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEEM_FUNCID_GET_OS_REVISION +#define OPTEE_SMC_CALL_GET_OS_REVISION \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION) + +/* + * Call with struct opteem_arg as argument + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG + * a1 Upper 32bit of a 64bit physical pointer to a struct opteem_arg + * a2 Lower 32bit of a 64bit physical pointer to a struct opteem_arg + * a3-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 Return value, OPTEE_SMC_RETURN_* + * a1-3 Not used + * a4-7 Preserved + * + * Ebusy return register usage: + * a0 Return value, OPTEE_SMC_RETURN_EBUSY + * a1-3 Preserved + * a4-7 Preserved + * + * RPC return register usage: + * a0 Return value, OPTEE_SMC_RETURN_IS_RPC(val) + * a1-2 RPC parameters + * a3-7 Resume information, must be preserved + * + * Possible return values: + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * OPTEE_SMC_RETURN_OK Call completed, result updated in + * the previously supplied struct + * opteem_arg. + * OPTEE_SMC_RETURN_EBUSY Trusted OS busy, try again later. + * OPTEE_SMC_RETURN_EBADADDR Bad physcial pointer to struct + * opteem_arg. + * OPTEE_SMC_RETURN_EBADCMD Bad/unknown cmd in struct opteem_arg + * OPTEE_SMC_RETURN_IS_RPC() Call suspended by RPC call to normal + * world. + */ +#define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEEM_FUNCID_CALL_WITH_ARG +#define OPTEE_SMC_CALL_WITH_ARG \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG) +/* Same as OPTEE_SMC_CALL_WITH_ARG but a "fast call". */ +#define OPTEE_SMC_FASTCALL_WITH_ARG \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG) + +/* + * Register a secure/non-secure shared memory region + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC*_REGISTER_SHM + * a1 Upper 32bits of 64bit physical address of start of SHM + * a2 Lower 32bits of 64bit physical address of start of SHM + * a3 Size of SHM + * a4 Cache settings of memory, as defined by the + * OPTEE_SMC_SHM_* values above + * a5-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK if OK + * OPTEE_SMC_RETURN_EBUSY can't obtain access to register SHM + * OPTEE_SMC_RETURN_ENOMEM not enough memory to register SHM + * OPTEE_SMC_RETURN_EBADADDR bad parameters + * OPTEE_SMC_RETURN_EBADCMD call not available + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_FUNCID_REGISTER_SHM 5 +#define OPTEE_SMC_REGISTER_SHM \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_REGISTER_SHM) + +/* + * Unregister a secure/non-secure shared memory region + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC*_*UNREGISTER_SHM + * a1 Upper 32bits of 64bit physical address of start of SHM + * a2 Lower 32bits of 64bit physical address of start of SHM + * a3 Size of SHM + * a3-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a00 OPTEE_SMC_RETURN_OK if OK + * OPTEE_SMC_RETURN_EBUSY can't obtain access to register SHM + * OPTEE_SMC_RETURN_ENOMEM not enough memory to register SHM + * OPTEE_SMC_RETURN_EBADCMD call not available + * a1-3 Not used + * a4-7 Preserved + */ +#define OPTEE_SMC_FUNCID_UNREGISTER_SHM 6 +#define OPTEE_SMC_UNREGISTER_SHM \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_UNREGISTER_SHM) + +/* + * Get Shared Memory Config + * + * Returns the Secure/Non-secure shared memory config. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_GET_SHM_CONFIG + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Have config return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Physical address of start of SHM + * a2 Size of of SHM + * a3 Cache settings of memory, as defined by the + * OPTEE_SMC_SHM_* values above + * a4-7 Preserved + * + * Not available register usage: + * a0 OPTEE_SMC_RETURN_NOTAVAIL + * a1-3 Not used + * a4-7 Preserved + */ +#define OPTEE_SMC_FUNCID_GET_SHM_CONFIG 7 +#define OPTEE_SMC_GET_SHM_CONFIG \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG) + +/* + * Configures L2CC mutex + * + * Disables, enables usage of L2CC mutex. Returns or sets physical address + * of L2CC mutex. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_L2CC_MUTEX + * a1 OPTEE_SMC_L2CC_MUTEX_GET_ADDR Get physical address of mutex + * OPTEE_SMC_L2CC_MUTEX_SET_ADDR Set physical address of mutex + * OPTEE_SMC_L2CC_MUTEX_ENABLE Enable usage of mutex + * OPTEE_SMC_L2CC_MUTEX_DISABLE Disable usage of mutex + * a2 if a1 == OPTEE_SMC_L2CC_MUTEX_SET_ADDR, physical address of mutex + * a3-6 Not used + * a7 Hypervisor Client ID register + * + * Have config return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Preserved + * a2 if a1 == OPTEE_SMC_L2CC_MUTEX_GET_ADDR, physical address of L2CC mutex + * a3-7 Preserved + * + * Error return register usage: + * a0 OPTEE_SMC_RETURN_NOTAVAIL Physical address not available + * OPTEE_SMC_RETURN_EBADADDR Bad supplied physical address + * OPTEE_SMC_RETURN_EBADCMD Unsupported value in a1 + * a1-7 Preserved + */ +#define OPTEE_SMC_L2CC_MUTEX_GET_ADDR 0 +#define OPTEE_SMC_L2CC_MUTEX_SET_ADDR 1 +#define OPTEE_SMC_L2CC_MUTEX_ENABLE 2 +#define OPTEE_SMC_L2CC_MUTEX_DISABLE 3 +#define OPTEE_SMC_FUNCID_L2CC_MUTEX 8 +#define OPTEE_SMC_L2CC_MUTEX \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_L2CC_MUTEX) + +/* + * Resume from RPC (for example after processing an IRQ) + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC + * a1-3 Value of a1-3 when OPTEE_SMC_CALL_WITH_ARG returned + * OPTEE_SMC_RETURN_RPC in a0 + * + * Return register usage is the same as for OPTEE_SMC_*CALL_WITH_ARG above. + * + * Possible return values + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * OPTEE_SMC_RETURN_OK Original call completed, result + * updated in the previously supplied. + * struct opteem_arg + * OPTEE_SMC_RETURN_RPC Call suspended by RPC call to normal + * world. + * OPTEE_SMC_RETURN_EBUSY Trusted OS busy, try again later. + * OPTEE_SMC_RETURN_ERESUME Resume failed, the opaque resume + * information was corrupt. + */ +#define OPTEE_SMC_FUNCID_RETURN_FROM_RPC 3 +#define OPTEE_SMC_CALL_RETURN_FROM_RPC \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC) + +#define OPTEE_SMC_RETURN_RPC_PREFIX_MASK 0xFFFF0000 +#define OPTEE_SMC_RETURN_RPC_PREFIX 0xFFFF0000 +#define OPTEE_SMC_RETURN_RPC_FUNC_MASK 0x0000FFFF + +#define OPTEE_SMC_RETURN_GET_RPC_FUNC(ret) \ + ((ret) & OPTEE_SMC_RETURN_RPC_FUNC_MASK) + +#define OPTEE_SMC_RPC_VAL(func) ((func) | OPTEE_SMC_RETURN_RPC_PREFIX) + +/* + * Allocate argument memory for RPC parameter passing. + * Argument memory is used to hold a struct opteem_arg. + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_ALLOC_ARG + * a1 Size in bytes of required argument memory + * a2 Not used + * a3 Resume information, must be preserved + * a4-5 Not used + * a6-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1 Upper 32bits of 64bit physical pointer to allocated argument + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated. + * a2 Lower 32bits of 64bit physical pointer to allocated argument + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated + * a3 Preserved + * a4 Upper 32bits of 64bit Shared memory cookie used when freeing + * the memory or doing an RPC + * a5 Lower 32bits of 64bit Shared memory cookie used when freeing + * the memory or doing an RPC + * a6-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_ALLOC_ARG 0 +#define OPTEE_SMC_RETURN_RPC_ALLOC_ARG \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC_ARG) + +/* + * Allocate payload memory for RPC parameter passing. + * Payload memory is used to hold the memory referred to by struct + * opteem_param_memref. + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_ALLOC_PAYLOAD + * a1 Size in bytes of required argument memory + * a2 Not used + * a3 Resume information, must be preserved + * a4-5 Not used + * a6-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1 Upper 32bits of 64bit physical pointer to allocated argument + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated + * a2 Lower 32bits of 64bit physical pointer to allocated argument + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated + * a3 Preserved + * a4 Upper 32bits of 64bit Shared memory cookie used when freeing + * the memory + * a5 Lower 32bits of 64bit Shared memory cookie used when freeing + * the memory + * a6-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_ALLOC_PAYLOAD 1 +#define OPTEE_SMC_RETURN_RPC_ALLOC_PAYLOAD \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC_PAYLOAD) + +/* + * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC_ARG. + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_FREE_ARG + * a1 Upper 32bits of 64bit shared memory cookie belonging to this + * argument memory + * a2 Lower 32bits of 64bit shared memory cookie belonging to this + * argument memory + * a3-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_FREE_ARG 2 +#define OPTEE_SMC_RETURN_RPC_FREE_ARG \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE_ARG) + +/* + * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC_PAYLOAD. + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_FREE_PAYLOAD + * a1 Upper 32bit of 64bit shared memory cookie belonging to this + * payload memory + * a2 Lower 32bit of 64bit shared memory cookie belonging to this + * payload memory + * a3-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_FREE_PAYLOAD 3 +#define OPTEE_SMC_RETURN_RPC_FREE_PAYLOAD \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE_PAYLOAD) + +/* + * Deliver an IRQ in normal world. + * + * "Call" register usage: + * a0 OPTEE_SMC_RETURN_RPC_IRQ + * a1-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_IRQ 4 +#define OPTEE_SMC_RETURN_RPC_IRQ \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_IRQ) + +/* + * Do an RPC request. The supplied struct opteem_arg tells which + * request to do and the parameters for the request. The following fields + * are used (the rest are unused): + * - cmd the Request ID + * - ret return value of the request, filled in by normal world + * - num_params number of parameters for the request + * - params the parameters + * - param_attrs attributes of the parameters + * + * "Call" register usage: + * a0 OPTEE_SMC_RETURN_RPC_CMD + * a1 Upper 32bit of a 64bit Shared memory cookie holding a + * struct opteem_arg, must be preserved, only the data should + * be updated + * a2 Lower 32bit of a 64bit Shared memory cookie holding a + * struct opteem_arg, must be preserved, only the data should + * be updated + * a3-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_CMD 5 +#define OPTEE_SMC_RETURN_RPC_CMD \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD) + +/* Returned in a0 */ +#define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF + +/* Returned in a0 only from Trusted OS functions */ +#define OPTEE_SMC_RETURN_OK 0x0 +#define OPTEE_SMC_RETURN_EBUSY 0x1 +#define OPTEE_SMC_RETURN_ERESUME 0x2 +#define OPTEE_SMC_RETURN_EBADADDR 0x3 +#define OPTEE_SMC_RETURN_EBADCMD 0x4 +#define OPTEE_SMC_RETURN_ENOMEM 0x5 +#define OPTEE_SMC_RETURN_NOTAVAIL 0x6 +#define OPTEE_SMC_RETURN_IS_RPC(ret) \ + (((ret) != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION) && \ + ((((ret) & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) == \ + OPTEE_SMC_RETURN_RPC_PREFIX))) + +#endif /* OPTEE_SMC_H */ diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c new file mode 100644 index 0000000..640af3d --- /dev/null +++ b/drivers/tee/optee/rpc.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/tee_drv.h> +#include "optee_private.h" +#include "optee_smc.h" + +struct optee_mutex_wait_entry { + struct list_head link; + struct completion comp; + struct mutex mu; + u32 wait_after; + u32 key; +}; + +/* + * Compares two serial numbers using Serial Number Arithmetic + * (https://www.ietf.org/rfc/rfc1982.txt). + */ +#define TICK_GT(t1, t2) \ + (((t1) < (t2) && (t2) - (t1) > 0xFFFFFFFFu) || \ + ((t1) > (t2) && (t1) - (t2) < 0xFFFFFFFFu)) + +static struct optee_mutex_wait_entry *muw_find_entry( + struct optee_mutex_wait *muw, u32 key) +{ + struct optee_mutex_wait_entry *w; + + mutex_lock(&muw->mu); + + list_for_each_entry(w, &muw->db, link) + if (w->key == key) + goto out; + + w = kmalloc(sizeof(struct optee_mutex_wait), GFP_KERNEL); + if (!w) + goto out; + + init_completion(&w->comp); + mutex_init(&w->mu); + w->wait_after = 0; + w->key = key; + list_add_tail(&w->link, &muw->db); +out: + mutex_unlock(&muw->mu); + return w; +} + +static void muw_delete_entry(struct optee_mutex_wait_entry *w) +{ + list_del(&w->link); + mutex_destroy(&w->mu); + kfree(w); +} + +static void muw_delete(struct optee_mutex_wait *muw, u32 key) +{ + struct optee_mutex_wait_entry *w; + + mutex_lock(&muw->mu); + + list_for_each_entry(w, &muw->db, link) { + if (w->key == key) { + muw_delete_entry(w); + break; + } + } + + mutex_unlock(&muw->mu); +} + +static void muw_wakeup(struct optee_mutex_wait *muw, u32 key, + u32 wait_after) +{ + struct optee_mutex_wait_entry *w = muw_find_entry(muw, key); + + if (!w) + return; + + mutex_lock(&w->mu); + w->wait_after = wait_after; + mutex_unlock(&w->mu); + complete(&w->comp); +} + +static void muw_sleep(struct optee_mutex_wait *muw, u32 key, u32 wait_tick) +{ + struct optee_mutex_wait_entry *w = muw_find_entry(muw, key); + u32 wait_after; + + if (!w) + return; + + mutex_lock(&w->mu); + wait_after = w->wait_after; + mutex_unlock(&w->mu); + + /* + * Only wait if the wait_tick is larger than wait_after, that is + * the mutex_wait hasn't been updated while this function was about + * to be called. + */ + if (TICK_GT(wait_tick, wait_after)) + wait_for_completion_timeout(&w->comp, HZ); +} + +void optee_mutex_wait_init(struct optee_mutex_wait *muw) +{ + mutex_init(&muw->mu); + INIT_LIST_HEAD(&muw->db); +} + +void optee_mutex_wait_uninit(struct optee_mutex_wait *muw) +{ + /* + * It's the callers responsibility to ensure that no one is using + * anything inside muw. + */ + + mutex_destroy(&muw->mu); + while (!list_empty(&muw->db)) { + struct optee_mutex_wait_entry *w; + + w = list_first_entry(&muw->db, struct optee_mutex_wait_entry, + link); + muw_delete_entry(w); + } +} + +static void handle_rpc_func_cmd_mutex_wait(struct optee *optee, + struct opteem_arg *arg) +{ + struct opteem_param *params; + + if (arg->num_params != OPTEEM_RPC_NUM_PARAMS) + goto bad; + + params = OPTEEM_GET_PARAMS(arg); + + if ((params[0].attr & OPTEEM_ATTR_TYPE_MASK) != + OPTEEM_ATTR_TYPE_VALUE_INPUT) + goto bad; + if (params[1].attr != OPTEEM_ATTR_TYPE_NONE) + goto bad; + + switch (arg->func) { + case OPTEEM_RPC_SLEEP_MUTEX_WAIT: + muw_sleep(&optee->mutex_wait, params[0].u.value.a, + params[0].u.value.b); + break; + case OPTEEM_RPC_SLEEP_MUTEX_WAKEUP: + muw_wakeup(&optee->mutex_wait, params[0].u.value.a, + params[0].u.value.b); + break; + case OPTEEM_RPC_SLEEP_MUTEX_DELETE: + muw_delete(&optee->mutex_wait, params[0].u.value.a); + break; + default: + goto bad; + } + + arg->ret = TEEC_SUCCESS; + return; +bad: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_rpc_func_cmd_wait(struct opteem_arg *arg) +{ + struct opteem_param *params; + u32 msec_to_wait; + + if (arg->num_params != OPTEEM_RPC_NUM_PARAMS) + goto bad; + + params = OPTEEM_GET_PARAMS(arg); + if (params[0].attr != OPTEEM_ATTR_TYPE_VALUE_INPUT) + goto bad; + + msec_to_wait = params[0].u.value.a; + + /* set task's state to interruptible sleep */ + set_current_state(TASK_INTERRUPTIBLE); + + /* take a nap */ + schedule_timeout(msecs_to_jiffies(msec_to_wait)); + + arg->ret = TEEC_SUCCESS; + return; +bad: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, + struct tee_shm *shm) +{ + struct opteem_arg *arg; + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg)) { + dev_err(optee->dev, "%s: tee_shm_get_va %p failed\n", + __func__, shm); + return; + } + + switch (arg->cmd) { + case OPTEEM_RPC_CMD_SLEEP_MUTEX: + handle_rpc_func_cmd_mutex_wait(optee, arg); + break; + case OPTEEM_RPC_CMD_SUSPEND: + handle_rpc_func_cmd_wait(arg); + break; + default: + optee_supp_thrd_req(ctx, arg); + } +} + +u32 optee_handle_rpc(struct tee_context *ctx, struct optee_smc_param *param) +{ + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct tee_shm *shm; + phys_addr_t pa; + + switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { + case OPTEE_SMC_RPC_FUNC_ALLOC_ARG: + shm = tee_shm_alloc(teedev, param->a1, TEE_SHM_MAPPED); + if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { + reg_pair_from_64(¶m->a1, ¶m->a2, pa); + reg_pair_from_64(¶m->a4, ¶m->a5, (u_long)shm); + } else { + param->a1 = 0; + param->a2 = 0; + param->a4 = 0; + param->a5 = 0; + } + break; + case OPTEE_SMC_RPC_FUNC_ALLOC_PAYLOAD: + shm = tee_shm_alloc(teedev, param->a1, + TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); + if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { + reg_pair_from_64(¶m->a1, ¶m->a2, pa); + reg_pair_from_64(¶m->a4, ¶m->a5, (u_long)shm); + } else { + param->a1 = 0; + param->a2 = 0; + param->a4 = 0; + param->a5 = 0; + } + break; + case OPTEE_SMC_RPC_FUNC_FREE_ARG: + case OPTEE_SMC_RPC_FUNC_FREE_PAYLOAD: + shm = reg_pair_to_ptr(param->a1, param->a2); + tee_shm_free(shm); + break; + case OPTEE_SMC_RPC_FUNC_IRQ: + break; + case OPTEE_SMC_RPC_FUNC_CMD: + shm = reg_pair_to_ptr(param->a1, param->a2); + handle_rpc_func_cmd(ctx, optee, shm); + break; + default: + dev_warn(optee->dev, "Unknown RPC func 0x%x\n", + (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); + break; + } + + return OPTEE_SMC_CALL_RETURN_FROM_RPC; +} diff --git a/drivers/tee/optee/smc_a32.S b/drivers/tee/optee/smc_a32.S new file mode 100644 index 0000000..30fe0e5 --- /dev/null +++ b/drivers/tee/optee/smc_a32.S @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/linkage.h> + + .text + .balign 4 + .code 32 + + /* void optee_smc(struct optee_smc_param *param); */ + .globl optee_smc +ENTRY(optee_smc) + push {r4-r8, lr} + mov r8, r0 + ldm r8, {r0-r7} +.arch_extension sec + smc #0 + stm r8, {r0-r7} + pop {r4-r8, pc} +ENDPROC(optee_smc) diff --git a/drivers/tee/optee/smc_a64.S b/drivers/tee/optee/smc_a64.S new file mode 100644 index 0000000..458a138 --- /dev/null +++ b/drivers/tee/optee/smc_a64.S @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/linkage.h> + + .text + +#define SMC_PARAM_W0_OFFS 0 +#define SMC_PARAM_W2_OFFS 8 +#define SMC_PARAM_W4_OFFS 16 +#define SMC_PARAM_W6_OFFS 24 + + /* void optee_smc(struct smc_param *param); */ + .globl optee_smc +ENTRY(optee_smc) + stp x28, x30, [sp, #-16]! + mov x28, x0 + ldp w0, w1, [x28, #SMC_PARAM_W0_OFFS] + ldp w2, w3, [x28, #SMC_PARAM_W2_OFFS] + ldp w4, w5, [x28, #SMC_PARAM_W4_OFFS] + ldp w6, w7, [x28, #SMC_PARAM_W6_OFFS] + smc #0 + stp w0, w1, [x28, #SMC_PARAM_W0_OFFS] + stp w2, w3, [x28, #SMC_PARAM_W2_OFFS] + ldp x28, x30, [sp], #16 + ret +ENDPROC(optee_smc) diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c new file mode 100644 index 0000000..97c0a9a --- /dev/null +++ b/drivers/tee/optee/supp.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include "optee_private.h" + +void optee_supp_init(struct optee_supp *supp) +{ + memset(supp, 0, sizeof(*supp)); + mutex_init(&supp->thrd_mutex); + mutex_init(&supp->supp_mutex); + sema_init(&supp->data_to_supp_sem, 0); + sema_init(&supp->data_from_supp_sem, 0); +} + +void optee_supp_uninit(struct optee_supp *supp) +{ + mutex_destroy(&supp->thrd_mutex); + mutex_destroy(&supp->supp_mutex); +} + +static void optee_supp_send(struct optee *optee, + const struct opteem_arg *arg, + struct opteem_arg *resp) +{ + struct optee_supp *supp = &optee->supp; + + /* + * Other threads blocks here until we've copied our answer from + * supplicant. + */ + mutex_lock(&supp->thrd_mutex); + + /* + * We have exclusive access to data_to_supp and data_from_supp + * since the supplicant is at this point either trying to down() + * data_to_supp_sem or still in userspace about to do the ioctl() + * to enter optee_supp_read() below. + */ + + supp->data_to_supp = arg; + supp->data_from_supp = resp; + + /* Let supplicant get the data */ + up(&supp->data_to_supp_sem); + + /* + * Wait for supplicant to process and return result, once we've + * down()'ed data_from_supp_sem we have exclusive access again. + */ + down(&supp->data_from_supp_sem); + + /* We're done, let someone else talk to the supplicant now. */ + mutex_unlock(&supp->thrd_mutex); +} + +static void copy_back_outdata(struct opteem_arg *arg, + const struct opteem_arg *resp) +{ + struct opteem_param *arg_params = OPTEEM_GET_PARAMS(arg); + struct opteem_param *resp_params = OPTEEM_GET_PARAMS(resp); + size_t n; + + /* Copy back out and inout parameters */ + for (n = 0; n < arg->num_params; n++) { + struct opteem_param *ap = arg_params + n; + + if (optee_param_is(ap, PARAM_VALUE | PARAM_OUT)) + ap->u.value = resp_params[n].u.value; + else if (optee_param_is(ap, PARAM_MEMREF | PARAM_OUT)) + ap->u.memref.size = resp_params[n].u.memref.size; + } + arg->ret = resp->ret; + +} + +void optee_supp_thrd_req(struct tee_context *ctx, struct opteem_arg *arg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS); + struct opteem_arg *resp; + + if (arg->num_params != OPTEEM_RPC_NUM_PARAMS) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + resp = kzalloc(s, GFP_KERNEL); + if (!resp) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + return; + } + + optee_supp_send(optee, arg, resp); + copy_back_outdata(arg, resp); + + kfree(resp); +} + +static u32 memref_to_user(struct tee_shm *shm, + struct opteem_param_memref *ph_mem, + struct opteem_param_memref *user_mem, int *fd) +{ + int res; + phys_addr_t pa; + + if (!shm) { + *fd = -1; + return TEEC_SUCCESS; + } + + res = tee_shm_get_pa(shm, 0, &pa); + if (res) + return TEEC_ERROR_BAD_PARAMETERS; + + if (pa > ph_mem->buf_ptr) + return TEEC_ERROR_BAD_PARAMETERS; + + user_mem->buf_ptr = ph_mem->buf_ptr - pa; + res = tee_shm_get_fd(shm); + if (res < 0) + return TEEC_ERROR_OUT_OF_MEMORY; + + *fd = res; + return TEEC_SUCCESS; +} + +static int optee_supp_copy_to_user(void __user *buf, + const struct opteem_arg *arg, struct opteem_arg *tmp) +{ + size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS); + struct opteem_param *arg_params; + struct opteem_param *tmp_params; + size_t n; + int ret; + + memcpy(tmp, arg, s); + arg_params = OPTEEM_GET_PARAMS(arg); + tmp_params = OPTEEM_GET_PARAMS(tmp); + + for (n = 0; n < arg->num_params; n++) { + if (optee_param_is(arg_params + n, PARAM_MEMREF | PARAM_INOUT)) + tmp_params[n].u.memref.shm_ref = -1; + } + + for (n = 0; n < arg->num_params; n++) { + if (optee_param_is(arg_params + n, + PARAM_MEMREF | PARAM_INOUT)) { + int fd = -1; + struct tee_shm *shm; + uint32_t res; + struct opteem_param_memref *memref; + + memref = &arg_params[n].u.memref; + shm = (struct tee_shm *)(uintptr_t)memref->shm_ref; + res = memref_to_user(shm, memref, + &tmp_params[n].u.memref, &fd); + /* Propagate kind of error to requesting thread. */ + if (res != TEEC_SUCCESS) { + tmp->ret = res; + if (res == TEEC_ERROR_OUT_OF_MEMORY) { + /* + * For out of memory it could help + * if tee-supplicant was restarted, + * maybe it leaks something. + */ + ret = -ENOMEM; + goto err; + } + /* Let supplicant grab next request. */ + ret = -EAGAIN; + } + tmp_params[n].u.memref.shm_ref = fd; + } + } + + if (copy_to_user(buf, tmp, s)) { + /* Something is wrong, let supplicant restart and try again */ + ret = -EINVAL; + goto err; + } + return 0; +err: + for (n = 0; n < arg->num_params; n++) { + int fd; + + if (!optee_param_is(arg_params + n, PARAM_MEMREF | PARAM_INOUT)) + continue; + fd = tmp_params[n].u.memref.shm_ref; + if (fd >= 0) + tee_shm_put_fd(fd); + } + return ret; +} + +int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len) +{ + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct optee_supp *supp = &optee->supp; + const size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS); + int ret; + + if (len != s) + return -EINVAL; + + /* + * In case two supplicants or two threads in one supplicant is + * calling this function simultaneously we need to protect the + * data with a mutex which we'll release before returning. + */ + mutex_lock(&supp->supp_mutex); + while (true) { + if (supp->supp_next_write) { + /* + * optee_supp_read() has been called again without + * a optee_supp_write() in between. Supplicant has + * probably been restarted before it was able to + * write back last result. Abort last request and + * wait for a new. + */ + if (supp->data_to_supp) { + memcpy(supp->data_from_supp, + supp->data_to_supp, s); + supp->data_from_supp->ret = + TEEC_ERROR_COMMUNICATION; + supp->data_to_supp = NULL; + supp->supp_next_write = false; + up(&supp->data_from_supp_sem); + } + } + + /* + * This is where supplicant will be hanging most of the + * time, let's make this interruptable so we can easily + * restart supplicant if needed. + */ + if (down_interruptible(&supp->data_to_supp_sem)) { + ret = -ERESTARTSYS; + goto out; + } + + /* We have exlusive access to the data */ + ret = optee_supp_copy_to_user(buf, supp->data_to_supp, + supp->data_from_supp); + if (!ret) + break; + supp->data_to_supp = NULL; + up(&supp->data_from_supp_sem); + if (ret != -EAGAIN) + goto out; + } + + /* We've consumed the data, set it to NULL */ + supp->data_to_supp = NULL; + + /* Allow optee_supp_write() below to do its work */ + supp->supp_next_write = true; + + ret = 0; +out: + mutex_unlock(&supp->supp_mutex); + return ret; +} + +int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len) +{ + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct optee_supp *supp = &optee->supp; + const size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS); + int ret = 0; + + if (len != s) + return -EINVAL; + + /* + * We still have exclusive access to the data since that's how we + * left it when returning from optee_supp_read(). + */ + + /* See comment on mutex in optee_supp_read() above */ + mutex_lock(&supp->supp_mutex); + + if (!supp->supp_next_write) { + /* + * Something strange is going on, supplicant shouldn't + * enter optee_supp_write() in this state + */ + ret = -ENOENT; + goto out; + } + + if (copy_from_user(supp->data_from_supp, buf, s)) { + /* + * Something is wrong, let supplicant restart. Next call to + * optee_supp_read() will give an error to the requesting + * thread and release it. + */ + ret = -EINVAL; + goto out; + } + + /* Data has been populated, set the pointer to NULL */ + supp->data_from_supp = NULL; + + /* Allow optee_supp_read() above to do its work */ + supp->supp_next_write = false; + + /* Let the requesting thread continue */ + up(&supp->data_from_supp_sem); +out: + mutex_unlock(&supp->supp_mutex); + return ret; +} diff --git a/include/uapi/linux/optee_msg.h b/include/uapi/linux/optee_msg.h new file mode 100644 index 0000000..338d862 --- /dev/null +++ b/include/uapi/linux/optee_msg.h @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef OPTEE_MSG_H +#define OPTEE_MSG_H + +#include <linux/types.h> + +/* + * This file is exported by OP-TEE and is kept in sync between secure + * world, normal world kernel driver, and user space client lib. + * + * This file is divided into three sections. + * 1. Formatting of messages. + * 2. Requests from normal world + * 3. Requests from secure world, Remote Procedure Call (RPC) + */ + +/***************************************************************************** + * Part 1 - formatting of messages + *****************************************************************************/ + +/* + * Same values as TEE_PARAM_* from TEE Internal API + */ +#define OPTEEM_ATTR_TYPE_NONE 0 +#define OPTEEM_ATTR_TYPE_VALUE_INPUT 1 +#define OPTEEM_ATTR_TYPE_VALUE_OUTPUT 2 +#define OPTEEM_ATTR_TYPE_VALUE_INOUT 3 +#define OPTEEM_ATTR_TYPE_MEMREF_INPUT 5 +#define OPTEEM_ATTR_TYPE_MEMREF_OUTPUT 6 +#define OPTEEM_ATTR_TYPE_MEMREF_INOUT 7 + +#define OPTEEM_ATTR_TYPE_MASK 0x7 + +/* + * Meta parameter to be absorbed by the Secure OS and not passed + * to the Trusted Application. + * + * Currently only used for struct opteem_meta_open_session which + * is added to OPTEEM_CMD_OPEN_SESSION. + */ +#define OPTEEM_ATTR_META 0x8 + + +/** + * struct opteem_param_memref - memory reference + * @buf_ptr: Address of the buffer + * @size: Size of the buffer + * @shm_ref: Shared memory reference only used by normal world + * + * Secure and normal world communicates pointers as physical address + * instead of the virtual address. This is because secure and normal world + * have completely independent memory mapping. Normal world can even have a + * hypervisor which need to translate the guest physical address (AKA IPA + * in ARM documentation) to a real physical address before passing the + * structure to secure world. + */ +struct opteem_param_memref { + __u64 buf_ptr; + __u64 size; + __u64 shm_ref; +}; + +/** + * struct opteem_param_value - values + * @a: first value + * @b: second value + * @c: third value + */ +struct opteem_param_value { + __u64 a; + __u64 b; + __u64 c; +}; + +/** + * struct opteem_param - parameter + * @attr: attributes + * @memref: a memory reference + * @value: a value + * + * @attr & OPTEEM_ATTR_TYPE_MASK indicates if memref or value is used in + * the union. OPTEEM_ATTR_TYPE_VALUE_* indicates value and + * OPTEEM_ATTR_TYPE_MEMREF_* indicates memref. OPTEEM_ATTR_TYPE_NONE + * indicates that none of the members are used. + */ +struct opteem_param { + __u64 attr; + union { + struct opteem_param_memref memref; + struct opteem_param_value value; + } u; +}; + +/** + * struct opteem_arg - call argument + * @cmd: Command, one of OPTEEM_CMD_* or OPTEEM_RPC_CMD_* + * @func: Trusted Application function, specific to the Trusted Application, + * used if cmd == OPTEEM_CMD_INVOKE_COMMAND + * @session: In parameter for all OPTEEM_CMD_* except + * OPTEEM_CMD_OPEN_SESSION where it's an output parameter instead + * @ret: return value + * @ret_origin: origin of the return value + * @num_params: number of parameters supplied to the OS Command + * @params: the parameters supplied to the OS Command + * + * All normal calls to Trusted OS uses this struct. If cmd requires further + * information than what these field holds it can be passed as a parameter + * tagged as meta (setting the OPTEEM_ATTR_META bit in corresponding + * param_attrs). All parameters tagged as meta has to come first. + */ +struct opteem_arg { + __u32 cmd; + __u32 func; + __u32 session; + __u32 ret; + __u32 ret_origin; + __u32 num_params __aligned(8); + + /* + * num_params is 8 byte aligned since the 'struct opteem_param' + * which follows requires 8 byte alignment. + * + * Commented out element used to visualize the layout dynamic part + * of the struct. This field is not available at all if + * num_params == 0. + * + * params is accessed through the macro OPTEEM_GET_PARAMS + * + * struct opteem_param params[num_params]; + */ +}; + +/** + * OPTEEM_GET_PARAMS - return pointer to struct opteem_param * + * + * @x: Pointer to a struct opteem_arg + * + * Returns a pointer to the params[] inside a struct opteem_arg. + */ +#define OPTEEM_GET_PARAMS(x) \ + (struct opteem_param *)(((struct opteem_arg *)(x)) + 1) + +/** + * OPTEEM_GET_ARG_SIZE - return size of struct opteem_arg + * + * @num_params: Number of parameters embedded in the struct opteem_arg + * + * Returns the size of the struct opteem_arg together with the number + * of embedded parameters. + */ +#define OPTEEM_GET_ARG_SIZE(num_params) \ + (sizeof(struct opteem_arg) + \ + sizeof(struct opteem_param) * (num_params)) + +/* Length in bytes of a UUID */ +#define OPTEEM_UUID_LEN 16 + +/** + * struct opteem_meta_open_session - additional parameters for + * OPTEEM_CMD_OPEN_SESSION + * @uuid: UUID of the Trusted Application + * @clnt_uuid: UUID of client + * @clnt_login: Login class of client, TEE_LOGIN_* if being Global Platform + * compliant + * + * This struct is passed in the first parameter as an input memref tagged + * as meta on an OPTEEM_CMD_OPEN_SESSION cmd. + */ +struct opteem_meta_open_session { + __u8 uuid[OPTEEM_UUID_LEN]; + __u8 clnt_uuid[OPTEEM_UUID_LEN]; + __u32 clnt_login; +}; + +/** + * struct optee_cmd_prefix - initial header for all user space buffers + * @func_id: Function Id OPTEEM_FUNCID_* below + * @pad: padding to make the struct size a multiple of 8 bytes + * + * This struct is 8 byte aligned since it's always followed by a struct + * opteem_arg which requires 8 byte alignment. + */ +struct opteem_cmd_prefix { + __u32 func_id; + __u32 pad __aligned(8); +}; + +/***************************************************************************** + * Part 2 - requests from normal world + *****************************************************************************/ + +/* + * Return the following UID if using API specified in this file without + * further extentions: + * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b. + * Represented in 4 32-bit words in OPTEEM_UID_0, OPTEEM_UID_1, + * OPTEEM_UID_2, OPTEEM_UID_3. + */ +#define OPTEEM_UID_0 0x384fb3e0 +#define OPTEEM_UID_1 0xe7f811e3 +#define OPTEEM_UID_2 0xaf630002 +#define OPTEEM_UID_3 0xa5d5c51b +#define OPTEEM_FUNCID_CALLS_UID 0xFF01 + +/* + * Returns 2.0 if using API specified in this file without further extentions. + * Represented in 2 32-bit words in OPTEEM_REVISION_MAJOR and + * OPTEEM_REVISION_MINOR + */ +#define OPTEEM_REVISION_MAJOR 2 +#define OPTEEM_REVISION_MINOR 0 +#define OPTEEM_FUNCID_CALLS_REVISION 0xFF03 + +/* + * Get UUID of Trusted OS. + * + * Used by non-secure world to figure out which Trusted OS is installed. + * Note that returned UUID is the UUID of the Trusted OS, not of the API. + * + * Returns UUID in 4 32-bit words in the same way as OPTEEM_FUNCID_CALLS_UID + * described above. + */ +#define OPTEEM_OS_OPTEE_UUID_0 0x486178e0 +#define OPTEEM_OS_OPTEE_UUID_1 0xe7f811e3 +#define OPTEEM_OS_OPTEE_UUID_2 0xbc5e0002 +#define OPTEEM_OS_OPTEE_UUID_3 0xa5d5c51b +#define OPTEEM_FUNCID_GET_OS_UUID 0x0000 + +/* + * Get revision of Trusted OS. + * + * Used by non-secure world to figure out which version of the Trusted OS + * is installed. Note that the returned revision is the revision of the + * Trusted OS, not of the API. + * + * Returns revision in 2 32-bit words in the same way as OPTEEM_CALLS_REVISION + * described above. + */ +#define OPTEEM_OS_OPTEE_REVISION_MAJOR 1 +#define OPTEEM_OS_OPTEE_REVISION_MINOR 0 +#define OPTEEM_FUNCID_GET_OS_REVISION 0x0001 + +/* + * Do a secure call with struct opteem_arg as argument + * The OPTEEM_CMD_* below defines what goes in struct opteem_arg::cmd + * + * For OPTEEM_CMD_OPEN_SESSION the first parameter is tagged as meta, holding + * a memref with a struct opteem_meta_open_session which is needed find the + * Trusted Application and to indicate the credentials of the client. + * + * For OPTEEM_CMD_INVOKE_COMMAND struct opteem_arg::func is Trusted + * Application function, specific to the Trusted Application. + */ +#define OPTEEM_CMD_OPEN_SESSION 0 +#define OPTEEM_CMD_INVOKE_COMMAND 1 +#define OPTEEM_CMD_CLOSE_SESSION 2 +#define OPTEEM_CMD_CANCEL 3 +#define OPTEEM_FUNCID_CALL_WITH_ARG 0x0004 + +/* + * Do a write response from tee-supplicant with struct opteem_arg as argument + */ +#define OPTEEM_FUNCID_SUPP_CMD_WRITE 0x1000 + +/* + * Do a read request from tee-supplicant with struct opteem_arg as argument + */ +#define OPTEEM_FUNCID_SUPP_CMD_READ 0x1001 + +/***************************************************************************** + * Part 3 - Requests from secure world, RPC + *****************************************************************************/ + +/* + * All RPC is done with a struct opteem_arg as bearer of information, + * struct opteem_arg::arg holds values defined by OPTEEM_RPC_CMD_* below + */ + +/* + * Number of parameters used in RPC communication, always this number but + * for some commands a parameter may be set to unused. + */ +#define OPTEEM_RPC_NUM_PARAMS 2 + +/* + * Load a TA into memory + * [in] param[0] memref holding a uuid (OPTEEM_UUID_LEN bytes) of the + * TA to load + * [out] param[1] memref allocated to hold the TA content. memref.buf + * may be == NULL to query the size of the TA content. + * memref.size is always updated with the actual size + * of the TA content. If returned memref.size is larger + * than the supplied memref.size, not content is loaded. + * [out] arg.ret return value of request, 0 on success. + */ +#define OPTEEM_RPC_CMD_LOAD_TA 0 + +/* + * Reserved + */ +#define OPTEEM_RPC_CMD_RPMB 1 + +/* + * File system access, defined in tee-supplicant + */ +#define OPTEEM_RPC_CMD_FS 2 + +/* + * Get time, defined in tee-supplicant + */ +#define OPTEEM_RPC_CMD_GET_TIME 3 + +/* + * Sleep mutex, helper for secure world to implement a sleeping mutex. + * struct opteem_arg::func one of OPTEEM_RPC_SLEEP_MUTEX_* below + * + * OPTEEM_RPC_SLEEP_MUTEX_WAIT + * [in] param[0].value .a sleep mutex key + * .b wait tick + * [not used] param[1] + * + * OPTEEM_RPC_SLEEP_MUTEX_WAKEUP + * [in] param[0].value .a sleep mutex key + * .b wait after + * [not used] param[1] + * + * OPTEEM_RPC_SLEEP_MUTEX_DELETE + * [in] param[0].value .a sleep mutex key + * [not used] param[1] + */ +#define OPTEEM_RPC_SLEEP_MUTEX_WAIT 0 +#define OPTEEM_RPC_SLEEP_MUTEX_WAKEUP 1 +#define OPTEEM_RPC_SLEEP_MUTEX_DELETE 2 +#define OPTEEM_RPC_CMD_SLEEP_MUTEX 4 + +/* + * Suspend execution + * + * [in] param[0].value .a number of milliseconds to suspend + */ +#define OPTEEM_RPC_CMD_SUSPEND 5 + +#endif /* OPTEE_MSG_H */
Adds a OP-TEE driver which also can be compiled as a loadable module. * Targets ARM and ARM64 * Supports using reserved memory from OP-TEE as shared memory * CMA as shared memory is optional and only tried if OP-TEE doesn't supply a reserved shared memory region * Probes OP-TEE version using SMCs * Accepts requests on privileged and unprivileged device * Uses OPTEE message protocol version 2 Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org> --- Documentation/devicetree/bindings/optee/optee.txt | 17 + .../devicetree/bindings/vendor-prefixes.txt | 1 + MAINTAINERS | 6 + drivers/tee/Kconfig | 10 + drivers/tee/Makefile | 1 + drivers/tee/optee/Kconfig | 19 + drivers/tee/optee/Makefile | 13 + drivers/tee/optee/call.c | 294 ++++++++++++ drivers/tee/optee/core.c | 509 ++++++++++++++++++++ drivers/tee/optee/optee_private.h | 138 ++++++ drivers/tee/optee/optee_smc.h | 510 +++++++++++++++++++++ drivers/tee/optee/rpc.c | 282 ++++++++++++ drivers/tee/optee/smc_a32.S | 30 ++ drivers/tee/optee/smc_a64.S | 37 ++ drivers/tee/optee/supp.c | 327 +++++++++++++ include/uapi/linux/optee_msg.h | 368 +++++++++++++++ 16 files changed, 2562 insertions(+) create mode 100644 Documentation/devicetree/bindings/optee/optee.txt create mode 100644 drivers/tee/optee/Kconfig create mode 100644 drivers/tee/optee/Makefile create mode 100644 drivers/tee/optee/call.c create mode 100644 drivers/tee/optee/core.c create mode 100644 drivers/tee/optee/optee_private.h create mode 100644 drivers/tee/optee/optee_smc.h create mode 100644 drivers/tee/optee/rpc.c create mode 100644 drivers/tee/optee/smc_a32.S create mode 100644 drivers/tee/optee/smc_a64.S create mode 100644 drivers/tee/optee/supp.c create mode 100644 include/uapi/linux/optee_msg.h