From 9e38023fe6f6c594afc343475c6e8d06959fd89f Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 4 Apr 2018 16:46:23 +0200 Subject: [PATCH 01/37] Add upstream-tee-subsys-patches.txt Adds upstream-tee-subsys-patches.txt describing all upstream patches related to the TEE subsystem. Signed-off-by: Jens Wiklander [jf: rebase on top of v4.18] Signed-off-by: Jerome Forissier --- upstream-tee-subsys-patches.txt | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 upstream-tee-subsys-patches.txt diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt new file mode 100644 index 00000000000000..311de9bb2c26c2 --- /dev/null +++ b/upstream-tee-subsys-patches.txt @@ -0,0 +1,53 @@ +Patches relating to the TEE subsystem +===================================== + +This is a list of all the patches that relates to the TEE subsystem. The +text inside the brackets are the kernel version where it was introduced, +followed by the sha1 hash in the upstream kernel tree. + + +[v4.17] f5d5641b90b3 optee: check shm reference are consistent in offset/size +[v4.17] bb765d1c331f tee: shm: fix use-after-free via temporarily dropped reference +[v4.16] 5c5f80307ab2 tee: optee: report OP-TEE revision information +[v4.16] 6e112de04278 tee: optee: GET_OS_REVISION: document a2 as a build identifier +[v4.16] 7dd003aec201 correct max value for id allocation +[v4.16] ded4c39e93f3 arm/arm64: smccc: Make function identifiers an unsigned quantity +[v4.16] 2490cdf6435b tee: shm: Potential NULL dereference calling tee_shm_register() +[v4.16] c94f31b526fe tee: shm: don't put_page on null shm->pages +[v4.16] 80ec6f5de60b tee: shm: make function __tee_shm_alloc static +[v4.16] cdbcf83d29c1 tee: optee: check type of registered shared memory +[v4.16] 95ffe4ca4387 tee: add start argument to shm_register callback +[v4.16] f681e08f671a tee: optee: fix header dependencies +[v4.16] ef8e08d24ca8 tee: shm: inline tee_shm_get_id() +[v4.16] 217e0250cccb tee: use reference counting for tee_context +[v4.16] f58e236c9d66 tee: optee: enable dynamic SHM support +[v4.16] abd135ba215c tee: optee: add optee-specific shared pool implementation +[v4.16] d885cc5e0759 tee: optee: store OP-TEE capabilities in private data +[v4.16] 53a107c812de tee: optee: add registered buffers handling into RPC calls +[v4.16] 64cf9d8a672e tee: optee: add registered shared parameters handling +[v4.16] 06ca79179c4e tee: optee: add shared buffer registration functions +[v4.16] 3bb48ba5cd60 tee: optee: add page list manipulation functions +[v4.16] de5c6dfc43da tee: optee: Update protocol definitions +[v4.16] e0c69ae8bfb5 tee: shm: add page accessor functions +[v4.16] b25946ad951c tee: shm: add accessors for buffer size and page offset +[v4.16] 033ddf12bcf5 tee: add register user memory +[v4.16] e2aca5d8928a tee: flexible shared memory pool creation +[v4.16] 1647a5ac1754 optee: support asynchronous supplicant requests +[v4.16] f2aa97240c84 tee: add TEE_IOCTL_PARAM_ATTR_META +[v4.16] 84debcc53533 tee: add tee_param_is_memref() for driver use +[v4.15] f044113113dd optee: fix invalid of_node_put() in optee_driver_init() +[v4.14] 39e6519a3f13 tee: optee: sync with new naming of interrupts +[v4.14] 059cf566e123 tee: indicate privileged dev in gen_caps +[v4.14] a9980e947ec9 tee: optee: interruptible RPC sleep +[v4.14] 96e72ddeec45 tee: optee: add const to tee_driver_ops and tee_desc structures +[v4.14] 53e3ca5cee24 tee: tee_shm: Constify dma_buf_ops structures. +[v4.14] 999616b8536c tee: add forward declaration for struct device +[v4.14] efb14036bd7f tee: optee: fix uninitialized symbol 'parg' +[v4.12] e84188852a72 tee: add ARM_SMCCC dependency +[v4.12] 4fb0a5eb364d tee: add OP-TEE driver +[v4.12] 967c9cca2cc5 tee: generic TEE subsystem +[v4.5] 14457459f9ca ARM: 8480/2: arm64: add implementation for arm-smccc +[v4.5] b329f95d70f3 ARM: 8479/2: add implementation for arm-smccc +[v4.5] 98dd64f34f47 ARM: 8478/2: arm/arm64: add arm-smccc + + From 9d5c773e6ff2250f39bff8b311127bf75ff3c39d Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Thu, 27 Oct 2016 23:18:35 +0200 Subject: [PATCH 02/37] tee: new ioctl to a register tee_shm from a dmabuf file descriptor This change allows userland to create a tee_shm object that refers to a dmabuf reference. Userland provides a dmabuf file descriptor as buffer reference. The created tee_shm object exported as a brand new dmabuf reference used to provide a clean fd to userland. Userland shall closed this new fd to release the tee_shm object resources. The initial dmabuf resources are tracked independently through original dmabuf file descriptor. Once the buffer is registered and until it is released, TEE driver keeps a refcount on the registered dmabuf structure. This change only support dmabuf references that relates to physically contiguous memory buffers. New tee_shm flag to identify tee_shm objects built from a registered dmabuf: TEE_SHM_EXT_DMA_BUF. Such tee_shm structures are flagged both TEE_SHM_DMA_BUF and TEE_SHM_EXT_DMA_BUF. Signed-off-by: Etienne Carriere Reviewed-by: Jens Wiklander [jf: squash fixup commit ("tee: fix unbalanced context refcount in register shm from fd")] [jf: rebase onto v5.9-rc8] Signed-off-by: Jerome Forissier --- drivers/tee/tee_core.c | 38 ++++++++++++++ drivers/tee/tee_shm.c | 111 ++++++++++++++++++++++++++++++++++++++- include/linux/tee_drv.h | 10 ++++ include/uapi/linux/tee.h | 29 ++++++++++ 4 files changed, 186 insertions(+), 2 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 64637e09a09536..6492aee9b8451f 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -351,6 +351,42 @@ tee_ioctl_shm_register(struct tee_context *ctx, return ret; } +static int tee_ioctl_shm_register_fd(struct tee_context *ctx, + struct tee_ioctl_shm_register_fd_data __user *udata) +{ + struct tee_ioctl_shm_register_fd_data data; + struct tee_shm *shm; + long ret; + + if (copy_from_user(&data, udata, sizeof(data))) + return -EFAULT; + + /* Currently no input flags are supported */ + if (data.flags) + return -EINVAL; + + shm = tee_shm_register_fd(ctx, data.fd); + if (IS_ERR_OR_NULL(shm)) + return -EINVAL; + + data.id = shm->id; + data.flags = shm->flags; + data.size = shm->size; + + if (copy_to_user(udata, &data, sizeof(data))) + ret = -EFAULT; + else + ret = tee_shm_get_fd(shm); + + /* + * When user space closes the file descriptor the shared memory + * should be freed or if tee_shm_get_fd() failed then it will + * be freed immediately. + */ + tee_shm_put(shm); + return ret; +} + static int params_from_user(struct tee_context *ctx, struct tee_param *params, size_t num_params, struct tee_ioctl_param __user *uparams) @@ -811,6 +847,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return tee_ioctl_shm_alloc(ctx, uarg); case TEE_IOC_SHM_REGISTER: return tee_ioctl_shm_register(ctx, uarg); + case TEE_IOC_SHM_REGISTER_FD: + return tee_ioctl_shm_register_fd(ctx, uarg); case TEE_IOC_OPEN_SESSION: return tee_ioctl_open_session(ctx, uarg); case TEE_IOC_INVOKE: diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 827ac3d0fea96c..7cf16603e26ba6 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -12,6 +12,14 @@ #include #include "tee_private.h" +/* extra references appended to shm object for registered shared memory */ +struct tee_shm_dmabuf_ref { + struct tee_shm shm; + struct dma_buf *dmabuf; + struct dma_buf_attachment *attach; + struct sg_table *sgt; +}; + static void tee_shm_release(struct tee_shm *shm) { struct tee_device *teedev = shm->ctx->teedev; @@ -22,7 +30,15 @@ static void tee_shm_release(struct tee_shm *shm) mutex_unlock(&teedev->mutex); } - if (shm->flags & TEE_SHM_POOL) { + if (shm->flags & TEE_SHM_EXT_DMA_BUF) { + struct tee_shm_dmabuf_ref *ref; + + ref = container_of(shm, struct tee_shm_dmabuf_ref, shm); + dma_buf_unmap_attachment(ref->attach, ref->sgt, + DMA_BIDIRECTIONAL); + dma_buf_detach(shm->dmabuf, ref->attach); + dma_buf_put(ref->dmabuf); + } else if (shm->flags & TEE_SHM_POOL) { struct tee_shm_pool_mgr *poolm; if (shm->flags & TEE_SHM_DMA_BUF) @@ -48,7 +64,6 @@ static void tee_shm_release(struct tee_shm *shm) teedev_ctx_put(shm->ctx); kfree(shm); - tee_device_put(teedev); } @@ -312,6 +327,98 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, } EXPORT_SYMBOL_GPL(tee_shm_register); +struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd) +{ + struct tee_shm_dmabuf_ref *ref; + void *rc; + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + if (!tee_device_get(ctx->teedev)) + return ERR_PTR(-EINVAL); + + teedev_ctx_get(ctx); + + ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) { + rc = ERR_PTR(-ENOMEM); + goto err; + } + + ref->shm.ctx = ctx; + ref->shm.id = -1; + + ref->dmabuf = dma_buf_get(fd); + if (!ref->dmabuf) { + rc = ERR_PTR(-EINVAL); + goto err; + } + + ref->attach = dma_buf_attach(ref->dmabuf, &ctx->teedev->dev); + if (IS_ERR_OR_NULL(ref->attach)) { + rc = ERR_PTR(-EINVAL); + goto err; + } + + ref->sgt = dma_buf_map_attachment(ref->attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(ref->sgt)) { + rc = ERR_PTR(-EINVAL); + goto err; + } + + if (sg_nents(ref->sgt->sgl) != 1) { + rc = ERR_PTR(-EINVAL); + goto err; + } + + ref->shm.paddr = sg_dma_address(ref->sgt->sgl); + ref->shm.size = sg_dma_len(ref->sgt->sgl); + ref->shm.flags = TEE_SHM_DMA_BUF | TEE_SHM_EXT_DMA_BUF; + + mutex_lock(&ctx->teedev->mutex); + ref->shm.id = idr_alloc(&ctx->teedev->idr, &ref->shm, + 1, 0, GFP_KERNEL); + mutex_unlock(&ctx->teedev->mutex); + if (ref->shm.id < 0) { + rc = ERR_PTR(ref->shm.id); + goto err; + } + + /* export a dmabuf to later get a userland ref */ + exp_info.ops = &tee_shm_dma_buf_ops; + exp_info.size = ref->shm.size; + exp_info.flags = O_RDWR; + exp_info.priv = &ref->shm; + + ref->shm.dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(ref->shm.dmabuf)) { + rc = ERR_PTR(-EINVAL); + goto err; + } + + return &ref->shm; + +err: + if (ref) { + if (ref->shm.id >= 0) { + mutex_lock(&ctx->teedev->mutex); + idr_remove(&ctx->teedev->idr, ref->shm.id); + mutex_unlock(&ctx->teedev->mutex); + } + if (ref->sgt) + dma_buf_unmap_attachment(ref->attach, ref->sgt, + DMA_BIDIRECTIONAL); + if (ref->attach) + dma_buf_detach(ref->dmabuf, ref->attach); + if (ref->dmabuf) + dma_buf_put(ref->dmabuf); + } + kfree(ref); + teedev_ctx_put(ctx); + tee_device_put(ctx->teedev); + return rc; +} +EXPORT_SYMBOL_GPL(tee_shm_register_fd); + /** * tee_shm_get_fd() - Increase reference count and return file descriptor * @shm: Shared memory handle diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index d074302989ddd2..f4c6d6e7ced7ab 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -342,6 +342,16 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags); struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, size_t length, u32 flags); +/** + * tee_shm_register_fd() - Register shared memory from file descriptor + * + * @ctx: Context that allocates the shared memory + * @fd: shared memory file descriptor reference. + * + * @returns a pointer to 'struct tee_shm' + */ +struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd); + /** * tee_shm_is_registered() - Check if shared memory object in registered in TEE * @shm: Shared memory handle diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index b619f37ee03e53..b7148b5ebf792f 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -118,6 +118,35 @@ struct tee_ioctl_shm_alloc_data { #define TEE_IOC_SHM_ALLOC _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 1, \ struct tee_ioctl_shm_alloc_data) +/** + * struct tee_ioctl_shm_register_fd_data - Shared memory registering argument + * @fd: [in] file descriptor identifying the shared memory + * @size: [out] Size of shared memory to allocate + * @flags: [in] Flags to/from allocation. + * @id: [out] Identifier of the shared memory + * + * The flags field should currently be zero as input. Updated by the call + * with actual flags as defined by TEE_IOCTL_SHM_* above. + * This structure is used as argument for TEE_IOC_SHM_ALLOC below. + */ +struct tee_ioctl_shm_register_fd_data { + __s64 fd; + __u64 size; + __u32 flags; + __s32 id; +} __aligned(8); + +/** + * TEE_IOC_SHM_REGISTER_FD - register a shared memory from a file descriptor + * + * Returns a file descriptor on success or < 0 on failure + * + * The returned file descriptor refers to the shared memory object in kernel + * land. The shared memory is freed when the descriptor is closed. + */ +#define TEE_IOC_SHM_REGISTER_FD _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 8, \ + struct tee_ioctl_shm_register_fd_data) + /** * struct tee_ioctl_buf_data - Variable sized buffer * @buf_ptr: [in] A __user pointer to a buffer From 595188c94fef5a864ccab8e703f11c4e251ce45c Mon Sep 17 00:00:00 2001 From: Joakim Bech Date: Fri, 1 Apr 2016 14:39:54 +0200 Subject: [PATCH 03/37] arm64: dt: Remove timer from mt8173 **not for mainline** From the commit below, the mt8173-evb failed to boot to console due to changes in the mt8173 device tree files. commit c0d6fe2f01c475cc137d90607a07578586883df8 Merge: b44a3d2 3e4dda7 Author: Linus Torvalds Date: Tue Nov 10 15:06:26 2015 -0800 Merge tag 'armsoc-dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Until properly solved, let's just remove the section in the device tree blob that causes this. Signed-off-by: Joakim Bech Reviewed-by: Pascal Brand --- arch/arm64/boot/dts/mediatek/mt8173.dtsi | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi index 5e046f9d48ce91..13b40caafb2be5 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi @@ -468,15 +468,6 @@ reg = <0 0x10007000 0 0x100>; }; - timer: timer@10008000 { - compatible = "mediatek,mt8173-timer", - "mediatek,mt6577-timer"; - reg = <0 0x10008000 0 0x1000>; - interrupts = ; - clocks = <&infracfg CLK_INFRA_CLK_13M>, - <&topckgen CLK_TOP_RTC_SEL>; - }; - pwrap: pwrap@1000d000 { compatible = "mediatek,mt8173-pwrap"; reg = <0 0x1000d000 0 0x1000>; From 1d5f2c312aa9084913f0ed29bb707de82b039efb Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 17 Aug 2016 14:08:08 +0200 Subject: [PATCH 04/37] arm64: dt: OP-TEE for foundation-v8 **not for mainline** Configures foundation-v8 with OP-TEE. Signed-off-by: Jens Wiklander [jf: rebase onto v5.9-rc7] Signed-off-by: Jerome Forissier --- arch/arm64/boot/dts/arm/foundation-v8.dtsi | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dtsi b/arch/arm64/boot/dts/arm/foundation-v8.dtsi index 05ae893d1b2ee7..597660c50ce4fe 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dtsi +++ b/arch/arm64/boot/dts/arm/foundation-v8.dtsi @@ -22,7 +22,6 @@ aliases { serial0 = &v2m_serial0; - serial1 = &v2m_serial1; serial2 = &v2m_serial2; serial3 = &v2m_serial3; }; @@ -67,6 +66,16 @@ <0x00000008 0x80000000 0 0x80000000>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + optee@0x83000000 { + reg = <0x00000000 0x83000000 0 0x01000000>; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupts = , @@ -197,14 +206,6 @@ clock-names = "uartclk", "apb_pclk"; }; - v2m_serial1: serial@a0000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0a0000 0x1000>; - interrupts = <6>; - clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; - clock-names = "uartclk", "apb_pclk"; - }; - v2m_serial2: serial@b0000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x0b0000 0x1000>; @@ -228,4 +229,12 @@ }; }; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; + }; From ef79f9efaf72c657e8be36bf7f35ff4f64466401 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 29 Mar 2016 11:01:28 +0200 Subject: [PATCH 05/37] arm64: dt: OP-TEE for Juno **not for mainline** Configures Juno with OP-TEE. Reviewed-by: Pascal Brand Signed-off-by: Jens Wiklander [jf: rebase onto v5.9-rc7] Signed-off-by: Jerome Forissier --- arch/arm64/boot/dts/arm/juno-base.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index f6c55877fbd94f..0ff5d784ccb582 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -800,6 +800,17 @@ <0x00000008 0x80000000 0x1 0x80000000>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Shared memory between secure and non-secure world */ + optee@0xfee00000 { + reg = <0x00000000 0xfee00000 0 0x00200000>; + }; + }; + bus@8000000 { compatible = "simple-bus"; #address-cells = <2>; @@ -837,4 +848,11 @@ interrupt-map-mask = <0 0>; interrupt-map = <0 0 &gic 0 GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; From 767ffb63218664b0ffb42e7940b16c137dacf78b Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Fri, 2 Dec 2016 09:45:32 +0100 Subject: [PATCH 06/37] arm64: dt: Add no-map to the reserved-memory node for OP-TEE for foundation-v8 **not for mainline** All the platforms that reserve memory for OP-TEE statically via the DT (i.e., not those that reserve it via UEFI or that patch the DT dynamically thanks to OP-TEE's CFG_DT option) have to mark it 'no-map' so that only the TEE driver may map it. Signed-off-by: Jens Wiklander --- arch/arm64/boot/dts/arm/foundation-v8.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dtsi b/arch/arm64/boot/dts/arm/foundation-v8.dtsi index 597660c50ce4fe..acbf17520b3043 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dtsi +++ b/arch/arm64/boot/dts/arm/foundation-v8.dtsi @@ -73,6 +73,7 @@ optee@0x83000000 { reg = <0x00000000 0x83000000 0 0x01000000>; + no-map; }; }; From c9108c8957d0cb86262b52286fbb9f0ec8f49949 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 3 Jan 2017 17:09:23 +0100 Subject: [PATCH 07/37] arm64: dt: Add no-map to the reserved-memory node for OP-TEE for juno **not for mainline** All the platforms that reserve memory for OP-TEE statically via the DT (i.e., not those that reserve it via UEFI or that patch the DT dynamically thanks to OP-TEE's CFG_DT option) have to mark it 'no-map' so that only the TEE driver may map it. Signed-off-by: Jens Wiklander --- arch/arm64/boot/dts/arm/juno-base.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index 0ff5d784ccb582..0ac89a86ae946c 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -808,6 +808,7 @@ /* Shared memory between secure and non-secure world */ optee@0xfee00000 { reg = <0x00000000 0xfee00000 0 0x00200000>; + no-map; }; }; From f9029fed1f3872956264b32685b257ada1ea7b2c Mon Sep 17 00:00:00 2001 From: Joakim Bech Date: Thu, 24 Mar 2016 09:50:14 +0100 Subject: [PATCH 08/37] arm64: dt: Add OP-TEE firmware to mt8173 **not for mainline** Signed-off-by: Joakim Bech Reviewed-by: Pascal Brand Reviewed-by: Jerome Forissier --- arch/arm64/boot/dts/mediatek/mt8173-evb.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts index 6dffada2e66b4c..e9ba9bf34143e0 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts @@ -60,6 +60,13 @@ gpio = <&pio 9 GPIO_ACTIVE_HIGH>; enable-active-high; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; &cec { From 9f5731cfa0ce7bd049d0149d36c10e020972e61a Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 25 Oct 2016 19:03:38 +0300 Subject: [PATCH 09/37] OP-TEE Benchmark **not for mainline** Add Benchmark support Reviewed-by: Joakim Bech Signed-off-by: Igor Opaniuk [jf: squash fixup commit "tee: optee: optee_bench.h: remove useless include **not for mainline**"] [jf: rebase onto v5.9-rc7] Signed-off-by: Jerome Forissier --- drivers/tee/optee/Kconfig | 7 ++ drivers/tee/optee/Makefile | 1 + drivers/tee/optee/bench.c | 157 ++++++++++++++++++++++++++++++++ drivers/tee/optee/call.c | 5 + drivers/tee/optee/core.c | 3 + drivers/tee/optee/optee_bench.h | 68 ++++++++++++++ drivers/tee/optee/optee_msg.h | 8 ++ drivers/tee/optee/rpc.c | 49 ++++++++++ 8 files changed, 298 insertions(+) create mode 100644 drivers/tee/optee/bench.c create mode 100644 drivers/tee/optee/optee_bench.h diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index 3ca71e3812ed4b..be57a62d5c6e57 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -15,3 +15,10 @@ config OPTEE_SHM_NUM_PRIV_PAGES help This sets the number of private shared memory pages to be used by OP-TEE TEE driver. + +config OPTEE_BENCHMARK + bool "OP-TEE Benchmark (EXPERIMENTAL)" + depends on OPTEE + help + This enables benchmarking feature in the OP-TEE Trusted + Execution Environment (TEE) driver. diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 56263ae3b1d7a3..92247436bf856a 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -6,3 +6,4 @@ optee-objs += rpc.o optee-objs += supp.o optee-objs += shm_pool.o optee-objs += device.o +optee-objs += bench.o diff --git a/drivers/tee/optee/bench.c b/drivers/tee/optee/bench.c new file mode 100644 index 00000000000000..9e73b2fb54adc1 --- /dev/null +++ b/drivers/tee/optee/bench.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017, 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 +#include "optee_bench.h" + +/* + * Specific defines for ARM performance timers + */ +/* aarch32 */ +#define OPTEE_BENCH_DEF_OPTS (1 | 16) +#define OPTEE_BENCH_DEF_OVER 0x8000000f +/* enable 64 divider for CCNT */ +#define OPTEE_BENCH_DIVIDER_OPTS (OPTEE_BENCH_DEF_OPTS | 8) + +/* aarch64 */ +#define OPTEE_BENCH_ARMV8_PMCR_MASK 0x3f +#define OPTEE_BENCH_ARMV8_PMCR_E (1 << 0) /* Enable all counters */ +#define OPTEE_BENCH_ARMV8_PMCR_P (1 << 1) /* Reset all counters */ +#define OPTEE_BENCH_ARMV8_PMCR_C (1 << 2) /* Cycle counter reset */ +#define OPTEE_BENCH_ARMV8_PMCR_D (1 << 3) /* 64 divider */ + +#define OPTEE_BENCH_ARMV8_PMUSERENR_EL0 (1 << 0) /* EL0 access enable */ +#define OPTEE_BENCH_ARMV8_PMUSERENR_CR (1 << 2) /* CCNT read enable */ + +struct optee_ts_global *optee_bench_ts_global; +struct rw_semaphore optee_bench_ts_rwsem; + +#ifdef CONFIG_OPTEE_BENCHMARK +static inline u32 armv8pmu_pmcr_read(void) +{ + u32 val = 0; + + asm volatile("mrs %0, pmcr_el0" : "=r"(val)); + + return (u32)val; +} + +static inline void armv8pmu_pmcr_write(u32 val) +{ + val &= OPTEE_BENCH_ARMV8_PMCR_MASK; + asm volatile("msr pmcr_el0, %0" :: "r"((u64)val)); +} + +static inline u64 read_ccounter(void) +{ + u64 ccounter; + +#ifdef __aarch64__ + asm volatile("mrs %0, PMCCNTR_EL0" : "=r"(ccounter)); +#else + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(ccounter)); +#endif + + return ccounter * OPTEE_BENCH_DIVIDER; +} + +static void optee_pmu_setup(void *data) +{ +#ifdef __aarch64__ + /* Enable EL0 access to PMU counters. */ + asm volatile("msr pmuserenr_el0, %0" :: "r"((u64) + OPTEE_BENCH_ARMV8_PMUSERENR_EL0 | + OPTEE_BENCH_ARMV8_PMUSERENR_CR)); + /* Enable PMU counters */ + armv8pmu_pmcr_write(OPTEE_BENCH_ARMV8_PMCR_P | + OPTEE_BENCH_ARMV8_PMCR_C | + OPTEE_BENCH_ARMV8_PMCR_D); + asm volatile("msr pmcntenset_el0, %0" :: "r"((u64)(1 << 31))); + armv8pmu_pmcr_write(armv8pmu_pmcr_read() | + OPTEE_BENCH_ARMV8_PMCR_E); +#else + /* Enable EL0 access to PMU counters */ + asm volatile("mcr p15, 0, %0, c9, c14, 0" :: "r"(1)); + /* Enable all PMU counters */ + asm volatile("mcr p15, 0, %0, c9, c12, 0" :: "r" + (OPTEE_BENCH_DIVIDER_OPTS)); + /* Disable counter overflow interrupts */ + asm volatile("mcr p15, 0, %0, c9, c12, 1" :: "r"(OPTEE_BENCH_DEF_OVER)); +#endif +} + +static void optee_pmu_disable(void *data) +{ +#ifdef __aarch64__ + /* Disable EL0 access */ + asm volatile("msr pmuserenr_el0, %0" :: "r"((u64)0)); + /* Disable PMU counters */ + armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ~OPTEE_BENCH_ARMV8_PMCR_E); +#else + /* Disable all PMU counters */ + asm volatile("mcr p15, 0, %0, c9, c12, 0" :: "r"(0)); + /* Enable counter overflow interrupts */ + asm volatile("mcr p15, 0, %0, c9, c12, 2" :: "r"(OPTEE_BENCH_DEF_OVER)); + /* Disable EL0 access to PMU counters. */ + asm volatile("mcr p15, 0, %0, c9, c14, 0" :: "r"(0)); +#endif +} + +void optee_bm_enable(void) +{ + on_each_cpu(optee_pmu_setup, NULL, 1); +} + +void optee_bm_disable(void) +{ + on_each_cpu(optee_pmu_disable, NULL, 1); +} + +void optee_bm_timestamp(void) +{ + struct optee_ts_cpu_buf *cpu_buf; + struct optee_time_st ts_data; + uint64_t ts_i; + void *ret_addr; + int cur_cpu = 0; + int ret; + + down_read(&optee_bench_ts_rwsem); + + if (!optee_bench_ts_global) { + up_read(&optee_bench_ts_rwsem); + return; + } + + cur_cpu = get_cpu(); + + if (cur_cpu >= optee_bench_ts_global->cores) { + put_cpu(); + up_read(&optee_bench_ts_rwsem); + return; + } + + ret_addr = __builtin_return_address(0); + + cpu_buf = &optee_bench_ts_global->cpu_buf[cur_cpu]; + ts_i = __sync_fetch_and_add(&cpu_buf->head, 1); + ts_data.cnt = read_ccounter(); + ts_data.addr = (uintptr_t)ret_addr; + ts_data.src = OPTEE_BENCH_KMOD; + cpu_buf->stamps[ts_i & OPTEE_BENCH_MAX_MASK] = ts_data; + + up_read(&optee_bench_ts_rwsem); + + put_cpu(); +} +#endif /* CONFIG_OPTEE_BENCHMARK */ diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 20b6fd7383c54b..3945041e661557 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -13,6 +13,7 @@ #include #include "optee_private.h" #include "optee_smc.h" +#include "optee_bench.h" struct optee_call_waiter { struct list_head list_node; @@ -137,10 +138,14 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) while (true) { struct arm_smccc_res res; + optee_bm_timestamp(); + optee->invoke_fn(param.a0, param.a1, param.a2, param.a3, param.a4, param.a5, param.a6, param.a7, &res); + optee_bm_timestamp(); + if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { /* * Out of threads in secure world, wait for a thread diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index b373b1b08b6dee..77733866c65502 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -18,6 +18,7 @@ #include #include #include +#include "optee_bench.h" #include "optee_private.h" #include "optee_smc.h" #include "shm_pool.h" @@ -602,6 +603,7 @@ static int optee_remove(struct platform_device *pdev) kfree(optee); + optee_bm_disable(); return 0; } @@ -703,6 +705,7 @@ static int optee_probe(struct platform_device *pdev) } pr_info("initialized driver\n"); + optee_bm_enable(); return 0; err: if (optee) { diff --git a/drivers/tee/optee/optee_bench.h b/drivers/tee/optee/optee_bench.h new file mode 100644 index 00000000000000..985e6a011f58f7 --- /dev/null +++ b/drivers/tee/optee/optee_bench.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016, 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_BENCH_H +#define _OPTEE_BENCH_H + +#include + +/* + * Cycle count divider is enabled (in PMCR), + * CCNT value is incremented every 64th clock cycle + */ +#define OPTEE_BENCH_DIVIDER 64 + +/* max amount of timestamps */ +#define OPTEE_BENCH_MAX_STAMPS 32 +#define OPTEE_BENCH_MAX_MASK (OPTEE_BENCH_MAX_STAMPS - 1) + +/* OP-TEE susbsystems ids */ +#define OPTEE_BENCH_KMOD 0x20000000 + +#define OPTEE_MSG_RPC_CMD_BENCH_REG_NEW 0 +#define OPTEE_MSG_RPC_CMD_BENCH_REG_DEL 1 + +/* storing timestamp */ +struct optee_time_st { + uint64_t cnt; /* stores value from CNTPCT register */ + uint64_t addr; /* stores value from program counter register */ + uint64_t src; /* OP-TEE subsystem id */ +}; + +/* per-cpu circular buffer for timestamps */ +struct optee_ts_cpu_buf { + uint64_t head; + uint64_t tail; + struct optee_time_st stamps[OPTEE_BENCH_MAX_STAMPS]; +}; + +/* memory layout for shared memory, where timestamps will be stored */ +struct optee_ts_global { + uint64_t cores; + struct optee_ts_cpu_buf cpu_buf[]; +}; + +extern struct optee_ts_global *optee_bench_ts_global; +extern struct rw_semaphore optee_bench_ts_rwsem; + +#ifdef CONFIG_OPTEE_BENCHMARK +void optee_bm_enable(void); +void optee_bm_disable(void); +void optee_bm_timestamp(void); +#else +static inline void optee_bm_enable(void) {} +static inline void optee_bm_disable(void) {} +static inline void optee_bm_timestamp(void) {} +#endif /* CONFIG_OPTEE_BENCHMARK */ +#endif /* _OPTEE_BENCH_H */ diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 795bc19ae17a6d..7788cc4a8e0554 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -419,4 +419,12 @@ struct optee_msg_arg { */ #define OPTEE_MSG_RPC_CMD_SHM_FREE 7 +/* + * Register timestamp buffer + * + * [in] param[0].u.value.a Subcommand (register buffer, unregister buffer) + * [in] param[0].u.value.b Physical address of timestamp buffer + * [in] param[0].u.value.c Size of buffer + */ +#define OPTEE_MSG_RPC_CMD_BENCH_REG 20 #endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index b4ade54d1f280a..b886ae38eeb64b 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -7,8 +7,10 @@ #include #include +#include #include #include +#include "optee_bench.h" #include "optee_private.h" #include "optee_smc.h" @@ -353,6 +355,50 @@ void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) free_pages_list(call_ctx); } +static void handle_rpc_func_cmd_bm_reg(struct optee_msg_arg *arg) +{ + u64 size; + u64 type; + u64 paddr; + + if (arg->num_params != 1) + goto bad; + + if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != + OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) + goto bad; + + type = arg->params[0].u.value.a; + switch (type) { + case OPTEE_MSG_RPC_CMD_BENCH_REG_NEW: + size = arg->params[0].u.value.c; + paddr = arg->params[0].u.value.b; + down_write(&optee_bench_ts_rwsem); + optee_bench_ts_global = + memremap(paddr, size, MEMREMAP_WB); + if (!optee_bench_ts_global) { + up_write(&optee_bench_ts_rwsem); + goto bad; + } + up_write(&optee_bench_ts_rwsem); + break; + case OPTEE_MSG_RPC_CMD_BENCH_REG_DEL: + down_write(&optee_bench_ts_rwsem); + if (optee_bench_ts_global) + memunmap(optee_bench_ts_global); + optee_bench_ts_global = NULL; + up_write(&optee_bench_ts_rwsem); + break; + default: + goto bad; + } + + 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 optee_call_ctx *call_ctx) @@ -382,6 +428,9 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, case OPTEE_MSG_RPC_CMD_SHM_FREE: handle_rpc_func_cmd_shm_free(ctx, arg); break; + case OPTEE_MSG_RPC_CMD_BENCH_REG: + handle_rpc_func_cmd_bm_reg(arg); + break; default: handle_rpc_supp_cmd(ctx, arg); } From 41e9290b04e0addda81c00413099493a70da4dbd Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Mon, 5 Feb 2018 18:43:34 +0200 Subject: [PATCH 10/37] tee: support of allocating DMA shared buffers **not for mainline** Add support of allocating DMA shared buffers via RPC calls. The main difference with OPTEE_MSG_RPC_SHM_TYPE_KERNEL is that SHM pool manager for shared memory exported to user space is explicitly chosen. As dma-buf is used for exporting buffers to userspace, it provides a possiblity to mmap an allocated SHM buffer into multiple TEE client applications (unlike OPTEE_MSG_RPC_SHM_TYPE_APPL, which leverages tee-supplicant for private allocations). Such buffers should be used only for internal purposes, when there is a need to share meta data between different OP-TEE components (for debugging/profiling purposes). Signed-off-by: Igor Opaniuk [jf: squash fixup commit] Signed-off-by: Jerome Forissier --- drivers/tee/optee/optee_msg.h | 2 ++ drivers/tee/optee/rpc.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 7788cc4a8e0554..8839fa1b2ea767 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -407,6 +407,8 @@ struct optee_msg_arg { #define OPTEE_MSG_RPC_SHM_TYPE_APPL 0 /* Memory only shared with non-secure kernel */ #define OPTEE_MSG_RPC_SHM_TYPE_KERNEL 1 +/* Memory shared with non-secure kernel, but exported to userspace */ +#define OPTEE_MSG_RPC_SHM_TYPE_GLOBAL 2 /* * Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index b886ae38eeb64b..817b1caee28c3e 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -224,6 +224,9 @@ static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED); break; + case OPTEE_MSG_RPC_SHM_TYPE_GLOBAL: + shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); + break; default: arg->ret = TEEC_ERROR_BAD_PARAMETERS; return; @@ -332,6 +335,7 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, cmd_free_suppl(ctx, shm); break; case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: + case OPTEE_MSG_RPC_SHM_TYPE_GLOBAL: tee_shm_free(shm); break; default: From 0b1c1035aa6898f8e3946d935f3c394a89fcc768 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 5 Nov 2018 14:23:42 +0100 Subject: [PATCH 11/37] upstream-tee-subsys-patches.txt: patches up to v4.20-rc1 Records patches available upstream up to v4.20-rc1. Acked-by: Joakim Bech Signed-off-by: Jens Wiklander --- upstream-tee-subsys-patches.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt index 311de9bb2c26c2..8d6c47a183df06 100644 --- a/upstream-tee-subsys-patches.txt +++ b/upstream-tee-subsys-patches.txt @@ -6,7 +6,11 @@ text inside the brackets are the kernel version where it was introduced, followed by the sha1 hash in the upstream kernel tree. -[v4.17] f5d5641b90b3 optee: check shm reference are consistent in offset/size +[v4.20-rc1] 25559c22cef8 tee: add kernel internal client interface +[v4.20-rc1] db878f76b9ff tee: optee: take DT status property into account +[v4.19] 3249527f19d6 tee: optee: making OPTEE_SHM_NUM_PRIV_PAGES configurable via Kconfig +[v4.19] cf89fe88a676 tee: replace getnstimeofday64() with ktime_get_real_ts64() +[v4.17] ab9d3db5b320 tee: check shm references are consistent in offset/size [v4.17] bb765d1c331f tee: shm: fix use-after-free via temporarily dropped reference [v4.16] 5c5f80307ab2 tee: optee: report OP-TEE revision information [v4.16] 6e112de04278 tee: optee: GET_OS_REVISION: document a2 as a build identifier From f795a4f50c0089c12947025caae30d4d69b9a8b8 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Mon, 4 Mar 2019 17:46:33 +0100 Subject: [PATCH 12/37] upstream-tee-subsys-patches.txt: patches up to v5.0 Records patches available upstream up to v5.0. Signed-off-by: Jerome Forissier --- upstream-tee-subsys-patches.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt index 8d6c47a183df06..0208cfaa5b6137 100644 --- a/upstream-tee-subsys-patches.txt +++ b/upstream-tee-subsys-patches.txt @@ -5,7 +5,9 @@ This is a list of all the patches that relates to the TEE subsystem. The text inside the brackets are the kernel version where it was introduced, followed by the sha1 hash in the upstream kernel tree. - +[v5.0] c7c0d8df0b94 tee: optee: add missing of_node_put after of_device_is_available +[v5.0-rc1] 3c15ddb97c77 tee: optee: log message if dynamic shm is enabled +[v5.0-rc1] b2d102bd0146 tee: optee: avoid possible double list_del() [v4.20-rc1] 25559c22cef8 tee: add kernel internal client interface [v4.20-rc1] db878f76b9ff tee: optee: take DT status property into account [v4.19] 3249527f19d6 tee: optee: making OPTEE_SHM_NUM_PRIV_PAGES configurable via Kconfig From 578ce3c9d8cbb07d527f2b6919e58bce3aeb1a5a Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Tue, 7 May 2019 09:59:45 +0200 Subject: [PATCH 13/37] upstream-tee-subsys-patches.txt: patches up to v5.1 Records patches available upstream up to v5.1. Signed-off-by: Jerome Forissier --- upstream-tee-subsys-patches.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt index 0208cfaa5b6137..39ff9bdac16c68 100644 --- a/upstream-tee-subsys-patches.txt +++ b/upstream-tee-subsys-patches.txt @@ -5,6 +5,14 @@ This is a list of all the patches that relates to the TEE subsystem. The text inside the brackets are the kernel version where it was introduced, followed by the sha1 hash in the upstream kernel tree. +[v5.1-rc1] 32356d309c22 tee: optee: update optee_msg.h and optee_smc.h to dual license +[v5.1-rc1] 4f062dc1b759 tee: add cancellation support to client interface +[v5.1-rc1] 62ade1bed27c tee: optee: Fix unsigned comparison with less than zero +[v5.1-rc1] bb342f016862 tee: fix possible error pointer ctx dereferencing +[v5.1-rc1] 50ceca6894ad tee: optee: Initialize some structs using memset instead of braces +[v5.1-rc1] c3fa24af9244 tee: optee: add TEE bus device enumeration support +[v5.1-rc1] 0fc1db9d1059 tee: add bus driver framework for TEE based devices +[v5.1-rc1] 42bf4152d8a7 tee: add supp_nowait flag in tee_context struct [v5.0] c7c0d8df0b94 tee: optee: add missing of_node_put after of_device_is_available [v5.0-rc1] 3c15ddb97c77 tee: optee: log message if dynamic shm is enabled [v5.0-rc1] b2d102bd0146 tee: optee: avoid possible double list_del() From fa11127f89d77b4da33c1a804446d74232c6b951 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 14 Aug 2017 17:13:34 +0200 Subject: [PATCH 14/37] staging/ion: add Allwinner ION "secure" heap Dumped from: https://github.com/loboris/OrangePI-Kernel/tree/master/linux-3.4 0cc8d855adb457d1860d6e25cb93b6cc75d5a09d Author: Sunny for Allwinner. Changes made on original "secure heap" implementation: - minor coding style: fix includes, empty lines and overlong lines, indentation, comment layout. - Original path modified the ion uapi. We do not attempt to modify uapi/ion.h. "secure" (or "domain") heaps are under ID ION_HEAP_TYPE_CUSTOM + 1 (legacy 'secure heap type' value). Signed-off-by: Etienne Carriere --- drivers/staging/android/ion/ion_secure_heap.c | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 drivers/staging/android/ion/ion_secure_heap.c diff --git a/drivers/staging/android/ion/ion_secure_heap.c b/drivers/staging/android/ion/ion_secure_heap.c new file mode 100644 index 00000000000000..57a75b3e578a01 --- /dev/null +++ b/drivers/staging/android/ion/ion_secure_heap.c @@ -0,0 +1,178 @@ +/* + * drivers/gpu/ion/ion_secure_heap.c + * + * Copyright (C) 2016-2017 Linaro, Inc. All rigths reserved. + * Copyright (C) Allwinner 2014 + * Author: for Allwinner. + * + * Add secure heap support. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ion.h" +#include "ion_priv.h" + +#ifndef ION_HEAP_TYPE_SECURE +#define ION_HEAP_TYPE_SECURE (ION_HEAP_TYPE_CUSTOM + 1) +#endif + +struct ion_secure_heap { + struct ion_heap heap; + struct gen_pool *pool; + ion_phys_addr_t base; + size_t size; +}; + +ion_phys_addr_t ion_secure_allocate(struct ion_heap *heap, + unsigned long size, + unsigned long align) +{ + struct ion_secure_heap *secure_heap = + container_of(heap, struct ion_secure_heap, heap); + unsigned long offset = gen_pool_alloc(secure_heap->pool, size); + + if (!offset) { + pr_err("%s(%d) err: alloc 0x%08x bytes failed\n", + __func__, __LINE__, (u32)size); + return ION_CARVEOUT_ALLOCATE_FAIL; + } + return offset; +} + +void ion_secure_free(struct ion_heap *heap, ion_phys_addr_t addr, + unsigned long size) +{ + struct ion_secure_heap *secure_heap = + container_of(heap, struct ion_secure_heap, heap); + + if (addr == ION_CARVEOUT_ALLOCATE_FAIL) + return; + gen_pool_free(secure_heap->pool, addr, size); +} + +static int ion_secure_heap_phys(struct ion_heap *heap, + struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + *addr = buffer->priv_phys; + *len = buffer->size; + return 0; +} + +static int ion_secure_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + buffer->priv_phys = ion_secure_allocate(heap, size, align); + return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0; +} + +static void ion_secure_heap_free(struct ion_buffer *buffer) +{ + struct ion_heap *heap = buffer->heap; + + ion_secure_free(heap, buffer->priv_phys, buffer->size); + buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL; +} + +struct sg_table *ion_secure_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct sg_table *table; + int ret; + + table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + ret = sg_alloc_table(table, 1, GFP_KERNEL); + if (ret) { + kfree(table); + return ERR_PTR(ret); + } + sg_set_page(table->sgl, phys_to_page(buffer->priv_phys), buffer->size, + 0); + return table; +} + +void ion_secure_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + sg_free_table(buffer->sg_table); + kfree(buffer->sg_table); +} + +int ion_secure_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + /* + * when user call ION_IOC_ALLOC not with ION_FLAG_CACHED, ion_mmap will + * change prog to pgprot_writecombine itself, so we donot need change to + * pgprot_writecombine here manually. + */ + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static struct ion_heap_ops secure_heap_ops = { + .allocate = ion_secure_heap_allocate, + .free = ion_secure_heap_free, + .phys = ion_secure_heap_phys, + .map_dma = ion_secure_heap_map_dma, + .unmap_dma = ion_secure_heap_unmap_dma, + .map_user = ion_secure_heap_map_user, + .map_kernel = ion_heap_map_kernel, + .unmap_kernel = ion_heap_unmap_kernel, +}; + +struct ion_heap *ion_secure_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_secure_heap *secure_heap; + + secure_heap = kzalloc(sizeof(struct ion_secure_heap), GFP_KERNEL); + if (!secure_heap) + return ERR_PTR(-ENOMEM); + + secure_heap->pool = gen_pool_create(12, -1); + if (!secure_heap->pool) { + kfree(secure_heap); + return ERR_PTR(-ENOMEM); + } + secure_heap->base = heap_data->base; + secure_heap->size = heap_data->size; + gen_pool_add(secure_heap->pool, secure_heap->base, heap_data->size, -1); + secure_heap->heap.ops = &secure_heap_ops; + secure_heap->heap.type = ION_HEAP_TYPE_SECURE; + + return &secure_heap->heap; +} + +void ion_secure_heap_destroy(struct ion_heap *heap) +{ + struct ion_secure_heap *secure_heap = + container_of(heap, struct ion_secure_heap, heap); + + gen_pool_destroy(secure_heap->pool); + kfree(secure_heap); + secure_heap = NULL; +} From 11ed7aed729224ebfe2dadb2672932995f4e6628 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 14 Aug 2017 17:16:39 +0200 Subject: [PATCH 15/37] ion: "unmapped" heap for secure data path OP-TEE/SDP (Secure Data Path) memory pools are created through ION secure type heap" from Allwinner. This change renames "secure" into "unmapped" as, from Linux point of view, the heap constraint is manipulating unampped memory pools/buffers. "Unmapped" heap support is integrated in ION UAPI (actually this was the Allwinner initial proposal) and ION DT parsing support. Based in work from Sunny for Allwinner. Changes: - rename "secure_heap" into "unmapped_heap" - define ION_HEAP_TYPE_UNMAPPED in ION UAPI (sic!) - add structure "struct unmapped_buffer_priv" to hold allocated buffer private data (currently only the buffer physical address. - adapt to recent ION (i.e ion_phys_addr_t => phys_addr_t) - Support dummy heap configuration: one can hard code into the Linux kernel configuration the location of a "unmapped heap". It will be created during ION device inits: see CONFIG_ION_DUMMY_UNMAPPED_HEAP. Signed-off-by: Etienne Carriere --- drivers/staging/android/ion/Kconfig | 32 +++ drivers/staging/android/ion/Makefile | 1 + drivers/staging/android/ion/ion_secure_heap.c | 178 ------------- .../staging/android/ion/ion_unmapped_heap.c | 252 ++++++++++++++++++ drivers/staging/android/uapi/ion.h | 3 + 5 files changed, 288 insertions(+), 178 deletions(-) delete mode 100644 drivers/staging/android/ion/ion_secure_heap.c create mode 100644 drivers/staging/android/ion/ion_unmapped_heap.c diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig index 989fe84a9f9d79..23ada1edb2deb5 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -25,3 +25,35 @@ config ION_CMA_HEAP Choose this option to enable CMA heaps with Ion. This heap is backed by the Contiguous Memory Allocator (CMA). If your system has these regions, you should say Y here. + +config ION_UNMAPPED_HEAP + bool "ION unmapped heap support" + depends on ION + help + Choose this option to enable UNMAPPED heaps with Ion. This heap is + backed in specific memory pools, carveout from the Linux memory. + Unless you know your system has these regions, you should say N here. + +config ION_DUMMY_UNMAPPED_HEAP + bool "ION dummy driver define an ION unmapped heap" + depends on ION_UNMAPPED_HEAP + help + Dummy ION driver will create a unmapped heap from physical + location provided through CONFIG_ION_DUMMY_UNMAPPED_BASE and + CONFIG_ION_DUMMY_UNMAPPED_SIZE. + +config ION_DUMMY_UNMAPPED_BASE + hex "Physical base address of the ION unmapped heap" + depends on ION_DUMMY_UNMAPPED_HEAP + default 0 + help + Allows one the statically define an unmapped heap from the + ION dummy driver to exercice unamped heaps buffer managment. + +config ION_DUMMY_UNMAPPED_SIZE + hex "Physical byte size of the ION unmapped heap" + depends on ION_DUMMY_UNMAPPED_HEAP + default 0 + help + Allows one the statically define an unmapped heap from the + ION dummy driver to exercice unamped heaps buffer managment. diff --git a/drivers/staging/android/ion/Makefile b/drivers/staging/android/ion/Makefile index 5f4487b1a22440..8cc78673661aea 100644 --- a/drivers/staging/android/ion/Makefile +++ b/drivers/staging/android/ion/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_ION) += ion.o ion_heap.o obj-$(CONFIG_ION_SYSTEM_HEAP) += ion_system_heap.o ion_page_pool.o obj-$(CONFIG_ION_CMA_HEAP) += ion_cma_heap.o +obj-$(CONFIG_ION_UNMAPPED_HEAP) += ion_unmapped_heap.o diff --git a/drivers/staging/android/ion/ion_secure_heap.c b/drivers/staging/android/ion/ion_secure_heap.c deleted file mode 100644 index 57a75b3e578a01..00000000000000 --- a/drivers/staging/android/ion/ion_secure_heap.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * drivers/gpu/ion/ion_secure_heap.c - * - * Copyright (C) 2016-2017 Linaro, Inc. All rigths reserved. - * Copyright (C) Allwinner 2014 - * Author: for Allwinner. - * - * Add secure heap support. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ion.h" -#include "ion_priv.h" - -#ifndef ION_HEAP_TYPE_SECURE -#define ION_HEAP_TYPE_SECURE (ION_HEAP_TYPE_CUSTOM + 1) -#endif - -struct ion_secure_heap { - struct ion_heap heap; - struct gen_pool *pool; - ion_phys_addr_t base; - size_t size; -}; - -ion_phys_addr_t ion_secure_allocate(struct ion_heap *heap, - unsigned long size, - unsigned long align) -{ - struct ion_secure_heap *secure_heap = - container_of(heap, struct ion_secure_heap, heap); - unsigned long offset = gen_pool_alloc(secure_heap->pool, size); - - if (!offset) { - pr_err("%s(%d) err: alloc 0x%08x bytes failed\n", - __func__, __LINE__, (u32)size); - return ION_CARVEOUT_ALLOCATE_FAIL; - } - return offset; -} - -void ion_secure_free(struct ion_heap *heap, ion_phys_addr_t addr, - unsigned long size) -{ - struct ion_secure_heap *secure_heap = - container_of(heap, struct ion_secure_heap, heap); - - if (addr == ION_CARVEOUT_ALLOCATE_FAIL) - return; - gen_pool_free(secure_heap->pool, addr, size); -} - -static int ion_secure_heap_phys(struct ion_heap *heap, - struct ion_buffer *buffer, - ion_phys_addr_t *addr, size_t *len) -{ - *addr = buffer->priv_phys; - *len = buffer->size; - return 0; -} - -static int ion_secure_heap_allocate(struct ion_heap *heap, - struct ion_buffer *buffer, - unsigned long size, unsigned long align, - unsigned long flags) -{ - buffer->priv_phys = ion_secure_allocate(heap, size, align); - return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0; -} - -static void ion_secure_heap_free(struct ion_buffer *buffer) -{ - struct ion_heap *heap = buffer->heap; - - ion_secure_free(heap, buffer->priv_phys, buffer->size); - buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL; -} - -struct sg_table *ion_secure_heap_map_dma(struct ion_heap *heap, - struct ion_buffer *buffer) -{ - struct sg_table *table; - int ret; - - table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); - if (!table) - return ERR_PTR(-ENOMEM); - ret = sg_alloc_table(table, 1, GFP_KERNEL); - if (ret) { - kfree(table); - return ERR_PTR(ret); - } - sg_set_page(table->sgl, phys_to_page(buffer->priv_phys), buffer->size, - 0); - return table; -} - -void ion_secure_heap_unmap_dma(struct ion_heap *heap, - struct ion_buffer *buffer) -{ - sg_free_table(buffer->sg_table); - kfree(buffer->sg_table); -} - -int ion_secure_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, - struct vm_area_struct *vma) -{ - /* - * when user call ION_IOC_ALLOC not with ION_FLAG_CACHED, ion_mmap will - * change prog to pgprot_writecombine itself, so we donot need change to - * pgprot_writecombine here manually. - */ - return remap_pfn_range(vma, vma->vm_start, - __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, - vma->vm_end - vma->vm_start, - vma->vm_page_prot); -} - -static struct ion_heap_ops secure_heap_ops = { - .allocate = ion_secure_heap_allocate, - .free = ion_secure_heap_free, - .phys = ion_secure_heap_phys, - .map_dma = ion_secure_heap_map_dma, - .unmap_dma = ion_secure_heap_unmap_dma, - .map_user = ion_secure_heap_map_user, - .map_kernel = ion_heap_map_kernel, - .unmap_kernel = ion_heap_unmap_kernel, -}; - -struct ion_heap *ion_secure_heap_create(struct ion_platform_heap *heap_data) -{ - struct ion_secure_heap *secure_heap; - - secure_heap = kzalloc(sizeof(struct ion_secure_heap), GFP_KERNEL); - if (!secure_heap) - return ERR_PTR(-ENOMEM); - - secure_heap->pool = gen_pool_create(12, -1); - if (!secure_heap->pool) { - kfree(secure_heap); - return ERR_PTR(-ENOMEM); - } - secure_heap->base = heap_data->base; - secure_heap->size = heap_data->size; - gen_pool_add(secure_heap->pool, secure_heap->base, heap_data->size, -1); - secure_heap->heap.ops = &secure_heap_ops; - secure_heap->heap.type = ION_HEAP_TYPE_SECURE; - - return &secure_heap->heap; -} - -void ion_secure_heap_destroy(struct ion_heap *heap) -{ - struct ion_secure_heap *secure_heap = - container_of(heap, struct ion_secure_heap, heap); - - gen_pool_destroy(secure_heap->pool); - kfree(secure_heap); - secure_heap = NULL; -} diff --git a/drivers/staging/android/ion/ion_unmapped_heap.c b/drivers/staging/android/ion/ion_unmapped_heap.c new file mode 100644 index 00000000000000..28898360920190 --- /dev/null +++ b/drivers/staging/android/ion/ion_unmapped_heap.c @@ -0,0 +1,252 @@ +/* + * drivers/staging/android/ion/ion_unmapped_heap.c + * + * Copyright (C) 2016-2017 Linaro, Inc. + * Copyright (C) Allwinner 2014 + * Author: for Allwinner. + * + * 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. + */ + +/* + * ION heap type for handling physical memory heap not mapped + * in the linux-based OS. + * + * "unmapped heap" buffers are default not mapped but buffer owner + * can explicitly request mapping for some specific purpose. + * + * Based on Allwinner work (allocation thru gen_pool) and + * HiSilicon work (create ION heaps from DT nodes, + * Author: Chen Feng ). + */ + +#include +#include +#include +#include + +#include "ion.h" + +/* + * TODO: non-contigous unammped heaps: + * - add a flag to specify contiguity constraint? + * - define antoher heap type that allocate to the smae pool(s)? + */ + +struct ion_unmapped_heap { + struct ion_heap heap; + struct gen_pool *pool; + phys_addr_t base; + size_t size; +}; + +struct unmapped_buffer_priv { + phys_addr_t base; +}; + +static phys_addr_t get_buffer_base(struct unmapped_buffer_priv *priv) +{ + return priv->base; +} + +static struct device *heap2dev(struct ion_heap *heap) +{ + return heap->dev->dev.this_device; +} + +static phys_addr_t ion_unmapped_allocate(struct ion_heap *heap, + unsigned long size, + phys_addr_t *addr) +{ + struct ion_unmapped_heap *umh = + container_of(heap, struct ion_unmapped_heap, heap); + unsigned long offset = gen_pool_alloc(umh->pool, size); + + if (!offset) { + dev_err(heap2dev(heap), + "%s(%d) err: alloc 0x%08x bytes failed\n", + __func__, __LINE__, (u32)size); + return false; + } + + *addr = offset; + return true; +} + +static void ion_unmapped_free(struct ion_heap *heap, phys_addr_t addr, + unsigned long size) +{ + struct ion_unmapped_heap *umh = + container_of(heap, struct ion_unmapped_heap, heap); + + gen_pool_free(umh->pool, addr, size); +} + +static struct sg_table *ion_unmapped_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct sg_table *table; + int ret; + + table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + ret = sg_alloc_table(table, 1, GFP_KERNEL); + if (ret) { + kfree(table); + return ERR_PTR(ret); + } + sg_set_page(table->sgl, + phys_to_page(get_buffer_base(buffer->priv_virt)), + buffer->size, 0); + + return table; +} + +void ion_unmapped_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + sg_free_table(buffer->sg_table); + kfree(buffer->sg_table); +} + +static int ion_unmapped_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, + unsigned long flags) +{ + struct unmapped_buffer_priv *priv; + phys_addr_t base; + int rc = -EINVAL; + + if (!ion_unmapped_allocate(heap, size, &base)) + return -ENOMEM; + + priv = devm_kzalloc(heap2dev(heap), sizeof(*priv), GFP_KERNEL); + if (IS_ERR_OR_NULL(priv)) { + rc = -ENOMEM; + goto err; + } + + priv->base = base; + buffer->size = roundup(size, PAGE_SIZE); + buffer->priv_virt = priv; + + buffer->sg_table = ion_unmapped_heap_map_dma(heap, buffer); + if (!buffer->sg_table) { + rc = -ENOMEM; + goto err; + } + sg_dma_address(buffer->sg_table->sgl) = priv->base; + sg_dma_len(buffer->sg_table->sgl) = size; + return 0; +err: + ion_unmapped_free(heap, base, size); + devm_kfree(heap2dev(heap), priv); + buffer->priv_virt = NULL; + return rc; +} + +static void ion_unmapped_heap_free(struct ion_buffer *buffer) +{ + struct ion_heap *heap = buffer->heap; + + ion_unmapped_heap_unmap_dma(heap, buffer); + ion_unmapped_free(heap, get_buffer_base(buffer->priv_virt), + buffer->size); + devm_kfree(heap2dev(heap), buffer->priv_virt); + buffer->priv_virt = NULL; +} + +static int ion_unmapped_heap_map_user(struct ion_heap *heap, + struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + phys_addr_t pa = get_buffer_base(buffer->priv_virt); + + /* + * when user call ION_IOC_ALLOC not with ION_FLAG_CACHED, ion_mmap will + * change vma->vm_page_prot to pgprot_writecombine itself, so we do not + * need change to pgprot_writecombine here manually. + */ + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(pa) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static struct ion_heap_ops unmapped_heap_ops = { + .allocate = ion_unmapped_heap_allocate, + .free = ion_unmapped_heap_free, + .map_user = ion_unmapped_heap_map_user, + .map_kernel = ion_heap_map_kernel, + .unmap_kernel = ion_heap_unmap_kernel, +}; + +struct ion_heap *ion_unmapped_heap_create(phys_addr_t base, size_t size) +{ + struct ion_unmapped_heap *umh; + + umh = kzalloc(sizeof(struct ion_unmapped_heap), GFP_KERNEL); + if (!umh) + return ERR_PTR(-ENOMEM); + + umh->pool = gen_pool_create(PAGE_SHIFT, -1); + if (!umh->pool) { + kfree(umh); + return ERR_PTR(-ENOMEM); + } + umh->base = base; + umh->size = size; + + gen_pool_add(umh->pool, umh->base, size, -1); + umh->heap.ops = &unmapped_heap_ops; + umh->heap.type = ION_HEAP_TYPE_UNMAPPED; + + return &umh->heap; +} +EXPORT_SYMBOL(ion_unmapped_heap_create); + +void ion_unmapped_heap_destroy(struct ion_heap *heap) +{ + struct ion_unmapped_heap *umh = + container_of(heap, struct ion_unmapped_heap, heap); + + gen_pool_destroy(umh->pool); + kfree(umh); + umh = NULL; +} +EXPORT_SYMBOL(ion_unmapped_heap_destroy); + +#if defined(CONFIG_ION_DUMMY_UNMAPPED_HEAP) && CONFIG_ION_DUMMY_UNMAPPED_SIZE +#define DUMMY_UNAMMPED_HEAP_NAME "unmapped_contiguous" + +static int ion_add_dummy_unmapped_heaps(void) +{ + struct ion_heap *heap; + const char name[] = DUMMY_UNAMMPED_HEAP_NAME; + + heap = ion_unmapped_heap_create(CONFIG_ION_DUMMY_UNMAPPED_BASE, + CONFIG_ION_DUMMY_UNMAPPED_SIZE); + if (IS_ERR(heap)) + return PTR_ERR(heap); + + heap->name = kzalloc(sizeof(name), GFP_KERNEL); + if (IS_ERR(heap->name)) { + kfree(heap); + return PTR_ERR(heap->name); + } + memcpy((char *)heap->name, name, sizeof(name)); + + ion_device_add_heap(heap); + return 0; +} +device_initcall(ion_add_dummy_unmapped_heaps); +#endif diff --git a/drivers/staging/android/uapi/ion.h b/drivers/staging/android/uapi/ion.h index 46c93fcb46d6e6..32862f2623d397 100644 --- a/drivers/staging/android/uapi/ion.h +++ b/drivers/staging/android/uapi/ion.h @@ -19,6 +19,8 @@ * carveout heap, allocations are physically * contiguous * @ION_HEAP_TYPE_DMA: memory allocated via DMA API + * @ION_HEAP_TYPE_UNMAPPED: memory not intended to be mapped into the + * linux address space unless for debug cases * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask * is used to identify the heaps, so only 32 * total heap types are supported @@ -29,6 +31,7 @@ enum ion_heap_type { ION_HEAP_TYPE_CARVEOUT, ION_HEAP_TYPE_CHUNK, ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_UNMAPPED, ION_HEAP_TYPE_CUSTOM, /* * must be last so device specific heaps always * are at the end of this enum From 366e674916bc6c88d83edc8a88908ec3e1085cf8 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Fri, 8 Sep 2017 13:53:10 +0200 Subject: [PATCH 16/37] staging/ion: condition unmapped heap to supported architectures Condition ION unmapped heap implementation to architectures that currently support it. ARM is one of these. Signed-off-by: Etienne Carriere Reviewed-by: Joakim Bech --- drivers/staging/android/ion/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig index 23ada1edb2deb5..3473a88a3cfc82 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -28,7 +28,7 @@ config ION_CMA_HEAP config ION_UNMAPPED_HEAP bool "ION unmapped heap support" - depends on ION + depends on ION && ARM help Choose this option to enable UNMAPPED heaps with Ion. This heap is backed in specific memory pools, carveout from the Linux memory. From 05790c9df7e4f800024580c37edcb85e992082e6 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Thu, 14 Sep 2017 17:52:51 +0200 Subject: [PATCH 17/37] staging/ion: ARM64 supports ION_UNMAPPED_HEAP Signed-off-by: Etienne Carriere --- drivers/staging/android/ion/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig index 3473a88a3cfc82..f3cfa1171a2bfe 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -28,7 +28,7 @@ config ION_CMA_HEAP config ION_UNMAPPED_HEAP bool "ION unmapped heap support" - depends on ION && ARM + depends on ION && (ARM || ARM64) help Choose this option to enable UNMAPPED heaps with Ion. This heap is backed in specific memory pools, carveout from the Linux memory. From 6af0afc0fbbeb0fe5a1dc5ae01bb19a5caf8d656 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 9 Oct 2017 13:47:40 +0200 Subject: [PATCH 18/37] staging:ion: add a no-map property to ion dmabuf attachment Ion unmapped heap aims at not being mapped. This change prevents Ion from calling dma-mapping support on dma_buf_attach for buffers in an unmapped heap. This change is a bit intrusive in the Ion driver. Maybe there is another way to deal with the dma-mapping resources used for the unmapped heap. Signed-off-by: Etienne Carriere [jf: rebase onto v5.9-rc7] Signed-off-by: Jerome Forissier --- drivers/staging/android/ion/ion.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 3c9f09506ffa22..2f6b7a7c6697b0 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -173,6 +173,7 @@ struct ion_dma_buf_attachment { struct device *dev; struct sg_table *table; struct list_head list; + bool no_map; }; static int ion_dma_buf_attach(struct dma_buf *dmabuf, @@ -192,6 +193,9 @@ static int ion_dma_buf_attach(struct dma_buf *dmabuf, return -ENOMEM; } + if (buffer->heap->type == ION_HEAP_TYPE_UNMAPPED) + a->no_map = true; + a->table = table; a->dev = attachment->dev; INIT_LIST_HEAD(&a->list); @@ -228,6 +232,9 @@ static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, table = a->table; + if (a->no_map) + return table; + ret = dma_map_sgtable(attachment->dev, table, direction, 0); if (ret) return ERR_PTR(ret); @@ -239,6 +246,11 @@ static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *table, enum dma_data_direction direction) { + struct ion_dma_buf_attachment *a = attachment->priv; + + if (a->no_map) + return; + dma_unmap_sgtable(attachment->dev, table, direction, 0); } From 816b96777488ac5bf3c2ef50a1210a2eb31606ec Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Fri, 31 Aug 2018 11:53:16 +0100 Subject: [PATCH 19/37] staging: android: ion: do not clear dma_address of unmapped heap Since commit 54ef5b9db767 (staging: android: ion: Initialize dma_address of new sg list") (Linux v4.17), the helper function dup_sg_table() called by ion_dma_buf_attach() does not preserve the dma_address from the original SG list. It is a problem for the unmapped heap, because dma_buf_attach() followed by dma_buf_map_attachment() now returns a SG table with NULL dma_address, which breaks tee_shm_register_fd(). This commit avoids the dma_address reset for the unmapped heap. Signed-off-by: Jerome Forissier Tested-by: Jerome Forissier (HiKey960, SDP) Tested-by: Etienne Carriere (Qemu_v7/v8, SDP) --- drivers/staging/android/ion/ion.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 2f6b7a7c6697b0..c007cfffc2c531 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -137,7 +137,8 @@ static void ion_buffer_kmap_put(struct ion_buffer *buffer) } } -static struct sg_table *dup_sg_table(struct sg_table *table) +static struct sg_table *dup_sg_table(struct sg_table *table, + bool preserve_dma_address) { struct sg_table *new_table; int ret, i; @@ -156,7 +157,8 @@ static struct sg_table *dup_sg_table(struct sg_table *table) new_sg = new_table->sgl; for_each_sgtable_sg(table, sg, i) { memcpy(new_sg, sg, sizeof(*sg)); - new_sg->dma_address = 0; + if (!preserve_dma_address) + new_sg->dma_address = 0; new_sg = sg_next(new_sg); } @@ -187,15 +189,15 @@ static int ion_dma_buf_attach(struct dma_buf *dmabuf, if (!a) return -ENOMEM; - table = dup_sg_table(buffer->sg_table); + if (buffer->heap->type == ION_HEAP_TYPE_UNMAPPED) + a->no_map = true; + + table = dup_sg_table(buffer->sg_table, a->no_map); if (IS_ERR(table)) { kfree(a); return -ENOMEM; } - if (buffer->heap->type == ION_HEAP_TYPE_UNMAPPED) - a->no_map = true; - a->table = table; a->dev = attachment->dev; INIT_LIST_HEAD(&a->list); From bfc70e107bec12fc63abd6b3efd626de5ccc9c7c Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Fri, 15 Nov 2019 13:55:02 +0100 Subject: [PATCH 20/37] upstream-tee-subsys-patches.txt: patches up to v5.4-rc7 Records patches available upstream up to v5.4-rc7. Signed-off-by: Jerome Forissier --- upstream-tee-subsys-patches.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt index 39ff9bdac16c68..89a7b1a4f83603 100644 --- a/upstream-tee-subsys-patches.txt +++ b/upstream-tee-subsys-patches.txt @@ -5,6 +5,8 @@ This is a list of all the patches that relates to the TEE subsystem. The text inside the brackets are the kernel version where it was introduced, followed by the sha1 hash in the upstream kernel tree. +[v5.4-rc1] 9f02b8f61f29 tee: optee: add might_sleep for RPC requests +[v5.2-rc1] 9733b072a12a optee: allow to work without static shared memory [v5.1-rc1] 32356d309c22 tee: optee: update optee_msg.h and optee_smc.h to dual license [v5.1-rc1] 4f062dc1b759 tee: add cancellation support to client interface [v5.1-rc1] 62ade1bed27c tee: optee: Fix unsigned comparison with less than zero From 10ef4b839197f3eabbf6365341b6cada8d746885 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Fri, 15 Nov 2019 13:55:02 +0100 Subject: [PATCH 21/37] upstream-tee-subsys-patches.txt: patches up to v5.5 Records patches available upstream up to v5.5. Signed-off-by: Jerome Forissier --- upstream-tee-subsys-patches.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt index 89a7b1a4f83603..5f1df980c11428 100644 --- a/upstream-tee-subsys-patches.txt +++ b/upstream-tee-subsys-patches.txt @@ -5,6 +5,11 @@ This is a list of all the patches that relates to the TEE subsystem. The text inside the brackets are the kernel version where it was introduced, followed by the sha1 hash in the upstream kernel tree. +[v5.5] 9e0caab8e0f9 tee: optee: Fix compilation issue with nommu +[v5.5-rc7] 5a769f6ff439 optee: Fix multi page dynamic shm pool alloc +[v5.5-rc1] 03212e347f94 tee: optee: fix device enumeration error handling +[v5.5-rc1] a249dd200d03 tee: optee: Fix dynamic shm pool allocations +[v5.5-rc1] 1832f2d8ff69 compat_ioctl: move more drivers to compat_ptr_ioctl [v5.4-rc1] 9f02b8f61f29 tee: optee: add might_sleep for RPC requests [v5.2-rc1] 9733b072a12a optee: allow to work without static shared memory [v5.1-rc1] 32356d309c22 tee: optee: update optee_msg.h and optee_smc.h to dual license From 185d7548c96847b52481e7920cc46d5250835cfd Mon Sep 17 00:00:00 2001 From: Cedric Neveux Date: Mon, 4 Mar 2019 08:54:23 +0100 Subject: [PATCH 22/37] driver: tee: Handle NULL pointer indication from client TEE Client introduce a new capability "TEE_GEN_CAP_MEMREF_NULL" to handle the support of the shared memory buffer with a NULL pointer. This capability depends on TEE Capabilities and driver support. Driver and TEE exchange capabilities at driver initialization. Signed-off-by: Michael Whitfield Signed-off-by: Cedric Neveux Reviewed-by: Jens Wiklander Reviewed-by: Joakim Bech Tested-by: Joakim Bech (QEMU) --- drivers/tee/optee/core.c | 7 +++++ drivers/tee/optee/optee_smc.h | 3 +++ drivers/tee/tee_core.c | 49 ++++++++++++++++++++++------------- include/linux/tee_drv.h | 3 +++ include/uapi/linux/tee.h | 13 ++++++++++ 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 77733866c65502..f4846c6a7ee79d 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -217,6 +217,8 @@ static void optee_get_version(struct tee_device *teedev, if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) v.gen_caps |= TEE_GEN_CAP_REG_MEM; + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) + v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; *vers = v; } @@ -263,6 +265,11 @@ static int optee_open(struct tee_context *ctx) mutex_init(&ctxdata->mutex); INIT_LIST_HEAD(&ctxdata->sess_list); + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) + ctx->cap_memref_null = true; + else + ctx->cap_memref_null = false; + ctx->data = ctxdata; return 0; } diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index c72122d9c99724..777ad54d4c2c20 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -215,6 +215,9 @@ struct optee_smc_get_shm_config_result { */ #define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2) +/* Secure world supports Shared Memory with a NULL buffer reference */ +#define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) + #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 6492aee9b8451f..c57c85d60d668e 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -419,25 +419,38 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: /* - * If we fail to get a pointer to a shared memory - * object (and increase the ref count) from an - * identifier we return an error. All pointers that - * has been added in params have an increased ref - * count. It's the callers responibility to do - * tee_shm_put() on all resolved pointers. + * If a NULL pointer is passed to a TA in the TEE, + * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL + * indicating a NULL memory reference. */ - shm = tee_shm_get_from_id(ctx, ip.c); - if (IS_ERR(shm)) - return PTR_ERR(shm); - - /* - * Ensure offset + size does not overflow offset - * and does not overflow the size of the referred - * shared memory object. - */ - if ((ip.a + ip.b) < ip.a || - (ip.a + ip.b) > shm->size) { - tee_shm_put(shm); + if (ip.c != TEE_MEMREF_NULL) { + /* + * If we fail to get a pointer to a shared + * memory object (and increase the ref count) + * from an identifier we return an error. All + * pointers that has been added in params have + * an increased ref count. It's the callers + * responibility to do tee_shm_put() on all + * resolved pointers. + */ + shm = tee_shm_get_from_id(ctx, ip.c); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + /* + * Ensure offset + size does not overflow + * offset and does not overflow the size of + * the referred shared memory object. + */ + if ((ip.a + ip.b) < ip.a || + (ip.a + ip.b) > shm->size) { + tee_shm_put(shm); + return -EINVAL; + } + } else if (ctx->cap_memref_null) { + /* Pass NULL pointer to OP-TEE */ + shm = NULL; + } else { return -EINVAL; } diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index f4c6d6e7ced7ab..aed7390899689e 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -47,6 +47,8 @@ struct tee_shm_pool; * and just return with an error code. It is needed for requests * that arises from TEE based kernel drivers that should be * non-blocking in nature. + * @cap_memref_null: flag indicating if the TEE Client support shared + * memory buffer with a NULL pointer. */ struct tee_context { struct tee_device *teedev; @@ -54,6 +56,7 @@ struct tee_context { struct kref refcount; bool releasing; bool supp_nowait; + bool cap_memref_null; }; struct tee_param_memref { diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index b7148b5ebf792f..9eed9deddcc672 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -51,6 +51,9 @@ #define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */ #define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */ #define TEE_GEN_CAP_REG_MEM (1 << 2)/* Supports registering shared memory */ +#define TEE_GEN_CAP_MEMREF_NULL (1 << 3)/* NULL MemRef support */ + +#define TEE_MEMREF_NULL (__u64)(-1) /* NULL MemRef Buffer */ /* * TEE Implementation ID @@ -229,6 +232,16 @@ struct tee_ioctl_buf_data { * a part of a shared memory by specifying an offset (@a) and size (@b) of * the object. To supply the entire shared memory object set the offset * (@a) to 0 and size (@b) to the previously returned size of the object. + * + * A client may need to present a NULL pointer in the argument + * passed to a trusted application in the TEE. + * This is also a requirement in GlobalPlatform Client API v1.0c + * (section 3.2.5 memory references), which can be found at + * http://www.globalplatform.org/specificationsdevice.asp + * + * If a NULL pointer is passed to a TA in the TEE, the (@c) + * IOCTL parameters value must be set to TEE_MEMREF_NULL indicating a NULL + * memory reference. */ struct tee_ioctl_param { __u64 attr; From 4b38a0a6dadf40f030fd2d6183f1f2afffe5e631 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Mon, 11 May 2020 19:10:48 +0300 Subject: [PATCH 23/37] arm64: dts: hi3798cv200-poplar: memreserve for bootloader Reserve memory for bootloader purposes. Acked-by: Jerome Forissier Signed-off-by: Igor Opaniuk --- arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts index 7d370dac4c8571..ec3b55eb49042d 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts @@ -6,6 +6,7 @@ */ /dts-v1/; +/memreserve/ 0x00000000 0x04080000; #include #include "hi3798cv200.dtsi" From 50c596553cf2f151e4007529d944edbd5a8f991e Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Mon, 11 May 2020 19:09:31 +0300 Subject: [PATCH 24/37] arm64: dts: hi3798cv200-poplar: add optee node Add optee node, so OP-TEE driver is probed properly. Acked-by: Jerome Forissier Signed-off-by: Igor Opaniuk --- arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts index ec3b55eb49042d..3bb161655313c4 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts @@ -71,6 +71,13 @@ gpio = <&gpio6 7 0>; enable-active-high; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; &ehci { From bc1e1358aabfc76704371eee477970c17f82fdb4 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 27 May 2020 10:38:12 +0200 Subject: [PATCH 25/37] ARM: dts: stm32: Define OP-TEE resources on stm32mp15 and enable on DK2 Define OP-TEE firmware node for stm32mp15 based platforms. The node if disable by default. Enable the OP-TEE node and define OP-TEE reserved memory for stm32mp157c-dk2. Signed-off-by: Etienne Carriere [jf: rebase onto v5.9] Signed-off-by: Jerome Forissier --- arch/arm/boot/dts/stm32mp151.dtsi | 8 ++++++++ arch/arm/boot/dts/stm32mp157c-dk2.dts | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp151.dtsi b/arch/arm/boot/dts/stm32mp151.dtsi index bfe29023fbd511..07772510b7afb7 100644 --- a/arch/arm/boot/dts/stm32mp151.dtsi +++ b/arch/arm/boot/dts/stm32mp151.dtsi @@ -28,6 +28,14 @@ method = "smc"; }; + firmware { + optee: optee { + compatible = "linaro,optee-tz"; + method = "smc"; + status = "disabled"; + }; + }; + intc: interrupt-controller@a0021000 { compatible = "arm,cortex-a7-gic"; #interrupt-cells = <3>; diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts index 045636555dddfc..2868dd8b4dd963 100644 --- a/arch/arm/boot/dts/stm32mp157c-dk2.dts +++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts @@ -16,6 +16,13 @@ model = "STMicroelectronics STM32MP157C-DK2 Discovery Board"; compatible = "st,stm32mp157c-dk2", "st,stm32mp157"; + reserved-memory { + optee_memory: optee@0xde000000 { + reg = <0xde000000 0x02000000>; + no-map; + }; + }; + aliases { ethernet0 = ðernet0; serial0 = &uart4; @@ -95,3 +102,7 @@ pinctrl-2 = <&usart2_idle_pins_c>; status = "disabled"; }; + +&optee { + status = "okay"; +}; From d2977196ce200e5855e9ef860ba448a0760ed460 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Fri, 15 Nov 2019 13:55:02 +0100 Subject: [PATCH 26/37] upstream-tee-subsys-patches.txt: patches up to v5.9 Records patches available upstream up to v5.9. Signed-off-by: Jerome Forissier --- upstream-tee-subsys-patches.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt index 5f1df980c11428..551b23fe0cc1ee 100644 --- a/upstream-tee-subsys-patches.txt +++ b/upstream-tee-subsys-patches.txt @@ -5,6 +5,34 @@ This is a list of all the patches that relates to the TEE subsystem. The text inside the brackets are the kernel version where it was introduced, followed by the sha1 hash in the upstream kernel tree. +[v5.9-rc1] 5f178bb71e3a optee: enable support for multi-stage bus enumeration +[v5.9-rc1] 58df195cd47d optee: use uuid for sysfs driver entry +[v5.8-rc1] d8ed45c5dcd4 mmap locking API: use coccinelle to convert mmap_sem rwsem call sites +[v5.8-rc1] 60b4000f5464 tee: fix crypto select +[v5.8-rc1] c5b4312bea5d tee: optee: Add support for session login client UUID generation +[v5.8-rc1] e33bcbab16d1 tee: add support for session's client UUID generation +[v5.8-rc1] 104edb94cc4b tee: add private login method for kernel clients +[v5.8-rc1] 2a6ba3f794e8 tee: enable support to register kernel memory +[v5.8-rc1] 1115899e7aad tee: remove unnecessary NULL check in tee_shm_alloc() +[v5.7-rc1] 758ecf13a41a tee: tee_shm_op_mmap(): use TEE_SHM_USER_MAPPED +[v5.7-rc1] 5271b2011e44 tee: remove redundant teedev in struct tee_shm +[v5.7-rc1] f1bbacedb0af tee: don't assign shm id for private shms +[v5.7-rc1] c180f9bbe29a tee: remove unused tee_shm_priv_alloc() +[v5.7-rc1] 59a135f6fb66 tee: remove linked list of struct tee_shm +[v5.6] 36fa3e50085e tee: amdtee: out of bounds read in find_session() +[v5.6-rc5] 483685bceedb tee: amdtee: fix memory leak in amdtee_open_session() +[v5.6-rc4] 872d92dec353 tee: amdtee: amdtee depends on CRYPTO_DEV_CCP_DD +[v5.6-rc1] 48d625e4c4ce tee: fix memory allocation failure checks on drv_data and amdtee +[v5.6-rc1] 279c075dc1d2 tee: amdtee: remove redundant NULL check for pool +[v5.6-rc1] f9568eae9247 tee: amdtee: rename err label to err_device_unregister +[v5.6-rc1] 2929015535fa tee: amdtee: skip tee_device_unregister if tee_device_alloc fails +[v5.6-rc1] f4c58c3758f9 tee: amdtee: print error message if tee not present +[v5.6-rc1] 5ae63958a6de tee: amdtee: remove unused variable initialization +[v5.6-rc1] bade7e1fbd34 tee: amdtee: check TEE status during driver initialization +[v5.6-rc1] 757cc3e9ff1d tee: add AMD-TEE driver +[v5.6-rc1] 1a74fa3894e7 tee: allow compilation of tee subsystem for AMD CPUs +[v5.6-rc1] f349710e413a optee: model OP-TEE as a platform device/driver +[v5.6-rc1] 42aa7c6eb3eb drm/tee_shm: Drop dma_buf_k(unmap) support [v5.5] 9e0caab8e0f9 tee: optee: Fix compilation issue with nommu [v5.5-rc7] 5a769f6ff439 optee: Fix multi page dynamic shm pool alloc [v5.5-rc1] 03212e347f94 tee: optee: fix device enumeration error handling From a1f8ac03637d0808053975d4870375b084d6ed7c Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 17 Sep 2020 19:10:22 +0530 Subject: [PATCH 27/37] tee: client UUID: Skip REE kernel login method as well Since the addition of session's client UUID generation via commit [1], login via REE kernel method was disallowed. So fix that via passing nill UUID in case of TEE_IOCTL_LOGIN_REE_KERNEL method as well. Fixes: e33bcbab16d1 ("tee: add support for session's client UUID generation") [1] Signed-off-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index c57c85d60d668e..e0674bf29b5a63 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -200,7 +200,8 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, int name_len; int rc; - if (connection_method == TEE_IOCTL_LOGIN_PUBLIC) { + if (connection_method == TEE_IOCTL_LOGIN_PUBLIC || + connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) { /* Nil UUID to be passed to TEE environment */ uuid_copy(uuid, &uuid_null); return 0; From 28676a5aba66dc983023ac3bea45e2aa9a06f5ce Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Thu, 15 Oct 2020 20:11:49 -0700 Subject: [PATCH 28/37] checkpatch: add --kconfig-prefix Kconfig allows to customize the CONFIG_ prefix via the $CONFIG_ environment variable. Out-of-tree projects may therefore use Kconfig with a different prefix, or they may use a custom configuration tool which does not use the CONFIG_ prefix at all. Such projects may still want to adhere to the Linux kernel coding style and run checkpatch.pl. One example is OP-TEE [1] which does not use Kconfig but does have configuration options prefixed with CFG_. It also mostly follows the kernel coding style and therefore being able to use checkpatch is quite valuable. To make this possible, add the --kconfig-prefix command line option. [1] https://github.com/OP-TEE/optee_os Signed-off-by: Jerome Forissier Signed-off-by: Andrew Morton Acked-by: Joe Perches Link: http://lkml.kernel.org/r/20200818081732.800449-1-jerome@forissier.org Signed-off-by: Linus Torvalds [jf: commit 3e89ad8506f3 from upstream] Signed-off-by: Jerome Forissier --- scripts/checkpatch.pl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 504d2e431c6041..9998340d69c676 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -65,6 +65,7 @@ # git output parsing needs US English output, so first set backtick child process LANGUAGE my $git_command ='export LANGUAGE=en_US.UTF-8; git'; my $tabsize = 8; +my ${CONFIG_} = "CONFIG_"; sub help { my ($exitcode) = @_; @@ -127,6 +128,8 @@ sub help { --typedefsfile Read additional types from this file --color[=WHEN] Use colors 'always', 'never', or only when output is a terminal ('auto'). Default is 'auto'. + --kconfig-prefix=WORD use WORD as a prefix for Kconfig symbols (default + ${CONFIG_}) -h, --help, --version display this help and exit When FILE is - read standard input. @@ -235,6 +238,7 @@ sub list_types { 'color=s' => \$color, 'no-color' => \$color, #keep old behaviors of -nocolor 'nocolor' => \$color, #keep old behaviors of -nocolor + 'kconfig-prefix=s' => \${CONFIG_}, 'h|help' => \$help, 'version' => \$help ) or help(1); @@ -6524,16 +6528,16 @@ sub process { } # check for IS_ENABLED() without CONFIG_ ($rawline for comments too) - if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^CONFIG_/) { + if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^${CONFIG_}/) { WARN("IS_ENABLED_CONFIG", - "IS_ENABLED($1) is normally used as IS_ENABLED(CONFIG_$1)\n" . $herecurr); + "IS_ENABLED($1) is normally used as IS_ENABLED(${CONFIG_}$1)\n" . $herecurr); } # check for #if defined CONFIG_ || defined CONFIG__MODULE - if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { + if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(${CONFIG_}[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { my $config = $1; if (WARN("PREFER_IS_ENABLED", - "Prefer IS_ENABLED() to CONFIG_ || CONFIG__MODULE\n" . $herecurr) && + "Prefer IS_ENABLED() to ${CONFIG_} || ${CONFIG_}_MODULE\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; } From 436d15c681f41d15ff26adc5d6bc29c757188185 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Thu, 14 Jan 2021 12:00:15 +0100 Subject: [PATCH 29/37] upstream-tee-subsys-patches.txt: fix wrong commit ID The ID for commit b83685bceedb ("tee: amdtee: fix memory leak in amdtee_open_session()") is wrong in upstream-tee-subsys-patches.txt. Fix it. Reported-by: Victor Chong Signed-off-by: Jerome Forissier --- upstream-tee-subsys-patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upstream-tee-subsys-patches.txt b/upstream-tee-subsys-patches.txt index 551b23fe0cc1ee..5d69a73616e39a 100644 --- a/upstream-tee-subsys-patches.txt +++ b/upstream-tee-subsys-patches.txt @@ -20,7 +20,7 @@ followed by the sha1 hash in the upstream kernel tree. [v5.7-rc1] c180f9bbe29a tee: remove unused tee_shm_priv_alloc() [v5.7-rc1] 59a135f6fb66 tee: remove linked list of struct tee_shm [v5.6] 36fa3e50085e tee: amdtee: out of bounds read in find_session() -[v5.6-rc5] 483685bceedb tee: amdtee: fix memory leak in amdtee_open_session() +[v5.6-rc5] b83685bceedb tee: amdtee: fix memory leak in amdtee_open_session() [v5.6-rc4] 872d92dec353 tee: amdtee: amdtee depends on CRYPTO_DEV_CCP_DD [v5.6-rc1] 48d625e4c4ce tee: fix memory allocation failure checks on drv_data and amdtee [v5.6-rc1] 279c075dc1d2 tee: amdtee: remove redundant NULL check for pool From 4c38674e15da8618dab76f253f3a5d88bfc01cf2 Mon Sep 17 00:00:00 2001 From: Javier Almansa Sobrino Date: Tue, 2 Feb 2021 14:12:08 +0000 Subject: [PATCH 30/37] Enable Microsoft fTPM driver on Arm Foundation v8 models. Signed-off-by: Javier Almansa Sobrino Acked-by: Joakim Bech Link: https://github.com/linaro-swg/linux/pull/85 [jf: not currently intended for upstream; add link to PR] Signed-off-by: Jerome Forissier --- arch/arm64/boot/dts/arm/foundation-v8.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dtsi b/arch/arm64/boot/dts/arm/foundation-v8.dtsi index acbf17520b3043..1754c8981b102c 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dtsi +++ b/arch/arm64/boot/dts/arm/foundation-v8.dtsi @@ -26,6 +26,10 @@ serial3 = &v2m_serial3; }; + ftpm { + compatible = "microsoft,ftpm"; + }; + cpus { #address-cells = <2>; #size-cells = <0>; From 52475481d279598a5c0146878c1982cb7d335983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Cercueil?= Date: Tue, 2 Mar 2021 12:17:04 +0100 Subject: [PATCH 31/37] stm32mp157c-ev1.dts: Fix optee status to 'okay' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optee device status is disabled by default, change its status to 'okay' in the dts of the EV1 board Signed-off-by: Timothée Cercueil --- arch/arm/boot/dts/stm32mp157c-ev1.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts index 85628e16d2d568..fa515206161f7c 100644 --- a/arch/arm/boot/dts/stm32mp157c-ev1.dts +++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts @@ -372,3 +372,7 @@ &usbphyc { status = "okay"; }; + +&optee { + status = "okay"; +}; From 2d3ba7d2cdd8a2aec31cbe260428c9e605a5355a Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 10 Apr 2020 21:14:13 -0700 Subject: [PATCH 32/37] optee: refactor call queue code Move the call queue handling logic off to a new file to allow for its use in other translation units cleanly. Signed-off-by: Hernan Gatta Acked-by: Etienne Carriere --- drivers/tee/optee/Makefile | 1 + drivers/tee/optee/call.c | 85 ------------------------------ drivers/tee/optee/call_queue.c | 86 +++++++++++++++++++++++++++++++ drivers/tee/optee/optee_private.h | 13 +++++ 4 files changed, 100 insertions(+), 85 deletions(-) create mode 100644 drivers/tee/optee/call_queue.c diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 92247436bf856a..725ac111208d3b 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_OPTEE) += optee.o optee-objs += core.o +optee-objs += call_queue.o optee-objs += call.o optee-objs += rpc.o optee-objs += supp.o diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 3945041e661557..bdaf5af51cbce2 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -15,91 +15,6 @@ #include "optee_smc.h" #include "optee_bench.h" -struct optee_call_waiter { - struct list_head list_node; - struct completion c; -}; - -static void optee_cq_wait_init(struct optee_call_queue *cq, - struct optee_call_waiter *w) -{ - /* - * We're preparing to make a call to secure world. In case we can't - * allocate a thread in secure world we'll end up waiting in - * optee_cq_wait_for_completion(). - * - * Normally if there's no contention in secure world the call will - * complete and we can cleanup directly with optee_cq_wait_final(). - */ - mutex_lock(&cq->mutex); - - /* - * We add ourselves to the queue, but we don't wait. This - * guarantees that we don't lose a completion if secure world - * returns busy and another thread just exited and try to complete - * someone. - */ - init_completion(&w->c); - list_add_tail(&w->list_node, &cq->waiters); - - mutex_unlock(&cq->mutex); -} - -static void optee_cq_wait_for_completion(struct optee_call_queue *cq, - struct optee_call_waiter *w) -{ - wait_for_completion(&w->c); - - mutex_lock(&cq->mutex); - - /* Move to end of list to get out of the way for other waiters */ - list_del(&w->list_node); - reinit_completion(&w->c); - list_add_tail(&w->list_node, &cq->waiters); - - mutex_unlock(&cq->mutex); -} - -static void optee_cq_complete_one(struct optee_call_queue *cq) -{ - struct optee_call_waiter *w; - - list_for_each_entry(w, &cq->waiters, list_node) { - if (!completion_done(&w->c)) { - complete(&w->c); - break; - } - } -} - -static void optee_cq_wait_final(struct optee_call_queue *cq, - struct optee_call_waiter *w) -{ - /* - * We're done with the call to secure world. The thread in secure - * world that was used for this call is now available for some - * other task to use. - */ - mutex_lock(&cq->mutex); - - /* Get out of the list */ - list_del(&w->list_node); - - /* Wake up one eventual waiting task */ - optee_cq_complete_one(cq); - - /* - * If we're completed we've got a completion from another task that - * was just done with its call to secure world. Since yet another - * thread now is available in secure world wake up another eventual - * waiting task. - */ - if (completion_done(&w->c)) - optee_cq_complete_one(cq); - - mutex_unlock(&cq->mutex); -} - /* Requires the filpstate mutex to be held */ static struct optee_session *find_session(struct optee_context_data *ctxdata, u32 session_id) diff --git a/drivers/tee/optee/call_queue.c b/drivers/tee/optee/call_queue.c new file mode 100644 index 00000000000000..70922c040fc628 --- /dev/null +++ b/drivers/tee/optee/call_queue.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015, Linaro Limited + */ + +#include "optee_private.h" + +void optee_cq_wait_init(struct optee_call_queue *cq, + struct optee_call_waiter *w) +{ + /* + * We're preparing to make a call to secure world. In case we can't + * allocate a thread in secure world we'll end up waiting in + * optee_cq_wait_for_completion(). + * + * Normally if there's no contention in secure world the call will + * complete and we can cleanup directly with optee_cq_wait_final(). + */ + mutex_lock(&cq->mutex); + + /* + * We add ourselves to the queue, but we don't wait. This + * guarantees that we don't lose a completion if secure world + * returns busy and another thread just exited and try to complete + * someone. + */ + init_completion(&w->c); + list_add_tail(&w->list_node, &cq->waiters); + + mutex_unlock(&cq->mutex); +} + +void optee_cq_wait_for_completion(struct optee_call_queue *cq, + struct optee_call_waiter *w) +{ + wait_for_completion(&w->c); + + mutex_lock(&cq->mutex); + + /* Move to end of list to get out of the way for other waiters */ + list_del(&w->list_node); + reinit_completion(&w->c); + list_add_tail(&w->list_node, &cq->waiters); + + mutex_unlock(&cq->mutex); +} + +void optee_cq_complete_one(struct optee_call_queue *cq) +{ + struct optee_call_waiter *w; + + list_for_each_entry(w, &cq->waiters, list_node) { + if (!completion_done(&w->c)) { + complete(&w->c); + break; + } + } +} + +void optee_cq_wait_final(struct optee_call_queue *cq, + struct optee_call_waiter *w) +{ + /* + * We're done with the call to secure world. The thread in secure + * world that was used for this call is now available for some + * other task to use. + */ + mutex_lock(&cq->mutex); + + /* Get out of the list */ + list_del(&w->list_node); + + /* Wake up one eventual waiting task */ + optee_cq_complete_one(cq); + + /* + * If we're completed we've got a completion from another task that + * was just done with its call to secure world. Since yet another + * thread now is available in secure world wake up another eventual + * waiting task. + */ + if (completion_done(&w->c)) + optee_cq_complete_one(cq); + + mutex_unlock(&cq->mutex); +} diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 8b71839a357ede..9c40bd776755a2 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -119,6 +119,11 @@ struct optee_rpc_param { u32 a7; }; +struct optee_call_waiter { + struct list_head list_node; + struct completion c; +}; + /* Holds context that is preserved during one STD call */ struct optee_call_ctx { /* information about pages list used in last allocation */ @@ -183,6 +188,14 @@ void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, #define PTA_CMD_GET_DEVICES_SUPP 0x1 int optee_enumerate_devices(u32 func); +void optee_cq_wait_init(struct optee_call_queue *cq, + struct optee_call_waiter *w); +void optee_cq_wait_for_completion(struct optee_call_queue *cq, + struct optee_call_waiter *w); +void optee_cq_complete_one(struct optee_call_queue *cq); +void optee_cq_wait_final(struct optee_call_queue *cq, + struct optee_call_waiter *w); + /* * Small helpers */ From 5cc3b41bf29a606396c7c61544a6c5f13c314d5c Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 10 Apr 2020 21:38:43 -0700 Subject: [PATCH 33/37] tee: add support for supplicant-style memory registration Supporting OCALLs, where a TA may request a function invocation on its Client Application (CA), requires that the secure thread requesting the OCALL be temporarily suspended as a result of performing an RPC to send the OCALL request to normal world. A possible OCALL request is one where the TA requests that the CA allocate shared memory in order to pass large buffers as part of the function invocation. When this happens, the request is routed to the CA. Normally, this would result in another entry into secure world to register that shared memory, if OP-TEE supports it. This re-entry would block, however, if OP-TEE only has one thread available. To avoid this problem, the TEE supplicant readily supports registering shared memory only with the driver, and passing the necessary information back to OP-TEE via RPC parameters instead. This change introduces the TEE_IOCTL_SHM_OCALL flag. When used, the call into OP-TEE to register shared memory is not performed, nor is its corresponding free. It is up to the OCALL implementation to properly deal with reference counting. Signed-off-by: Hernan Gatta Reviewed-by: Jens Wiklander --- drivers/tee/optee/call.c | 6 +++ drivers/tee/optee/rpc.c | 101 +++++++++++++++++++++++---------------- drivers/tee/tee_core.c | 10 ++-- drivers/tee/tee_shm.c | 3 +- include/linux/tee_drv.h | 1 + include/uapi/linux/tee.h | 2 + 6 files changed, 77 insertions(+), 46 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index bdaf5af51cbce2..49cc9b0dc13abf 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -503,6 +503,9 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, phys_addr_t msg_parg; int rc; + if (shm->flags & TEE_SHM_OCALL) + return 0; + if (!num_pages) return -EINVAL; @@ -552,6 +555,9 @@ int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) phys_addr_t msg_parg; int rc = 0; + if (shm->flags & TEE_SHM_OCALL) + return 0; + shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); if (IS_ERR(shm_arg)) return PTR_ERR(shm_arg); diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 817b1caee28c3e..6ac3058c7d0957 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -192,14 +192,67 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) return shm; } +int optee_rpc_process_shm_alloc(struct tee_shm *shm, + struct optee_msg_param *msg_param, void **list) +{ + phys_addr_t pa; + size_t sz; + size_t offs; + struct page **pages; + size_t page_num; + void *pages_list; + + if (tee_shm_get_pa(shm, 0, &pa)) + return -EINVAL; + + sz = tee_shm_get_size(shm); + + if (tee_shm_is_registered(shm)) { + pages = tee_shm_get_pages(shm, &page_num); + if (!pages || !page_num) + return -EINVAL; + + offs = tee_shm_get_page_offset(shm); + if (offs >= OPTEE_MSG_NONCONTIG_PAGE_SIZE) + return -EINVAL; + + pages_list = optee_allocate_pages_list(page_num); + if (!pages_list) + return -ENOMEM; + + /* + * In the least bits of u.tmem.buf_ptr we store buffer offset + * from 4k page, as described in OP-TEE ABI. + */ + + msg_param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | + OPTEE_MSG_ATTR_NONCONTIG; + msg_param->u.tmem.buf_ptr = virt_to_phys(pages_list) | offs; + msg_param->u.tmem.size = sz; + msg_param->u.tmem.shm_ref = (unsigned long)shm; + optee_fill_pages_list(pages_list, pages, page_num, + tee_shm_get_page_offset(shm)); + *list = pages_list; + } else { + msg_param->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; + msg_param->u.tmem.buf_ptr = pa; + msg_param->u.tmem.size = sz; + msg_param->u.tmem.shm_ref = (unsigned long)shm; + *list = NULL; + } + + return 0; +} + static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, struct optee_msg_arg *arg, struct optee_call_ctx *call_ctx) { - phys_addr_t pa; struct tee_shm *shm; + void *pages_list; size_t sz; size_t n; + int rc; arg->ret_origin = TEEC_ORIGIN_COMMS; @@ -237,52 +290,18 @@ static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, return; } - if (tee_shm_get_pa(shm, 0, &pa)) { + rc = optee_rpc_process_shm_alloc(shm, arg->params, &pages_list); + if (rc == -ENOMEM) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + goto bad; + } else if (rc) { arg->ret = TEEC_ERROR_BAD_PARAMETERS; goto bad; } - sz = tee_shm_get_size(shm); - if (tee_shm_is_registered(shm)) { - struct page **pages; - u64 *pages_list; - size_t page_num; - - pages = tee_shm_get_pages(shm, &page_num); - if (!pages || !page_num) { - arg->ret = TEEC_ERROR_OUT_OF_MEMORY; - goto bad; - } - - pages_list = optee_allocate_pages_list(page_num); - if (!pages_list) { - arg->ret = TEEC_ERROR_OUT_OF_MEMORY; - goto bad; - } - call_ctx->pages_list = pages_list; - call_ctx->num_entries = page_num; - - arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | - OPTEE_MSG_ATTR_NONCONTIG; - /* - * In the least bits of u.tmem.buf_ptr we store buffer offset - * from 4k page, as described in OP-TEE ABI. - */ - arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) | - (tee_shm_get_page_offset(shm) & - (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); - arg->params[0].u.tmem.size = tee_shm_get_size(shm); - arg->params[0].u.tmem.shm_ref = (unsigned long)shm; - - optee_fill_pages_list(pages_list, pages, page_num, - tee_shm_get_page_offset(shm)); - } else { - arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; - arg->params[0].u.tmem.buf_ptr = pa; - arg->params[0].u.tmem.size = sz; - arg->params[0].u.tmem.shm_ref = (unsigned long)shm; + call_ctx->num_entries = shm->num_pages; } arg->ret = TEEC_SUCCESS; diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index e0674bf29b5a63..5234323de456c1 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -320,18 +320,20 @@ tee_ioctl_shm_register(struct tee_context *ctx, struct tee_ioctl_shm_register_data __user *udata) { long ret; + u32 flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED; struct tee_ioctl_shm_register_data data; struct tee_shm *shm; if (copy_from_user(&data, udata, sizeof(data))) return -EFAULT; - /* Currently no input flags are supported */ - if (data.flags) + if (data.flags & ~TEE_IOCTL_SHM_OCALL) return -EINVAL; - shm = tee_shm_register(ctx, data.addr, data.length, - TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED); + if (data.flags & TEE_IOCTL_SHM_OCALL) + flags |= TEE_SHM_OCALL; + + shm = tee_shm_register(ctx, data.addr, data.length, flags); if (IS_ERR(shm)) return PTR_ERR(shm); diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 7cf16603e26ba6..5443ea5b8ccd1e 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -208,7 +208,8 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, int num_pages; unsigned long start; - if (flags != req_user_flags && flags != req_kernel_flags) + if (((flags & req_user_flags) != req_user_flags) && + ((flags & req_kernel_flags) != req_kernel_flags)) return ERR_PTR(-ENOTSUPP); if (!tee_device_get(teedev)) diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index aed7390899689e..92d861317839b4 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -27,6 +27,7 @@ #define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */ #define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */ #define TEE_SHM_KERNEL_MAPPED BIT(6) /* Memory mapped in kernel space */ +#define TEE_SHM_OCALL BIT(7) /* Memory used for an OCALL */ struct device; struct tee_device; diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 9eed9deddcc672..3dfd274062c018 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -43,8 +43,10 @@ #define TEE_IOC_BASE 0 /* Flags relating to shared memory */ +#define TEE_IOCTL_SHM_NONE 0x0 /* no flags */ #define TEE_IOCTL_SHM_MAPPED 0x1 /* memory mapped in normal world */ #define TEE_IOCTL_SHM_DMA_BUF 0x2 /* dma-buf handle on shared memory */ +#define TEE_IOCTL_SHM_OCALL 0x4 /* memory used for an OCALL */ #define TEE_MAX_ARG_SIZE 1024 From ff602f2b09a805f809a5107d0260e643f7450cdd Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 10 Apr 2020 21:42:49 -0700 Subject: [PATCH 34/37] tee: optee: detect and report OCALL support Add a new generic capability to denote support for OCALLs in secure world: TEE_GEN_CAP_OCALL. Additionally, support detection of this capability in OP-TEE. Signed-off-by: Hernan Gatta Reviewed-by: Jens Wiklander Acked-by: Etienne Carriere --- drivers/tee/optee/core.c | 2 ++ drivers/tee/optee/optee_smc.h | 3 +++ include/uapi/linux/tee.h | 1 + 3 files changed, 6 insertions(+) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index f4846c6a7ee79d..df22fcd86092d5 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -219,6 +219,8 @@ static void optee_get_version(struct tee_device *teedev, v.gen_caps |= TEE_GEN_CAP_REG_MEM; if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_OCALL) + v.gen_caps |= TEE_GEN_CAP_OCALL; *vers = v; } diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 777ad54d4c2c20..689c05543bd2c1 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -218,6 +218,9 @@ struct optee_smc_get_shm_config_result { /* Secure world supports Shared Memory with a NULL buffer reference */ #define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) +/* Secure world is built with OCALL support */ +#define OPTEE_SMC_SEC_CAP_OCALL BIT(5) + #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 3dfd274062c018..083a1b81b4c62d 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -54,6 +54,7 @@ #define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */ #define TEE_GEN_CAP_REG_MEM (1 << 2)/* Supports registering shared memory */ #define TEE_GEN_CAP_MEMREF_NULL (1 << 3)/* NULL MemRef support */ +#define TEE_GEN_CAP_OCALL (1 << 4)/* Supports calls from TA to CA */ #define TEE_MEMREF_NULL (__u64)(-1) /* NULL MemRef Buffer */ From be70804f684350a1a23db13b83fb8007a5682d3b Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 10 Apr 2020 21:46:23 -0700 Subject: [PATCH 35/37] tee: add helper method to increase the refcount on an SHM At times, it may be useful to allow a caller to increase the reference count on a shared memory (SHM) object at will. This patch adds tee_shm_get, the increasing counterpart to tee_shm_put. Signed-off-by: Hernan Gatta Reviewed-by: Jens Wiklander Acked-by: Etienne Carriere --- drivers/tee/tee_shm.c | 11 +++++++++++ include/linux/tee_drv.h | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 5443ea5b8ccd1e..82bf8b1cd1d29b 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -571,6 +571,17 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) } EXPORT_SYMBOL_GPL(tee_shm_get_from_id); +/** + * tee_shm_get() - Increase reference count on a shared memory handle + * @shm: Shared memory handle + */ +void tee_shm_get(struct tee_shm *shm) +{ + if (shm->flags & TEE_SHM_DMA_BUF) + get_dma_buf(shm->dmabuf); +} +EXPORT_SYMBOL_GPL(tee_shm_get); + /** * tee_shm_put() - Decrease reference count on a shared memory handle * @shm: Shared memory handle diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 92d861317839b4..9ddb3010b4d2b4 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -372,6 +372,12 @@ static inline bool tee_shm_is_registered(struct tee_shm *shm) */ void tee_shm_free(struct tee_shm *shm); +/** + * tee_shm_get() - Increase reference count on a shared memory handle + * @shm: Shared memory handle + */ +void tee_shm_get(struct tee_shm *shm); + /** * tee_shm_put() - Decrease reference count on a shared memory handle * @shm: Shared memory handle From 647242cab3f23118ded21ca81b3529afa363e9dc Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Sun, 18 Oct 2020 20:06:01 -0700 Subject: [PATCH 36/37] tee: OCALL support Enable Trusted Applications (TAs) to invoke functions on their corresponding Client Application (CA), both during session open and function invocation. These function invocations from TA to CA are referred to as "Out Calls", or OCALLs for short. The fundamental mechanism is one whereby upon a function invocation from the CA to the TA, the TEE returns prematurely from the invocation with an RPC. This RPC is generated after a TA calls the TEEC_InvokeCommand equivalent function in secure world. The RPC carries information describing the OCALL as well as its parameters. When this happens, the driver saves the state of the current call and returns to user-mode. The TEE Client API will have invoked the TEE_IOC_INVOKE IOCTL with a special parameter that carries OCALL information. When the IOCTL returns prematurely, this parameter includes information about what the CA is expected to do on behalf of the TA along with data to be used to reply to the request. The TEE Client API dispatches the request accordingly to the CA proper. Once that is done, the TEE Client API calls the TEE_IOC_INVOKE IOCTL again with the modified OCALL parameter and associated information (such as the result of the OCALL, and the parameters, as requested by the TA). The driver notices that this invocation is in fact a resumption as opposed to a brand-new invocation, and resumes the secure world thread that sent the RPC in the first place. The same mechanism applies to OCALLs during session open. This patch also minimally updates the OP-TEE and AMD TEE drivers to match the new signatures for session open and invoke. If an OCALL is specified by the CA, EOPNOTSUPP is returned. Signed-off-by: Hernan Gatta --- drivers/tee/amdtee/amdtee_private.h | 8 +- drivers/tee/amdtee/core.c | 23 +- drivers/tee/optee/call.c | 28 ++- drivers/tee/optee/core.c | 1 + drivers/tee/optee/optee_private.h | 6 +- drivers/tee/tee_core.c | 362 ++++++++++++++++++++++++---- include/linux/tee_drv.h | 37 +-- include/uapi/linux/tee.h | 55 ++++- 8 files changed, 439 insertions(+), 81 deletions(-) diff --git a/drivers/tee/amdtee/amdtee_private.h b/drivers/tee/amdtee/amdtee_private.h index d7f798c3394bc7..a837676d1866ac 100644 --- a/drivers/tee/amdtee/amdtee_private.h +++ b/drivers/tee/amdtee/amdtee_private.h @@ -122,13 +122,17 @@ static inline u32 get_session_index(u32 session) int amdtee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, - struct tee_param *param); + struct tee_param *normal_param, + u32 num_normal_params, + struct tee_param *ocall_param); int amdtee_close_session(struct tee_context *ctx, u32 session); int amdtee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param); + struct tee_param *normal_param, + u32 num_normal_params, + struct tee_param *ocall_param); int amdtee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); diff --git a/drivers/tee/amdtee/core.c b/drivers/tee/amdtee/core.c index 27b4cd77d0db6f..4ff958b1b931a1 100644 --- a/drivers/tee/amdtee/core.c +++ b/drivers/tee/amdtee/core.c @@ -230,7 +230,9 @@ static void destroy_session(struct kref *ref) int amdtee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, - struct tee_param *param) + struct tee_param *normal_param, + u32 num_normal_params, + struct tee_param *ocall_param) { struct amdtee_context_data *ctxdata = ctx->data; struct amdtee_session *sess = NULL; @@ -239,6 +241,11 @@ int amdtee_open_session(struct tee_context *ctx, int rc, i; void *ta; + if (ocall_param) { + pr_err("OCALLs not supported\n"); + return -EOPNOTSUPP; + } + if (arg->clnt_login != TEE_IOCTL_LOGIN_PUBLIC) { pr_err("unsupported client login method\n"); return -EINVAL; @@ -279,7 +286,7 @@ int amdtee_open_session(struct tee_context *ctx, } /* Open session with loaded TA */ - handle_open_session(arg, &session_info, param); + handle_open_session(arg, &session_info, normal_param); if (arg->ret != TEEC_SUCCESS) { pr_err("open_session failed %d\n", arg->ret); spin_lock(&sess->lock); @@ -391,12 +398,19 @@ void amdtee_unmap_shmem(struct tee_shm *shm) int amdtee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param) + struct tee_param *normal_param, + u32 num_normal_params, + struct tee_param *ocall_param) { struct amdtee_context_data *ctxdata = ctx->data; struct amdtee_session *sess; u32 i, session_info; + if (ocall_param) { + pr_err("OCALLs not supported\n"); + return -EOPNOTSUPP; + } + /* Check that the session is valid */ mutex_lock(&session_list_mutex); sess = find_session(ctxdata, arg->session); @@ -409,7 +423,7 @@ int amdtee_invoke_func(struct tee_context *ctx, if (!sess) return -EINVAL; - handle_invoke_cmd(arg, session_info, param); + handle_invoke_cmd(arg, session_info, normal_param); return 0; } @@ -422,6 +436,7 @@ int amdtee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) static const struct tee_driver_ops amdtee_ops = { .get_version = amdtee_get_version, .open = amdtee_open, + .pre_release = NULL, .release = amdtee_release, .open_session = amdtee_open_session, .close_session = amdtee_close_session, diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 49cc9b0dc13abf..24b5153db47ea6 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -127,7 +127,9 @@ static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, - struct tee_param *param) + struct tee_param *normal_param, + u32 num_normal_params, + struct tee_param *ocall_param) { struct optee_context_data *ctxdata = ctx->data; int rc; @@ -136,6 +138,11 @@ int optee_open_session(struct tee_context *ctx, phys_addr_t msg_parg; struct optee_session *sess = NULL; + if (ocall_param) { + pr_err("OCALLs not supported\n"); + return -EOPNOTSUPP; + } + /* +2 for the meta parameters added below */ shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg); if (IS_ERR(shm)) @@ -160,7 +167,8 @@ int optee_open_session(struct tee_context *ctx, if (rc) goto out; - rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param); + rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, + normal_param); if (rc) goto out; @@ -185,7 +193,8 @@ int optee_open_session(struct tee_context *ctx, kfree(sess); } - if (optee_from_msg_param(param, arg->num_params, msg_arg->params + 2)) { + if (optee_from_msg_param(normal_param, arg->num_params, + msg_arg->params + 2)) { arg->ret = TEEC_ERROR_COMMUNICATION; arg->ret_origin = TEEC_ORIGIN_COMMS; /* Close session again to avoid leakage */ @@ -232,7 +241,8 @@ int optee_close_session(struct tee_context *ctx, u32 session) } int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param) + struct tee_param *normal_param, u32 num_normal_params, + struct tee_param *ocall_param) { struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; @@ -241,6 +251,11 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct optee_session *sess; int rc; + if (ocall_param) { + pr_err("OCALLs not supported\n"); + return -EOPNOTSUPP; + } + /* Check that the session is valid */ mutex_lock(&ctxdata->mutex); sess = find_session(ctxdata, arg->session); @@ -256,7 +271,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, msg_arg->session = arg->session; msg_arg->cancel_id = arg->cancel_id; - rc = optee_to_msg_param(msg_arg->params, arg->num_params, param); + rc = optee_to_msg_param(msg_arg->params, arg->num_params, normal_param); if (rc) goto out; @@ -265,7 +280,8 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } - if (optee_from_msg_param(param, arg->num_params, msg_arg->params)) { + if (optee_from_msg_param(normal_param, arg->num_params, + msg_arg->params)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index df22fcd86092d5..c2fae61401ecb9 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -334,6 +334,7 @@ static void optee_release(struct tee_context *ctx) static const struct tee_driver_ops optee_ops = { .get_version = optee_get_version, .open = optee_open, + .pre_release = NULL, .release = optee_release, .open_session = optee_open_session, .close_session = optee_close_session, diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 9c40bd776755a2..53ccc89c3e7663 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -155,10 +155,12 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, - struct tee_param *param); + struct tee_param *normal_param, u32 num_normal_params, + struct tee_param *ocall_param); int optee_close_session(struct tee_context *ctx, u32 session); int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param); + struct tee_param *normal_param, u32 num_normal_params, + struct tee_param *ocall_param); int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); void optee_enable_shm_cache(struct optee *optee); diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 5234323de456c1..42b59375974af7 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -98,6 +98,8 @@ void teedev_ctx_put(struct tee_context *ctx) static void teedev_close_context(struct tee_context *ctx) { + if (ctx->teedev->desc->ops->pre_release) + ctx->teedev->desc->ops->pre_release(ctx); tee_device_put(ctx->teedev); teedev_ctx_put(ctx); } @@ -390,9 +392,81 @@ static int tee_ioctl_shm_register_fd(struct tee_context *ctx, return ret; } -static int params_from_user(struct tee_context *ctx, struct tee_param *params, - size_t num_params, - struct tee_ioctl_param __user *uparams) +static bool param_is_ocall_reply(struct tee_ioctl_param *param) +{ + u64 type = param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK; + + return param->attr & TEE_IOCTL_PARAM_ATTR_OCALL && + type == TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT && + param->a; +} + +static bool param_is_ocall_request(struct tee_param *param) +{ + u64 type = param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK; + + return param->attr & TEE_IOCTL_PARAM_ATTR_OCALL && + type == TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT && + param->u.value.a; +} + +static bool param_is_ocall_request_safe(struct tee_param *param) +{ + return param ? param_is_ocall_request(param) : false; +} + +static bool param_is_ocall(struct tee_param *param) +{ + u64 type = param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK; + + return param->attr & TEE_IOCTL_PARAM_ATTR_OCALL && + type == TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; +} + +static struct tee_shm *shm_from_user(struct tee_context *ctx, + struct tee_ioctl_param *ip) +{ + struct tee_shm *shm = ERR_PTR(-EINVAL); + + /* + * If a NULL pointer is passed to a TA in the TEE, + * the ip->c IOCTL parameters is set to TEE_MEMREF_NULL + * indicating a NULL memory reference. + */ + if (ip->c != TEE_MEMREF_NULL) { + /* + * If we fail to get a pointer to a shared + * memory object (and increase the ref count) + * from an identifier we return an error. All + * pointers that has been added in params have + * an increased ref count. It's the callers + * responibility to do tee_shm_put() on all + * resolved pointers. + */ + shm = tee_shm_get_from_id(ctx, ip->c); + if (IS_ERR(shm)) + return shm; + + /* + * Ensure offset + size does not overflow + * offset and does not overflow the size of + * the referred shared memory object. + */ + if ((ip->a + ip->b) < ip->a || (ip->a + ip->b) > shm->size) { + tee_shm_put(shm); + return ERR_PTR(-EINVAL); + } + } else if (ctx->cap_memref_null) { + /* Pass NULL pointer to OP-TEE */ + shm = NULL; + } + + return shm; +} + +static int params_from_user_normal(struct tee_context *ctx, + struct tee_param *params, size_t num_params, + struct tee_ioctl_param __user *uparams) { size_t n; @@ -421,41 +495,63 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + shm = shm_from_user(ctx, &ip); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + params[n].u.memref.shm_offs = ip.a; + params[n].u.memref.size = ip.b; + params[n].u.memref.shm = shm; + break; + default: + /* Unknown attribute */ + return -EINVAL; + } + } + return 0; +} + +static int params_from_user_ocall(struct tee_context *ctx, + struct tee_param *params, size_t num_params, + struct tee_ioctl_param __user *uparams) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_shm *shm; + struct tee_ioctl_param ip; + + if (copy_from_user(&ip, uparams + n, sizeof(ip))) + return -EFAULT; + + /* All unused attribute bits has to be zero */ + if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) + return -EINVAL; + + params[n].attr = ip.attr; + switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { + case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + params[n].u.value.a = ip.a; + params[n].u.value.b = ip.b; + params[n].u.value.c = ip.c; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + shm = shm_from_user(ctx, &ip); + if (IS_ERR(shm)) + return PTR_ERR(shm); + /* - * If a NULL pointer is passed to a TA in the TEE, - * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL - * indicating a NULL memory reference. + * Reference counting for OCALL memref parameters is + * handled by the TEE-specific driver as necessary. */ - if (ip.c != TEE_MEMREF_NULL) { - /* - * If we fail to get a pointer to a shared - * memory object (and increase the ref count) - * from an identifier we return an error. All - * pointers that has been added in params have - * an increased ref count. It's the callers - * responibility to do tee_shm_put() on all - * resolved pointers. - */ - shm = tee_shm_get_from_id(ctx, ip.c); - if (IS_ERR(shm)) - return PTR_ERR(shm); - - /* - * Ensure offset + size does not overflow - * offset and does not overflow the size of - * the referred shared memory object. - */ - if ((ip.a + ip.b) < ip.a || - (ip.a + ip.b) > shm->size) { - tee_shm_put(shm); - return -EINVAL; - } - } else if (ctx->cap_memref_null) { - /* Pass NULL pointer to OP-TEE */ - shm = NULL; - } else { - return -EINVAL; - } + if (shm) + tee_shm_put(shm); params[n].u.memref.shm_offs = ip.a; params[n].u.memref.size = ip.b; @@ -469,8 +565,25 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, return 0; } -static int params_to_user(struct tee_ioctl_param __user *uparams, - size_t num_params, struct tee_param *params) +static int params_from_user(struct tee_context *ctx, struct tee_param *params, + size_t num_params, + struct tee_ioctl_param __user *uparams) +{ + struct tee_ioctl_param ip; + + if (!num_params) + return 0; + + if (copy_from_user(&ip, uparams, sizeof(ip))) + return -EFAULT; + + return param_is_ocall_reply(&ip) + ? params_from_user_ocall(ctx, params, num_params, uparams) + : params_from_user_normal(ctx, params, num_params, uparams); +} + +static int params_to_user_normal(struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) { size_t n; @@ -478,7 +591,7 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, struct tee_ioctl_param __user *up = uparams + n; struct tee_param *p = params + n; - switch (p->attr) { + switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: if (put_user(p->u.value.a, &up->a) || @@ -497,6 +610,87 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, return 0; } +static int params_to_user_ocall(struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_ioctl_param __user *up = uparams + n; + struct tee_param *p = params + n; + + if (put_user(p->attr, &up->attr)) + return -EFAULT; + + switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + if (put_user(p->u.value.a, &up->a) || + put_user(p->u.value.b, &up->b) || + put_user(p->u.value.c, &up->c)) + return -EFAULT; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + if (p->u.memref.shm) { + if ((put_user((u64)p->u.memref.shm_offs, + &up->a) || + put_user((u64)p->u.memref.size, &up->b) || + put_user(p->u.memref.shm->id, &up->c))) + return -EFAULT; + } else { + if (put_user(0, &up->a) || + put_user(0, &up->b) || + put_user(TEE_MEMREF_NULL, &up->c)) + return -EFAULT; + } + break; + default: + break; + } + } + return 0; +} + +static int params_to_user(struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) +{ + if (!num_params) + return 0; + + return param_is_ocall_request(params) + ? params_to_user_ocall(uparams, num_params, params) + : params_to_user_normal(uparams, num_params, params); +} + +static inline int find_ocall_param(struct tee_param *params, u32 num_params, + struct tee_param **normal_params, + u32 *num_normal_params, + struct tee_param **ocall_param) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + if (param_is_ocall(params + n)) { + if (n == 0) { + *normal_params = params + 1; + *num_normal_params = num_params - 1; + *ocall_param = params; + return 0; + } else { + return -EINVAL; + } + } + } + + *normal_params = params; + *num_normal_params = num_params; + *ocall_param = NULL; + + return 0; +} + static int tee_ioctl_open_session(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { @@ -507,6 +701,9 @@ static int tee_ioctl_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg arg; struct tee_ioctl_param __user *uparams = NULL; struct tee_param *params = NULL; + struct tee_param *ocall_param = NULL; + struct tee_param *normal_params = NULL; + u32 num_normal_params = 0; bool have_session = false; if (!ctx->teedev->desc->ops->open_session) @@ -535,6 +732,10 @@ static int tee_ioctl_open_session(struct tee_context *ctx, rc = params_from_user(ctx, params, arg.num_params, uparams); if (rc) goto out; + rc = find_ocall_param(params, arg.num_params, &normal_params, + &num_normal_params, &ocall_param); + if (rc) + goto out; } if (arg.clnt_login >= TEE_IOCTL_LOGIN_REE_KERNEL_MIN && @@ -544,7 +745,9 @@ static int tee_ioctl_open_session(struct tee_context *ctx, goto out; } - rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params); + rc = ctx->teedev->desc->ops->open_session(ctx, &arg, normal_params, + num_normal_params, + ocall_param); if (rc) goto out; have_session = true; @@ -566,10 +769,11 @@ static int tee_ioctl_open_session(struct tee_context *ctx, if (params) { /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); + if (!param_is_ocall_request_safe(ocall_param)) + for (n = 0; n < arg.num_params; n++) + if (tee_param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); kfree(params); } @@ -586,6 +790,9 @@ static int tee_ioctl_invoke(struct tee_context *ctx, struct tee_ioctl_invoke_arg arg; struct tee_ioctl_param __user *uparams = NULL; struct tee_param *params = NULL; + struct tee_param *ocall_param = NULL; + struct tee_param *normal_params = NULL; + u32 num_normal_params = 0; if (!ctx->teedev->desc->ops->invoke_func) return -EINVAL; @@ -613,9 +820,23 @@ static int tee_ioctl_invoke(struct tee_context *ctx, rc = params_from_user(ctx, params, arg.num_params, uparams); if (rc) goto out; + + /* + * The OCALL parameter must be first so that we know how to + * process the remainder of the parameters, or not be present at + * all. This function returns an error if the OCALL parameter is + * found in the wrong place. If it is not found, 'ocall_param' + * remains NULL. + */ + rc = find_ocall_param(params, arg.num_params, &normal_params, + &num_normal_params, &ocall_param); + if (rc) + goto out; } - rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params); + rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, normal_params, + num_normal_params, + ocall_param); if (rc) goto out; @@ -627,11 +848,22 @@ static int tee_ioctl_invoke(struct tee_context *ctx, rc = params_to_user(uparams, arg.num_params, params); out: if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); + /* + * Decrease the ref count for all valid shared memory pointers + * if this is a normal return. If returning with an OCALL + * request, the parameters should have been overwritten with + * those of the OCALL. The original parameters, and thus the + * memrefs carrying the SHMs whose ref count was increased on + * entry, shall be restored once the full OCALL sequence is + * finished. When that happens, we decrease the ref count on + * them. Otherwise, we leave the SHMs be; the TEE-specific + * driver should have dealt with their ref counts already. + */ + if (!param_is_ocall_request_safe(ocall_param)) + for (n = 0; n < arg.num_params; n++) + if (tee_param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); kfree(params); } return rc; @@ -1220,9 +1452,22 @@ int tee_client_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param) { + struct tee_param *ocall_param = NULL; + struct tee_param *normal_params = NULL; + u32 num_normal_params = 0; + int rc; + if (!ctx->teedev->desc->ops->open_session) return -EINVAL; - return ctx->teedev->desc->ops->open_session(ctx, arg, param); + + rc = find_ocall_param(param, arg->num_params, &normal_params, + &num_normal_params, &ocall_param); + if (rc) + return rc; + + return ctx->teedev->desc->ops->open_session(ctx, arg, normal_params, + num_normal_params, + ocall_param); } EXPORT_SYMBOL_GPL(tee_client_open_session); @@ -1238,9 +1483,22 @@ int tee_client_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param) { + struct tee_param *ocall_param = NULL; + struct tee_param *normal_params = NULL; + u32 num_normal_params = 0; + int rc; + if (!ctx->teedev->desc->ops->invoke_func) return -EINVAL; - return ctx->teedev->desc->ops->invoke_func(ctx, arg, param); + + rc = find_ocall_param(param, arg->num_params, &normal_params, + &num_normal_params, &ocall_param); + if (rc) + return rc; + + return ctx->teedev->desc->ops->invoke_func(ctx, arg, normal_params, + num_normal_params, + ocall_param); } EXPORT_SYMBOL_GPL(tee_client_invoke_func); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 9ddb3010b4d2b4..ef141997c7cd39 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -36,20 +36,22 @@ struct tee_shm_pool; /** * struct tee_context - driver specific context on file pointer data - * @teedev: pointer to this drivers struct tee_device - * @list_shm: List of shared memory object owned by this context - * @data: driver specific context data, managed by the driver - * @refcount: reference counter for this structure - * @releasing: flag that indicates if context is being released right now. - * It is needed to break circular dependency on context during - * shared memory release. - * @supp_nowait: flag that indicates that requests in this context should not - * wait for tee-supplicant daemon to be started if not present - * and just return with an error code. It is needed for requests - * that arises from TEE based kernel drivers that should be - * non-blocking in nature. + * @teedev: pointer to this drivers struct tee_device + * @list_shm: List of shared memory object owned by this context + * @data: driver specific context data, managed by the driver + * @refcount: reference counter for this structure + * @releasing: flag that indicates if context is being released right now. + * It is needed to break circular dependency on context during + * shared memory release. + * @supp_nowait: flag that indicates that requests in this context should + * not wait for tee-supplicant daemon to be started if not + * present and just return with an error code. It is needed + * for requests that arises from TEE based kernel drivers that + * should be non-blocking in nature. * @cap_memref_null: flag indicating if the TEE Client support shared * memory buffer with a NULL pointer. + * @cap_ocall: flag indicating that OP-TEE supports OCALLs, allowing TAs + * to invoke commands on their CA. */ struct tee_context { struct tee_device *teedev; @@ -58,6 +60,7 @@ struct tee_context { bool releasing; bool supp_nowait; bool cap_memref_null; + bool cap_ocall; }; struct tee_param_memref { @@ -84,6 +87,7 @@ struct tee_param { * struct tee_driver_ops - driver operations vtable * @get_version: returns version of driver * @open: called when the device file is opened + * @pre_release: called prior to context release, before release proper * @release: release this open file * @open_session: open a new session * @close_session: close a session @@ -98,14 +102,19 @@ struct tee_driver_ops { void (*get_version)(struct tee_device *teedev, struct tee_ioctl_version_data *vers); int (*open)(struct tee_context *ctx); + void (*pre_release)(struct tee_context *ctx); void (*release)(struct tee_context *ctx); int (*open_session)(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, - struct tee_param *param); + struct tee_param *normal_param, + u32 num_normal_params, + struct tee_param *ocall_param); int (*close_session)(struct tee_context *ctx, u32 session); int (*invoke_func)(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param); + struct tee_param *normal_param, + u32 num_normal_params, + struct tee_param *ocall_param); int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session); int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_param *param); diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 083a1b81b4c62d..54faa0c23418c5 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -194,9 +194,14 @@ struct tee_ioctl_buf_data { /* Meta parameter carrying extra information about the message. */ #define TEE_IOCTL_PARAM_ATTR_META 0x100 +/* Parameter carrying information about an OCALL reply or request. */ +#define TEE_IOCTL_PARAM_ATTR_OCALL 0x200 + /* Mask of all known attr bits */ #define TEE_IOCTL_PARAM_ATTR_MASK \ - (TEE_IOCTL_PARAM_ATTR_TYPE_MASK | TEE_IOCTL_PARAM_ATTR_META) + (TEE_IOCTL_PARAM_ATTR_TYPE_MASK | \ + TEE_IOCTL_PARAM_ATTR_META | \ + TEE_IOCTL_PARAM_ATTR_OCALL) /* * Matches TEEC_LOGIN_* in GP TEE Client API @@ -289,6 +294,54 @@ struct tee_ioctl_open_session_arg { #define TEE_IOC_OPEN_SESSION _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 2, \ struct tee_ioctl_buf_data) +/* + * Command sent to the CA to request allocation of shared memory to carry the + * parameters of an OCALL + * + * [in] param[0].u.value.b requested memory size + * [out] param[0].u.value.c SHM ID + * + * Note: [in] means from driver to CA, [out], from CA to driver. + */ +#define TEE_IOCTL_OCALL_CMD_SHM_ALLOC 1 + +/* + * Command sent to the CA to free previously allocated shared memory. + * + * [in] param[0].u.value.c SHM ID + * + * Note: [in] means from driver to CA. + */ +#define TEE_IOCTL_OCALL_CMD_SHM_FREE 2 + +/* + * Command sent to the CA to execute an OCALL by Id. + * + * [any] param[0..3].u.* carry OCALL parameters + */ +#define TEE_IOCTL_OCALL_CMD_INVOKE 3 + +/* + * Join the Id of the function that the TEE Client API must execute on behalf of + * the CA with the Id of the command that the CA must execute + * + * As an example, TEE_IOCTL_OCALL_MAKE_PAIR(TEE_IOCTL_OCALL_CMD_INVOKE, 10) + * means that the Client API must forward a function invocation to a CA-provided + * handler, and the handler must execute command Id '10', whose meaning is up to + * the user-defined contract between the CA & TA. + */ +#define TEE_IOCTL_OCALL_MAKE_PAIR(func, cmd) \ + (((__u64)(func) << 32) | (__u32)(cmd)) + +/* + * Get the Id of the function that the TEE Client API must execute on behalf of + * the CA + */ +#define TEE_IOCTL_OCALL_GET_FUNC(x) ((__u32)((x) >> 32)) + +/* Get the Id of the command that the CA must execute */ +#define TEE_IOCTL_OCALL_GET_CMD(x) ((__u32)(x)) + /** * struct tee_ioctl_invoke_func_arg - Invokes a function in a Trusted * Application From fda5c7ca854fa0f74711d68f510a994d4f2710e7 Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Sun, 18 Oct 2020 20:09:02 -0700 Subject: [PATCH 37/37] optee: OCALL support Enable OCALL support specifically for OP-TEE. Signed-off-by: Hernan Gatta --- drivers/tee/optee/call.c | 886 ++++++++++++++++++++++++++---- drivers/tee/optee/core.c | 69 ++- drivers/tee/optee/optee_msg.h | 23 +- drivers/tee/optee/optee_private.h | 143 ++++- drivers/tee/optee/rpc.c | 35 ++ 5 files changed, 1008 insertions(+), 148 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 24b5153db47ea6..499ebbed3387c6 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -28,28 +28,393 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, return NULL; } +static void param_clear_ocall(struct tee_param *ocall) +{ + if (ocall) + memset(&ocall->u, 0, sizeof(ocall->u)); +} + +static u64 param_get_ocall_func(struct tee_param *param) +{ + return TEE_IOCTL_OCALL_GET_FUNC(param->u.value.a); +} + +/* Requires @sem in the parent struct optee_session to be held */ +static int verify_ocall_request(u32 num_params, struct optee_call_ctx *call_ctx) +{ + size_t n; + size_t sz; + struct optee_msg_arg *arg = call_ctx->rpc_arg; + + switch (arg->cmd) { + case OPTEE_MSG_RPC_CMD_SHM_ALLOC: + case OPTEE_MSG_RPC_CMD_SHM_FREE: + if (!num_params) + return -EINVAL; + + /* + * This parameter either carries the requested allocation size + * or a pointer to the SHM to be freed. + */ + if (!arg->num_params || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) + return -EINVAL; + + /* + * Ensure that we won't read past the end of the SHM underlying + * arg->params. + */ + sz = sizeof(*arg) + sizeof(*arg->params) * arg->num_params; + if (sz > call_ctx->rpc_shm->size) + return -EINVAL; + + /* The remaining parameters are unused */ + for (n = 1; n < arg->num_params; n++) + if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) + return -EINVAL; + break; + case OPTEE_MSG_RPC_CMD_OCALL: + /* 'num_params' is checked later */ + + /* These parameters carry the OCALL descriptors */ + if (arg->num_params < 2 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INOUT || + arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT || + arg->params[0].u.value.a > U32_MAX || /* OCALL Cmd Id */ + arg->params[1].u.value.c != 0) /* TA UUID (128 bytes) */ + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Requires @sem in the parent struct optee_session to be held */ +static int verify_ocall_reply(u64 func, struct tee_param *params, + u32 num_params, struct optee_call_ctx *call_ctx) +{ + size_t n; + + switch (func) { + case TEE_IOCTL_OCALL_CMD_SHM_ALLOC: + if (call_ctx->rpc_arg->cmd != OPTEE_MSG_RPC_CMD_SHM_ALLOC || + !num_params) + return -EINVAL; + + /* This parameter carries the allocated SHM ID */ + if (params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT || + params[0].u.value.c > INT_MAX) + return -EINVAL; + break; + case TEE_IOCTL_OCALL_CMD_SHM_FREE: + if (call_ctx->rpc_arg->cmd != OPTEE_MSG_RPC_CMD_SHM_FREE || + !num_params) + return -EINVAL; + + /* Sanity check, not used while processing the reply */ + if (params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT) + return -EINVAL; + break; + case TEE_IOCTL_OCALL_CMD_INVOKE: + if (call_ctx->rpc_arg->cmd != OPTEE_MSG_RPC_CMD_OCALL) + return -EINVAL; + + /* Skip the loop below */ + return 0; + default: + return -EINVAL; + } + + /* The remaining parameters are unused */ + for (n = 1; n < num_params; n++) + if (params[n].attr != TEE_IOCTL_PARAM_ATTR_TYPE_NONE) + return -EINVAL; + + return 0; +} + +/* Requires @sem in the parent struct optee_session to be held */ +static void process_ocall_memrefs(struct optee_msg_param *params, + u32 num_params, bool increment) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_shm *shm; + const struct optee_msg_param *mp = params + n; + u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; + + switch (attr) { + case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: + shm = (struct tee_shm *)(uintptr_t)mp->u.rmem.shm_ref; + break; + default: + shm = NULL; + break; + } + + if (!shm) + continue; + + if (increment) + tee_shm_get(shm); + else + tee_shm_put(shm); + } +} + +/* + * Requires @sem in the parent struct optee_session to be held (if OCALLs are + * expected) + */ +static void call_prologue(struct optee_call_ctx *call_ctx) +{ + struct optee *optee = tee_get_drvdata(call_ctx->ctx->teedev); + + /* Initialize waiter */ + optee_cq_wait_init(&optee->call_queue, &call_ctx->waiter); +} + +/* + * Requires @sem in the parent struct optee_session to be held (if OCALLs are + * expected) + */ +static void call_epilogue(struct optee_call_ctx *call_ctx) +{ + struct optee *optee = tee_get_drvdata(call_ctx->ctx->teedev); + + optee_rpc_finalize_call(call_ctx); + + /* + * We're done with our thread in secure world, if there's any + * thread waiters wake up one. + */ + optee_cq_wait_final(&optee->call_queue, &call_ctx->waiter); +} + +/* Requires @sem in the parent struct optee_session to be held */ +static int process_ocall_request(struct tee_param *params, u32 num_params, + struct tee_param *ocall, + struct optee_call_ctx *call_ctx) +{ + u32 cmd_id; + struct tee_shm *shm; + size_t shm_sz; + struct optee_msg_param *msg_param; + u32 msg_num_params; + int rc = 0; + + /* + * Points to the octets of the UUID corresponding to the TA requesting + * the OCALL, if applicable for this call. + */ + void *clnt_id; + + rc = verify_ocall_request(num_params, call_ctx); + if (rc) + goto exit_set_ret; + + /* + * Clear out the parameters of the original function invocation. The + * original contents are backed up in call_ctx->msg_arg and will be + * restored elsewhere once the OCALL is over. + */ + memset(params, 0, num_params * sizeof(*params)); + + /* Set up the OCALL request */ + switch (call_ctx->rpc_arg->cmd) { + case OPTEE_MSG_RPC_CMD_SHM_ALLOC: + ocall->u.value.a = + TEE_IOCTL_OCALL_MAKE_PAIR(TEE_IOCTL_OCALL_CMD_SHM_ALLOC, + 0); + + shm_sz = call_ctx->rpc_arg->params[0].u.value.b; + params[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; + params[0].u.value.a = 0; + params[0].u.value.b = shm_sz; + params[0].u.value.c = 0; + break; + case OPTEE_MSG_RPC_CMD_SHM_FREE: + ocall->u.value.a = + TEE_IOCTL_OCALL_MAKE_PAIR(TEE_IOCTL_OCALL_CMD_SHM_FREE, + 0); + + shm = (struct tee_shm *)(uintptr_t) + call_ctx->rpc_arg->params[0].u.value.b; + params[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + params[0].u.value.a = 0; + params[0].u.value.b = 0; + params[0].u.value.c = tee_shm_get_id(shm); + break; + case OPTEE_MSG_RPC_CMD_OCALL: + /* -2 here and +2 below to skip the OCALL descriptors */ + msg_num_params = call_ctx->rpc_arg->num_params - 2; + if (num_params < msg_num_params) { + rc = -EINVAL; + goto exit_set_ret; + } + + msg_param = call_ctx->rpc_arg->params + 2; + rc = optee_from_msg_param(params, msg_num_params, msg_param); + if (rc) + goto exit_set_ret; + + process_ocall_memrefs(msg_param, msg_num_params, true); + call_ctx->rpc_must_release = true; + + cmd_id = (u32)call_ctx->rpc_arg->params[0].u.value.a; + ocall->u.value.a = + TEE_IOCTL_OCALL_MAKE_PAIR(TEE_IOCTL_OCALL_CMD_INVOKE, + cmd_id); + + clnt_id = &call_ctx->rpc_arg->params[1].u.value; + memcpy(&ocall->u.value.b, clnt_id, TEE_IOCTL_UUID_LEN); + break; + default: + /* NOT REACHED */ + rc = -EINVAL; + goto exit_set_ret; + } + + return rc; + +exit_set_ret: + call_ctx->rpc_arg->ret = TEEC_ERROR_BAD_PARAMETERS; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + return rc; +} + +/* Requires @sem in the parent struct optee_session to be held */ +static int process_ocall_reply(u32 ret, u32 ret_origin, + struct tee_param *params, u32 num_params, + struct tee_param *ocall, + struct optee_call_ctx *call_ctx) +{ + const u64 func = param_get_ocall_func(ocall); + struct tee_shm *shm; + void *shm_pages_list; + struct optee_msg_param *msg_param; + u32 msg_num_params; + int rc = 0; + + rc = verify_ocall_reply(func, params, num_params, call_ctx); + if (rc) + goto exit_set_ret; + + switch (func) { + case TEE_IOCTL_OCALL_CMD_SHM_ALLOC: + if (ret != TEEC_SUCCESS) + goto exit_propagate_ret; + + if (call_ctx->ocall_pages_list) + goto exit_set_ret; + + shm = tee_shm_get_from_id(call_ctx->ctx, + (int)params[0].u.value.c); + if (IS_ERR(shm)) { + rc = PTR_ERR(shm); + goto exit_set_ret; + } + + rc = optee_rpc_process_shm_alloc(shm, call_ctx->rpc_arg->params, + &shm_pages_list); + + /* The CA holds a reference */ + tee_shm_put(shm); + + if (rc) + goto exit_set_ret; + + /* Could be NULL and zero, respectively */ + call_ctx->ocall_pages_list = shm_pages_list; + call_ctx->ocall_num_entries = shm->num_pages; + break; + case TEE_IOCTL_OCALL_CMD_SHM_FREE: + /* OP-TEE ignores ret and ret_origin for an SHM_FREE RPC */ + if (ret != TEEC_SUCCESS) + goto exit_propagate_ret; + + if (call_ctx->ocall_pages_list) { + optee_free_pages_list(call_ctx->ocall_pages_list, + call_ctx->ocall_num_entries); + call_ctx->ocall_pages_list = NULL; + call_ctx->ocall_num_entries = 0; + } + break; + case TEE_IOCTL_OCALL_CMD_INVOKE: + /* -2 here and +2 below to skip the OCALL descriptors */ + msg_num_params = call_ctx->rpc_arg->num_params - 2; + if (num_params < msg_num_params) { + rc = -EINVAL; + goto exit_set_ret; + } + + msg_param = call_ctx->rpc_arg->params + 2; + rc = optee_to_msg_param(msg_param, msg_num_params, params); + if (rc) + goto exit_set_ret; + + process_ocall_memrefs(msg_param, msg_num_params, false); + call_ctx->rpc_must_release = false; + + call_ctx->rpc_arg->params[0].u.value.b = ret; + call_ctx->rpc_arg->params[0].u.value.c = ret_origin; + break; + default: + rc = -EINVAL; + goto exit_set_ret; + } + + call_ctx->rpc_arg->ret = TEEC_SUCCESS; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + + return rc; + +exit_propagate_ret: + call_ctx->rpc_arg->ret = ret; + call_ctx->rpc_arg->ret_origin = ret_origin; + return -EINVAL; +exit_set_ret: + call_ctx->rpc_arg->ret = TEEC_ERROR_BAD_PARAMETERS; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + return rc; +} + +static void clear_call_ctx(struct optee_call_ctx *call_ctx) +{ + memset(call_ctx, 0, sizeof(*call_ctx)); +} + /** - * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world + * optee_do_call_with_ctx() - Invoke OP-TEE in secure world * @ctx: calling context - * @parg: physical address of message to pass to secure world * * Does and SMC to OP-TEE in secure world and handles eventual resulting * Remote Procedure Calls (RPC) from OP-TEE. * - * Returns return code from secure world, 0 is OK + * Returns return code from secure world, 0 is OK, -EAGAIN means an OCALL + * request was received. */ -u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) +u32 optee_do_call_with_ctx(struct optee_call_ctx *call_ctx) { - struct optee *optee = tee_get_drvdata(ctx->teedev); - struct optee_call_waiter w; + struct optee *optee = tee_get_drvdata(call_ctx->ctx->teedev); struct optee_rpc_param param = { }; - struct optee_call_ctx call_ctx = { }; u32 ret; - param.a0 = OPTEE_SMC_CALL_WITH_ARG; - reg_pair_from_64(¶m.a1, ¶m.a2, parg); - /* Initialize waiter */ - optee_cq_wait_init(&optee->call_queue, &w); + if (call_ctx->rpc_shm) { + param.a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; + reg_pair_from_64(¶m.a1, ¶m.a2, + (uintptr_t)call_ctx->rpc_shm); + param.a3 = call_ctx->thread_id; + } else { + param.a0 = OPTEE_SMC_CALL_WITH_ARG; + reg_pair_from_64(¶m.a1, ¶m.a2, call_ctx->msg_parg); + } + while (true) { struct arm_smccc_res res; @@ -63,33 +428,64 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { /* - * Out of threads in secure world, wait for a thread + * Out of threads in secure world, wait for a thread to * become available. */ - optee_cq_wait_for_completion(&optee->call_queue, &w); + optee_cq_wait_for_completion(&optee->call_queue, + &call_ctx->waiter); } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { - might_sleep(); param.a0 = res.a0; param.a1 = res.a1; param.a2 = res.a2; param.a3 = res.a3; - optee_handle_rpc(ctx, ¶m, &call_ctx); + + if (optee_rpc_is_ocall(¶m, call_ctx)) + return -EAGAIN; + + might_sleep(); + optee_handle_rpc(call_ctx->ctx, ¶m, call_ctx); } else { ret = res.a0; break; } } - optee_rpc_finalize_call(&call_ctx); - /* - * We're done with our thread in secure world, if there's any - * thread waiters wake up one. - */ - optee_cq_wait_final(&optee->call_queue, &w); - return ret; } +/** + * optee_do_call_with_arg() - Invoke OP-TEE in secure world + * @ctx: calling context + * @parg: physical address of message to pass to secure world + * + * Wraps a call to optee_do_call_with_ctx that sets up the calling context on + * behalf of a caller that does not expect OCALLs. + * + * Returns return code from secure world, 0 is OK + */ +u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) +{ + struct optee_call_ctx call_ctx = { }; + int rc; + + call_ctx.ctx = ctx; + call_ctx.msg_parg = parg; + + call_prologue(&call_ctx); + + rc = optee_do_call_with_ctx(&call_ctx); + if (rc == -EAGAIN) { + pr_warn("received an unexpected OCALL, cancelling it now"); + call_ctx.rpc_arg->ret = TEEC_ERROR_NOT_SUPPORTED; + call_ctx.rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + optee_do_call_with_ctx(&call_ctx); + } + + call_epilogue(&call_ctx); + + return rc; +} + static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, struct optee_msg_arg **msg_arg, phys_addr_t *msg_parg) @@ -125,97 +521,256 @@ static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, return shm; } -int optee_open_session(struct tee_context *ctx, - struct tee_ioctl_open_session_arg *arg, - struct tee_param *normal_param, - u32 num_normal_params, - struct tee_param *ocall_param) +/* + * Requires @sem in the parent struct optee_session to be held; the caller is + * expected to have filled in the ret and ret_origin elements of rpc_arg. + */ +static int cancel_ocall(struct optee_call_ctx *call_ctx) { - struct optee_context_data *ctxdata = ctx->data; int rc; + + /* +2 and -2 to skip the OCALL descriptors */ + if (call_ctx->rpc_must_release) { + process_ocall_memrefs(call_ctx->rpc_arg->params + 2, + call_ctx->rpc_arg->num_params - 2, false); + call_ctx->rpc_must_release = false; + } + + if (call_ctx->ocall_pages_list) { + optee_free_pages_list(call_ctx->ocall_pages_list, + call_ctx->ocall_num_entries); + call_ctx->ocall_pages_list = NULL; + call_ctx->ocall_num_entries = 0; + } + + rc = optee_do_call_with_ctx(call_ctx); + if (rc == -EAGAIN) + pr_warn("received an OCALL while cancelling an OCALL"); + + call_epilogue(call_ctx); + + return rc; +} + +static int close_session(struct tee_context *ctx, u32 session) +{ struct tee_shm *shm; struct optee_msg_arg *msg_arg; phys_addr_t msg_parg; - struct optee_session *sess = NULL; - - if (ocall_param) { - pr_err("OCALLs not supported\n"); - return -EOPNOTSUPP; - } - /* +2 for the meta parameters added below */ - shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); if (IS_ERR(shm)) return PTR_ERR(shm); - msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; - msg_arg->cancel_id = arg->cancel_id; + msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; + msg_arg->session = session; + optee_do_call_with_arg(ctx, msg_parg); - /* - * Initialize and add the meta parameters needed when opening a - * session. - */ - msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | - OPTEE_MSG_ATTR_META; - msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | - OPTEE_MSG_ATTR_META; - memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid)); - msg_arg->params[1].u.value.c = arg->clnt_login; - - rc = tee_session_calc_client_uuid((uuid_t *)&msg_arg->params[1].u.value, - arg->clnt_login, arg->clnt_uuid); - if (rc) - goto out; + tee_shm_free(shm); + return 0; +} - rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, - normal_param); - if (rc) - goto out; +int optee_open_session(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *normal_param, u32 num_normal_params, + struct tee_param *ocall_param) +{ + struct optee_context_data *ctxdata = ctx->data; + struct optee_session *sess = NULL; + struct optee_call_ctx *call_ctx = NULL; + int sess_tmp_id; + u64 ocall_func; + int rc = 0; - sess = kzalloc(sizeof(*sess), GFP_KERNEL); - if (!sess) { - rc = -ENOMEM; - goto out; - } + if (ocall_param && !ctx->cap_ocall) + return -EOPNOTSUPP; - if (optee_do_call_with_arg(ctx, msg_parg)) { - msg_arg->ret = TEEC_ERROR_COMMUNICATION; - msg_arg->ret_origin = TEEC_ORIGIN_COMMS; - } + ocall_func = ocall_param ? param_get_ocall_func(ocall_param) : 0; + if (ocall_func) { + if (arg->session > INT_MAX) + return -EINVAL; - if (msg_arg->ret == TEEC_SUCCESS) { - /* A new session has been created, add it to the list. */ - sess->session_id = msg_arg->session; + sess_tmp_id = (int)arg->session; mutex_lock(&ctxdata->mutex); - list_add(&sess->list_node, &ctxdata->sess_list); + sess = idr_remove(&ctxdata->tmp_sess_list, sess_tmp_id); mutex_unlock(&ctxdata->mutex); + if (!sess) + return -EINVAL; + + call_ctx = &sess->call_ctx; + if (!call_ctx->rpc_shm) { + rc = -EINVAL; + call_ctx->rpc_arg->ret = TEEC_ERROR_BAD_PARAMETERS; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + goto exit_cancel; + } + + rc = process_ocall_reply(arg->ret, arg->ret_origin, + normal_param, num_normal_params, + ocall_param, call_ctx); + if (rc) + goto exit_cancel; } else { - kfree(sess); + sess = kzalloc(sizeof(*sess), GFP_KERNEL); + if (!sess) + return -ENOMEM; + + call_ctx = &sess->call_ctx; + + /* +2 for the meta parameters added below */ + call_ctx->msg_shm = get_msg_arg(ctx, num_normal_params + 2, + &call_ctx->msg_arg, + &call_ctx->msg_parg); + if (IS_ERR(call_ctx->msg_shm)) { + rc = PTR_ERR(call_ctx->msg_shm); + goto exit_free; + } + + call_ctx->ctx = ctx; + call_ctx->msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; + call_ctx->msg_arg->cancel_id = arg->cancel_id; + + /* + * Initialize and add the meta parameters needed when opening a + * session. + */ + call_ctx->msg_arg->params[0].attr = + OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | OPTEE_MSG_ATTR_META; + call_ctx->msg_arg->params[1].attr = + OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | OPTEE_MSG_ATTR_META; + memcpy(&call_ctx->msg_arg->params[0].u.value, arg->uuid, + sizeof(arg->uuid)); + call_ctx->msg_arg->params[1].u.value.c = arg->clnt_login; + rc = tee_session_calc_client_uuid((uuid_t *) + &call_ctx->msg_arg->params[1].u.value, + arg->clnt_login, arg->clnt_uuid); + if (rc) + goto exit_free_shm; + + rc = optee_to_msg_param(call_ctx->msg_arg->params + 2, + num_normal_params, normal_param); + if (rc) + goto exit_free_shm; + + call_prologue(call_ctx); } - if (optee_from_msg_param(normal_param, arg->num_params, - msg_arg->params + 2)) { - arg->ret = TEEC_ERROR_COMMUNICATION; - arg->ret_origin = TEEC_ORIGIN_COMMS; - /* Close session again to avoid leakage */ - optee_close_session(ctx, msg_arg->session); + rc = optee_do_call_with_ctx(call_ctx); + if (rc == -EAGAIN) { + rc = process_ocall_request(normal_param, num_normal_params, + ocall_param, call_ctx); + if (rc) + goto exit_cancel; + + /* + * 'sess' becomes globally visible after adding it to the IDR, + * so do not touch it once the mutex is unlocked. + */ + mutex_lock(&ctxdata->mutex); + sess_tmp_id = idr_alloc(&ctxdata->tmp_sess_list, sess, 1, 0, + GFP_KERNEL); + if (sess_tmp_id >= 1) + sess->session_id = sess_tmp_id; + mutex_unlock(&ctxdata->mutex); + if (sess_tmp_id < 0) { + rc = sess_tmp_id; + call_ctx->rpc_arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + goto exit_cancel; + } + + arg->session = sess_tmp_id; } else { - arg->session = msg_arg->session; - arg->ret = msg_arg->ret; - arg->ret_origin = msg_arg->ret_origin; + call_epilogue(call_ctx); + + if (rc) { + arg->ret = TEEC_ERROR_COMMUNICATION; + arg->ret_origin = TEEC_ORIGIN_COMMS; + } else { + arg->ret = call_ctx->msg_arg->ret; + arg->ret_origin = call_ctx->msg_arg->ret_origin; + } + + if (optee_from_msg_param(normal_param, num_normal_params, + call_ctx->msg_arg->params + 2)) { + if (arg->ret == TEEC_SUCCESS) + close_session(ctx, call_ctx->msg_arg->session); + + arg->ret = TEEC_ERROR_COMMUNICATION; + arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + if (arg->ret) + goto exit_clear_free_all; + + /* + * A new session has been created, initialize it and add it to + * the list. + */ + sema_init(&sess->sem, 1); + arg->session = call_ctx->msg_arg->session; + sess->session_id = call_ctx->msg_arg->session; + + tee_shm_free(call_ctx->msg_shm); + clear_call_ctx(call_ctx); + + mutex_lock(&ctxdata->mutex); + list_add(&sess->list_node, &ctxdata->sess_list); + mutex_unlock(&ctxdata->mutex); + + param_clear_ocall(ocall_param); } -out: - tee_shm_free(shm); return rc; + +exit_cancel: + /* See comment in optee_cancel_open_session_ocall */ + if (cancel_ocall(call_ctx) == 0 && + call_ctx->msg_arg->ret == TEEC_SUCCESS) + close_session(ctx, call_ctx->msg_arg->session); + optee_from_msg_param(normal_param, num_normal_params, + call_ctx->msg_arg->params); +exit_clear_free_all: + param_clear_ocall(ocall_param); +exit_free_shm: + tee_shm_free(call_ctx->msg_shm); +exit_free: + kfree(sess); + return rc; +} + +void optee_cancel_open_session_ocall(struct optee_session *sess) +{ + struct optee_call_ctx *call_ctx = &sess->call_ctx; + + call_ctx->rpc_arg->ret = TEEC_ERROR_TARGET_DEAD; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + + /* + * Reaching this function means an OCALL is pending during session open + * but the CA has terminated abnormally. As such, the OCALL is + * cancelled. However, there is a chance that the TA's session open + * handler ignores the cancellation and lets the session open anyway. If + * that happens, close it. + */ + if (cancel_ocall(&sess->call_ctx) == 0 && + call_ctx->msg_arg->ret == TEEC_SUCCESS) + close_session(call_ctx->ctx, call_ctx->msg_arg->session); + + /* + * Decrease the ref count on all shared memory pointers passed into the + * original function invocation. + */ + process_ocall_memrefs(call_ctx->msg_arg->params, + call_ctx->msg_arg->num_params, false); + + tee_shm_free(call_ctx->msg_shm); + kfree(sess); } int optee_close_session(struct tee_context *ctx, u32 session) { struct optee_context_data *ctxdata = ctx->data; - struct tee_shm *shm; - struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess; /* Check that the session is valid and remove it from the list */ @@ -226,17 +781,20 @@ int optee_close_session(struct tee_context *ctx, u32 session) mutex_unlock(&ctxdata->mutex); if (!sess) return -EINVAL; - kfree(sess); - shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + /* + * If another thread found the session before we removed it from the + * list and that thread is operating on the session object itself, wait + * until it is done before we destroy it. + */ + down(&sess->sem); - msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; - msg_arg->session = session; - optee_do_call_with_arg(ctx, msg_parg); + if (sess->call_ctx.rpc_shm) + optee_cancel_invoke_function_ocall(&sess->call_ctx); + + kfree(sess); + close_session(ctx, session); - tee_shm_free(shm); return 0; } @@ -245,54 +803,140 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *ocall_param) { struct optee_context_data *ctxdata = ctx->data; - struct tee_shm *shm; - struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; + struct optee_call_ctx *call_ctx; struct optee_session *sess; - int rc; + u64 ocall_func; + int rc = 0; - if (ocall_param) { - pr_err("OCALLs not supported\n"); - return -EOPNOTSUPP; + if (ocall_param && !ctx->cap_ocall) { + rc = -EOPNOTSUPP; + goto exit; } /* Check that the session is valid */ mutex_lock(&ctxdata->mutex); sess = find_session(ctxdata, arg->session); + if (sess) + down(&sess->sem); mutex_unlock(&ctxdata->mutex); if (!sess) return -EINVAL; - shm = get_msg_arg(ctx, arg->num_params, &msg_arg, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; - msg_arg->func = arg->func; - msg_arg->session = arg->session; - msg_arg->cancel_id = arg->cancel_id; + call_ctx = &sess->call_ctx; + ocall_func = ocall_param ? param_get_ocall_func(ocall_param) : 0; + if (ocall_func) { + /* The current call is a reply to an OCALL request */ - rc = optee_to_msg_param(msg_arg->params, arg->num_params, normal_param); - if (rc) - goto out; + if (!call_ctx->rpc_shm) { + rc = -EINVAL; + goto exit; + } - if (optee_do_call_with_arg(ctx, msg_parg)) { - msg_arg->ret = TEEC_ERROR_COMMUNICATION; - msg_arg->ret_origin = TEEC_ORIGIN_COMMS; + rc = process_ocall_reply(arg->ret, arg->ret_origin, + normal_param, num_normal_params, + ocall_param, call_ctx); + if (rc) + goto exit_cancel; + } else { + /* + * The current call is an invocation that may result in an OCALL + * request. + */ + + if (call_ctx->rpc_shm) { + rc = -EINVAL; + call_ctx->rpc_arg->ret = TEEC_ERROR_BAD_PARAMETERS; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + goto exit_cancel; + } + + call_ctx->msg_shm = get_msg_arg(ctx, num_normal_params, + &call_ctx->msg_arg, + &call_ctx->msg_parg); + if (IS_ERR(call_ctx->msg_shm)) { + rc = PTR_ERR(call_ctx->msg_shm); + goto exit_clear; + } + + call_ctx->ctx = ctx; + call_ctx->msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; + call_ctx->msg_arg->func = arg->func; + call_ctx->msg_arg->session = arg->session; + call_ctx->msg_arg->cancel_id = arg->cancel_id; + + rc = optee_to_msg_param(call_ctx->msg_arg->params, + num_normal_params, normal_param); + if (rc) { + tee_shm_free(call_ctx->msg_shm); + goto exit_clear; + } + + call_prologue(call_ctx); } - if (optee_from_msg_param(normal_param, arg->num_params, - msg_arg->params)) { - msg_arg->ret = TEEC_ERROR_COMMUNICATION; - msg_arg->ret_origin = TEEC_ORIGIN_COMMS; + rc = optee_do_call_with_ctx(call_ctx); + if (rc == -EAGAIN) { + rc = process_ocall_request(normal_param, num_normal_params, + ocall_param, call_ctx); + if (rc) + goto exit_cancel; + } else { + call_epilogue(call_ctx); + + arg->ret = call_ctx->msg_arg->ret; + arg->ret_origin = call_ctx->msg_arg->ret_origin; + + if (rc) { + arg->ret = TEEC_ERROR_COMMUNICATION; + arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + if (optee_from_msg_param(normal_param, num_normal_params, + call_ctx->msg_arg->params)) { + arg->ret = TEEC_ERROR_COMMUNICATION; + arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + tee_shm_free(call_ctx->msg_shm); + clear_call_ctx(call_ctx); + param_clear_ocall(ocall_param); } - arg->ret = msg_arg->ret; - arg->ret_origin = msg_arg->ret_origin; -out: - tee_shm_free(shm); + up(&sess->sem); + return rc; + +exit_cancel: + cancel_ocall(call_ctx); + optee_from_msg_param(normal_param, num_normal_params, + call_ctx->msg_arg->params); + tee_shm_free(call_ctx->msg_shm); + param_clear_ocall(ocall_param); +exit_clear: + clear_call_ctx(call_ctx); +exit: + up(&sess->sem); return rc; } +/* Requires @sem in the parent struct optee_session to be held */ +void optee_cancel_invoke_function_ocall(struct optee_call_ctx *call_ctx) +{ + call_ctx->rpc_arg->ret = TEEC_ERROR_TARGET_DEAD; + call_ctx->rpc_arg->ret_origin = TEEC_ORIGIN_COMMS; + + cancel_ocall(call_ctx); + + /* + * Decrease the ref count on all shared memory pointers passed into the + * original function invocation. + */ + process_ocall_memrefs(call_ctx->msg_arg->params, + call_ctx->msg_arg->num_params, false); + + tee_shm_free(call_ctx->msg_shm); + clear_call_ctx(call_ctx); +} + int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) { struct optee_context_data *ctxdata = ctx->data; diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index c2fae61401ecb9..21f9d570fe8eed 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "optee_bench.h" #include "optee_private.h" #include "optee_smc.h" @@ -266,11 +267,10 @@ static int optee_open(struct tee_context *ctx) } mutex_init(&ctxdata->mutex); INIT_LIST_HEAD(&ctxdata->sess_list); + idr_init(&ctxdata->tmp_sess_list); - if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) - ctx->cap_memref_null = true; - else - ctx->cap_memref_null = false; + ctx->cap_memref_null = optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL; + ctx->cap_ocall = optee->sec_caps & OPTEE_SMC_SEC_CAP_OCALL; ctx->data = ctxdata; return 0; @@ -315,6 +315,7 @@ static void optee_release(struct tee_context *ctx) } kfree(sess); } + idr_destroy(&ctxdata->tmp_sess_list); kfree(ctxdata); if (!IS_ERR(shm)) @@ -331,10 +332,68 @@ static void optee_release(struct tee_context *ctx) } } +static void optee_pre_release(struct tee_context *ctx) +{ + struct optee_context_data *ctxdata = ctx->data; + struct optee_session *sess; + bool have_xa = false; + unsigned long i = 0; + struct xarray xa; + int id; + + if (!ctxdata) + return; + + /* + * Only if necessary, add into 'xa' sessions that have to have an OCALL + * cancelled instead of doing so in the loops to avoid calling into + * secure world with @mutex held. + */ + mutex_lock(&ctxdata->mutex); + idr_for_each_entry(&ctxdata->tmp_sess_list, sess, id) { + if (!have_xa) { + xa_init(&xa); + have_xa = true; + } + idr_remove(&ctxdata->tmp_sess_list, id); + xa_store(&xa, i++, xa_tag_pointer(sess, 1), GFP_KERNEL); + } + list_for_each_entry(sess, &ctxdata->sess_list, list_node) { + if (!sess->call_ctx.rpc_shm) + continue; + if (!have_xa) { + xa_init(&xa); + have_xa = true; + } + xa_store(&xa, i++, sess, GFP_KERNEL); + } + mutex_unlock(&ctxdata->mutex); + + if (!have_xa) + return; + + xa_for_each(&xa, i, sess) { + if (xa_pointer_tag(sess)) { + optee_cancel_open_session_ocall(xa_untag_pointer(sess)); + } else { + /* + * Holding @sem here while calling into secure world is + * fine seeing as there is no code path that would + * recursively acquire it. + */ + down(&sess->sem); + optee_cancel_invoke_function_ocall(&sess->call_ctx); + up(&sess->sem); + } + } + + xa_destroy(&xa); +} + static const struct tee_driver_ops optee_ops = { .get_version = optee_get_version, .open = optee_open, - .pre_release = NULL, + .pre_release = optee_pre_release, .release = optee_release, .open_session = optee_open_session, .close_session = optee_close_session, diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 8839fa1b2ea767..4c2aae8f932fe7 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -404,12 +404,13 @@ struct optee_msg_arg { */ #define OPTEE_MSG_RPC_CMD_SHM_ALLOC 6 /* Memory that can be shared with a non-secure user space application */ -#define OPTEE_MSG_RPC_SHM_TYPE_APPL 0 +#define OPTEE_MSG_RPC_SHM_TYPE_APPL 0 /* Memory only shared with non-secure kernel */ -#define OPTEE_MSG_RPC_SHM_TYPE_KERNEL 1 +#define OPTEE_MSG_RPC_SHM_TYPE_KERNEL 1 /* Memory shared with non-secure kernel, but exported to userspace */ -#define OPTEE_MSG_RPC_SHM_TYPE_GLOBAL 2 - +#define OPTEE_MSG_RPC_SHM_TYPE_GLOBAL 2 +/* Memory shared with the requesting TA's Client Application */ +#define OPTEE_MSG_RPC_SHM_TYPE_CLIENT_APPL 3 /* * Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC * @@ -429,4 +430,18 @@ struct optee_msg_arg { * [in] param[0].u.value.c Size of buffer */ #define OPTEE_MSG_RPC_CMD_BENCH_REG 20 + +/* + * Send a command to the Client Application. + * + * [in] param[0].u.value[0].a command Id + * [out] param[0].u.value[0].b OCALL return value + * [out] param[0].u.value[0].c OCALL return value origin + * [in] param[0].u.value[1].a UUID of TA whence OCALL originated (Hi) + * [out] param[0].u.value[1].b UUID of TA whence OCALL originated (Lo) + * + * [in/out] any[2..5].* OCALL parameters as specified by the TA, if any + */ +#define OPTEE_MSG_RPC_CMD_OCALL 22 + #endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 53ccc89c3e7663..7e5679c74edbe5 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -16,10 +16,13 @@ /* Some Global Platform error codes used in this driver */ #define TEEC_SUCCESS 0x00000000 +#define TEEC_ERROR_CANCEL 0xFFFF0002 #define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 +#define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A #define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C #define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010 +#define TEEC_ERROR_TARGET_DEAD 0xFFFF3024 #define TEEC_ORIGIN_COMMS 0x00000002 @@ -97,15 +100,69 @@ struct optee { struct work_struct scan_bus_work; }; +struct optee_call_waiter { + struct list_head list_node; + struct completion c; +}; + +/** + * struct optee_call_ctx - holds context that is preserved during one STD call + * @pages_list: list of pages allocated for RPC requests + * @num_entries: number of pages in 'pages_list' + * @ctx: TEE context whence the OCALL originated, if any + * @msg_shm: shared memory object used for calling into OP-TEE + * @msg_arg: arguments used for calling into OP-TEE, namely the data + * behind 'msg_shm' + * @msg_parg: physical pointer underlying 'msg_shm' + * @rpc_must_release: indicates that OCALL parameters have had their refcount + * increased and must be decreased on cancellation + * @rpc_shm: shared memory object used for responding to RPCs + * @rpc_arg: arguments used for responding to RPCs, namely the data + * behind 'rpc_shm' + * @thread_id: secure thread Id whence the OCALL originated and which + * must be resumed when replying to the OCALL + * @waiter: object used to wait until a secure thread becomes + * available is the previous call into OP-TEE failed + * because all secure threads are in use + * @ocall_pages_list: list of pages allocated for OCALL requests + * @ocall_num_entries: number of pages in 'ocall_pages_list' + */ +struct optee_call_ctx { + /* Information about pages list used in last allocation */ + void *pages_list; + size_t num_entries; + + /* OCALL support */ + struct tee_context *ctx; + + struct tee_shm *msg_shm; + struct optee_msg_arg *msg_arg; + phys_addr_t msg_parg; + + bool rpc_must_release; + struct tee_shm *rpc_shm; + struct optee_msg_arg *rpc_arg; + + u32 thread_id; + struct optee_call_waiter waiter; + + void *ocall_pages_list; + size_t ocall_num_entries; +}; + struct optee_session { + /* Serializes access to this struct */ + struct semaphore sem; struct list_head list_node; u32 session_id; + struct optee_call_ctx call_ctx; }; struct optee_context_data { /* Serializes access to this struct */ struct mutex mutex; struct list_head sess_list; + struct idr tmp_sess_list; }; struct optee_rpc_param { @@ -119,25 +176,41 @@ struct optee_rpc_param { u32 a7; }; -struct optee_call_waiter { - struct list_head list_node; - struct completion c; -}; - -/* Holds context that is preserved during one STD call */ -struct optee_call_ctx { - /* information about pages list used in last allocation */ - void *pages_list; - size_t num_entries; -}; +/* + * RPC support + */ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, struct optee_call_ctx *call_ctx); +bool optee_rpc_is_ocall(struct optee_rpc_param *param, + struct optee_call_ctx *call_ctx); +int optee_rpc_process_shm_alloc(struct tee_shm *shm, + struct optee_msg_param *msg_param, void **list); void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx); +/* + * Wait queue + */ + void optee_wait_queue_init(struct optee_wait_queue *wq); void optee_wait_queue_exit(struct optee_wait_queue *wq); +/* + * Call queue + */ + +void optee_cq_wait_init(struct optee_call_queue *cq, + struct optee_call_waiter *w); +void optee_cq_wait_for_completion(struct optee_call_queue *cq, + struct optee_call_waiter *w); +void optee_cq_complete_one(struct optee_call_queue *cq); +void optee_cq_wait_final(struct optee_call_queue *cq, + struct optee_call_waiter *w); + +/* + * Supplicant + */ + u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, struct tee_param *param); @@ -152,17 +225,40 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_param *param); +/* + * Calls into OP-TEE + */ + u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); + +/* + * Sessions + */ + int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *normal_param, u32 num_normal_params, struct tee_param *ocall_param); int optee_close_session(struct tee_context *ctx, u32 session); + +/* + * Function invocations + */ + int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *normal_param, u32 num_normal_params, struct tee_param *ocall_param); + +/* + * Cancellations + */ + int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); +/* + * Shared memory + */ + void optee_enable_shm_cache(struct optee *optee); void optee_disable_shm_cache(struct optee *optee); @@ -176,27 +272,38 @@ int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, unsigned long start); int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm); +/* + * Paremeters + */ + int optee_from_msg_param(struct tee_param *params, size_t num_params, const struct optee_msg_param *msg_params); int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, const struct tee_param *params); +/* + * RPC memory + */ + u64 *optee_allocate_pages_list(size_t num_entries); void optee_free_pages_list(void *array, size_t num_entries); void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, size_t page_offset); +/* + * Devices + */ + #define PTA_CMD_GET_DEVICES 0x0 #define PTA_CMD_GET_DEVICES_SUPP 0x1 int optee_enumerate_devices(u32 func); -void optee_cq_wait_init(struct optee_call_queue *cq, - struct optee_call_waiter *w); -void optee_cq_wait_for_completion(struct optee_call_queue *cq, - struct optee_call_waiter *w); -void optee_cq_complete_one(struct optee_call_queue *cq); -void optee_cq_wait_final(struct optee_call_queue *cq, - struct optee_call_waiter *w); +/* + * OCALLs + */ + +void optee_cancel_open_session_ocall(struct optee_session *sess); +void optee_cancel_invoke_function_ocall(struct optee_call_ctx *call_ctx); /* * Small helpers diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 6ac3058c7d0957..37c5a74607e099 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -513,3 +513,38 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; } + +bool optee_rpc_is_ocall(struct optee_rpc_param *param, + struct optee_call_ctx *call_ctx) +{ + u32 func; + u64 shm_type; + + struct tee_shm *shm; + struct optee_msg_arg *arg; + + func = OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0); + if (func != OPTEE_SMC_RPC_FUNC_CMD) + return false; + + shm = reg_pair_to_ptr(param->a1, param->a2); + arg = tee_shm_get_va(shm, 0); + + switch (arg->cmd) { + case OPTEE_MSG_RPC_CMD_SHM_ALLOC: + case OPTEE_MSG_RPC_CMD_SHM_FREE: + shm_type = arg->params[0].u.value.a; + if (shm_type != OPTEE_MSG_RPC_SHM_TYPE_CLIENT_APPL) + break; + fallthrough; + case OPTEE_MSG_RPC_CMD_OCALL: + call_ctx->rpc_shm = shm; + call_ctx->rpc_arg = arg; + call_ctx->thread_id = param->a3; + return true; + default: + break; + } + + return false; +}