From 8516c25f77fb91db2e58dced9a7510d086f5b340 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 15 May 2017 11:09:28 +0200 Subject: [PATCH 01/45] tee: optee: fix uninitialized symbol 'parg' Fixes the static checker warning in optee_release(). error: uninitialized symbol 'parg'. Reported-by: Dan Carpenter Signed-off-by: Jens Wiklander --- drivers/tee/optee/core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 58169e519422d3..857141e29e8022 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -224,13 +224,14 @@ static void optee_release(struct tee_context *ctx) if (!IS_ERR(shm)) { arg = tee_shm_get_va(shm, 0); /* - * If va2pa fails for some reason, we can't call - * optee_close_session(), only free the memory. Secure OS - * will leak sessions and finally refuse more sessions, but - * we will at least let normal world reclaim its memory. + * If va2pa fails for some reason, we can't call into + * secure world, only free the memory. Secure OS will leak + * sessions and finally refuse more sessions, but we will + * at least let normal world reclaim its memory. */ if (!IS_ERR(arg)) - tee_shm_va2pa(shm, arg, &parg); + if (tee_shm_va2pa(shm, arg, &parg)) + arg = NULL; /* prevent usage of parg below */ } list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, From 0c6b38b06885508900458cba689c0bf352a4473d Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Wed, 31 May 2017 13:21:05 +0200 Subject: [PATCH 02/45] tee: add forward declaration for struct device tee_drv.h references struct device, but does not include device.h nor platform_device.h. Therefore, if tee_drv.h is included by some file that does not pull device.h nor platform_device.h beforehand, we have a compile warning. Fix this by adding a forward declaration. Signed-off-by: Jerome Forissier Signed-off-by: Jens Wiklander --- include/linux/tee_drv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 0f175b8f6456c1..cb889afe576b34 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -28,6 +28,7 @@ #define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */ #define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */ +struct device; struct tee_device; struct tee_shm; struct tee_shm_pool; From 0383cf4e703aec480cebb71dc9412dc9ba96a7f2 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 1 Jul 2017 17:56:06 +0530 Subject: [PATCH 03/45] tee: tee_shm: Constify dma_buf_ops structures. dma_buf_ops are not supposed to change at runtime. All functions working with dma_buf_ops provided by work with const dma_buf_ops. So mark the non-const structs as const. File size before: text data bss dec hex filename 2026 112 0 2138 85a drivers/tee/tee_shm.o File size After adding 'const': text data bss dec hex filename 2138 0 0 2138 85a drivers/tee/tee_shm.o Signed-off-by: Arvind Yadav Signed-off-by: Jens Wiklander --- drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index d356d7f025eb4c..4bc7956cefc4af 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -80,7 +80,7 @@ static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) size, vma->vm_page_prot); } -static struct dma_buf_ops tee_shm_dma_buf_ops = { +static const struct dma_buf_ops tee_shm_dma_buf_ops = { .map_dma_buf = tee_shm_op_map_dma_buf, .unmap_dma_buf = tee_shm_op_unmap_dma_buf, .release = tee_shm_op_release, From a00c8193699488885384f2f7aad76f7ab346662d Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Thu, 29 Jun 2017 15:05:04 +0530 Subject: [PATCH 04/45] tee: optee: add const to tee_driver_ops and tee_desc structures Add const to tee_desc structures as they are only passed as an argument to the function tee_device_alloc. This argument is of type const, so declare these structures as const too. Add const to tee_driver_ops structures as they are only stored in the ops field of a tee_desc structure. This field is of type const, so declare these structure types as const. Signed-off-by: Bhumika Goyal Signed-off-by: Jens Wiklander --- drivers/tee/optee/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 857141e29e8022..7952357df9c862 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -259,7 +259,7 @@ static void optee_release(struct tee_context *ctx) } } -static struct tee_driver_ops optee_ops = { +static const struct tee_driver_ops optee_ops = { .get_version = optee_get_version, .open = optee_open, .release = optee_release, @@ -269,13 +269,13 @@ static struct tee_driver_ops optee_ops = { .cancel_req = optee_cancel_req, }; -static struct tee_desc optee_desc = { +static const struct tee_desc optee_desc = { .name = DRIVER_NAME "-clnt", .ops = &optee_ops, .owner = THIS_MODULE, }; -static struct tee_driver_ops optee_supp_ops = { +static const struct tee_driver_ops optee_supp_ops = { .get_version = optee_get_version, .open = optee_open, .release = optee_release, @@ -283,7 +283,7 @@ static struct tee_driver_ops optee_supp_ops = { .supp_send = optee_supp_send, }; -static struct tee_desc optee_supp_desc = { +static const struct tee_desc optee_supp_desc = { .name = DRIVER_NAME "-supp", .ops = &optee_supp_ops, .owner = THIS_MODULE, From 625dacb2d3323d210bebdb9c2c30f9cab44c3219 Mon Sep 17 00:00:00 2001 From: tiger-yu99 Date: Sat, 6 May 2017 00:20:32 +0800 Subject: [PATCH 05/45] tee: optee: interruptible RPC sleep Prior to this patch RPC sleep was uninterruptible since msleep() is uninterruptible. Change to use msleep_interruptible() instead. Signed-off-by: Tiger Yu Reviewed-by: Joakim Bech Signed-off-by: Jerome Forissier Signed-off-by: Jens Wiklander --- drivers/tee/optee/rpc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 8814eca060216a..9488ed1541bc8f 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -140,11 +140,8 @@ static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) msec_to_wait = arg->params[0].u.value.a; - /* set task's state to interruptible sleep */ - set_current_state(TASK_INTERRUPTIBLE); - - /* take a nap */ - msleep(msec_to_wait); + /* Go to interruptible sleep */ + msleep_interruptible(msec_to_wait); arg->ret = TEEC_SUCCESS; return; From 87cc807a799935696b7aa7d5a77ce8e24cbbfe06 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Thu, 16 Feb 2017 09:07:02 +0100 Subject: [PATCH 06/45] tee: indicate privileged dev in gen_caps Mirrors the TEE_DESC_PRIVILEGED bit of struct tee_desc:flags into struct tee_ioctl_version_data:gen_caps as TEE_GEN_CAP_PRIVILEGED in tee_ioctl_version() Reviewed-by: Jerome Forissier Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 5 +++++ include/uapi/linux/tee.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 5c60bf4423e68d..58a5009eacc388 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -90,8 +90,13 @@ static int tee_ioctl_version(struct tee_context *ctx, struct tee_ioctl_version_data vers; ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); + + if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED) + vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED; + if (copy_to_user(uvers, &vers, sizeof(vers))) return -EFAULT; + return 0; } diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 370d8845ab2173..688782e901407c 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -49,6 +49,7 @@ #define TEE_MAX_ARG_SIZE 1024 #define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */ +#define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */ /* * TEE Implementation ID From 864563c596fac49c4fcf7790fdad609d8f4f38c9 Mon Sep 17 00:00:00 2001 From: David Wang Date: Thu, 16 Feb 2017 16:43:44 +0800 Subject: [PATCH 07/45] tee: optee: sync with new naming of interrupts In the latest changes of optee_os, the interrupts' names are changed to "native" and "foreign" interrupts. Signed-off-by: David Wang Signed-off-by: Jerome Forissier Signed-off-by: Jens Wiklander --- drivers/tee/optee/optee_smc.h | 12 ++++++------ drivers/tee/optee/rpc.c | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 13b7c98cdf2537..069c8e1429de8d 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -298,7 +298,7 @@ struct optee_smc_disable_shm_cache_result { OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE) /* - * Resume from RPC (for example after processing an IRQ) + * Resume from RPC (for example after processing a foreign interrupt) * * Call register usage: * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC @@ -383,19 +383,19 @@ struct optee_smc_disable_shm_cache_result { OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE) /* - * Deliver an IRQ in normal world. + * Deliver foreign interrupt to normal world. * * "Call" register usage: - * a0 OPTEE_SMC_RETURN_RPC_IRQ + * a0 OPTEE_SMC_RETURN_RPC_FOREIGN_INTR * a1-7 Resume information, must be preserved * * "Return" register usage: * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. * a1-7 Preserved */ -#define OPTEE_SMC_RPC_FUNC_IRQ 4 -#define OPTEE_SMC_RETURN_RPC_IRQ \ - OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_IRQ) +#define OPTEE_SMC_RPC_FUNC_FOREIGN_INTR 4 +#define OPTEE_SMC_RETURN_RPC_FOREIGN_INTR \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FOREIGN_INTR) /* * Do an RPC request. The supplied struct optee_msg_arg tells which diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 9488ed1541bc8f..cef417f4f4d286 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -371,11 +371,11 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param) shm = reg_pair_to_ptr(param->a1, param->a2); tee_shm_free(shm); break; - case OPTEE_SMC_RPC_FUNC_IRQ: + case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: /* - * An IRQ was raised while secure world was executing, - * since all IRQs are handled in Linux a dummy RPC is - * performed to let Linux take the IRQ through the normal + * A foreign interrupt was raised while secure world was + * executing, since they are handled in Linux a dummy RPC is + * performed to let Linux take the interrupt through the normal * vector. */ break; From 7efd6cdca7a71fbcc27c6aa2c18ee6ec19905e01 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Thu, 19 Nov 2015 13:29:35 +0100 Subject: [PATCH 08/45] tee: add kernel internal client interface Adds a kernel internal TEE client interface to be used by other drivers. Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 124 ++++++++++++++++++++++++++++++++++++---- include/linux/tee_drv.h | 73 +++++++++++++++++++++++ 2 files changed, 186 insertions(+), 11 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 58a5009eacc388..bcc39e263bdd49 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -38,15 +38,13 @@ static DEFINE_SPINLOCK(driver_lock); static struct class *tee_class; static dev_t tee_devt; -static int tee_open(struct inode *inode, struct file *filp) +static struct tee_context *teedev_open(struct tee_device *teedev) { int rc; - struct tee_device *teedev; struct tee_context *ctx; - teedev = container_of(inode->i_cdev, struct tee_device, cdev); if (!tee_device_get(teedev)) - return -EINVAL; + return ERR_PTR(-EINVAL); ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { @@ -56,22 +54,20 @@ static int tee_open(struct inode *inode, struct file *filp) ctx->teedev = teedev; INIT_LIST_HEAD(&ctx->list_shm); - filp->private_data = ctx; rc = teedev->desc->ops->open(ctx); if (rc) goto err; - return 0; + return ctx; err: kfree(ctx); tee_device_put(teedev); - return rc; + return ERR_PTR(rc); + } -static int tee_release(struct inode *inode, struct file *filp) +static void teedev_close_context(struct tee_context *ctx) { - struct tee_context *ctx = filp->private_data; - struct tee_device *teedev = ctx->teedev; struct tee_shm *shm; ctx->teedev->desc->ops->release(ctx); @@ -79,8 +75,25 @@ static int tee_release(struct inode *inode, struct file *filp) list_for_each_entry(shm, &ctx->list_shm, link) shm->ctx = NULL; mutex_unlock(&ctx->teedev->mutex); + tee_device_put(ctx->teedev); kfree(ctx); - tee_device_put(teedev); +} + +static int tee_open(struct inode *inode, struct file *filp) +{ + struct tee_context *ctx; + + ctx = teedev_open(container_of(inode->i_cdev, struct tee_device, cdev)); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + filp->private_data = ctx; + return 0; +} + +static int tee_release(struct inode *inode, struct file *filp) +{ + teedev_close_context(filp->private_data); return 0; } @@ -862,6 +875,95 @@ void *tee_get_drvdata(struct tee_device *teedev) } EXPORT_SYMBOL_GPL(tee_get_drvdata); +struct match_dev_data { + struct tee_ioctl_version_data *vers; + const void *data; + int (*match)(struct tee_ioctl_version_data *, const void *); +}; + +static int match_dev(struct device *dev, const void *data) +{ + const struct match_dev_data *match_data = data; + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + + teedev->desc->ops->get_version(teedev, match_data->vers); + return match_data->match(match_data->vers, match_data->data); +} + +struct tee_context * +tee_client_open_context(struct tee_context *start, + int (*match)(struct tee_ioctl_version_data *, + const void *), + const void *data, struct tee_ioctl_version_data *vers) +{ + struct device *dev = NULL; + struct device *put_dev = NULL; + struct tee_context *ctx = NULL; + struct tee_ioctl_version_data v; + struct match_dev_data match_data = { vers ? vers : &v, data, match }; + + if (start) + dev = &start->teedev->dev; + + do { + dev = class_find_device(tee_class, dev, &match_data, match_dev); + if (!dev) { + ctx = ERR_PTR(-ENOENT); + break; + } + + put_device(put_dev); + put_dev = dev; + + ctx = teedev_open(container_of(dev, struct tee_device, dev)); + } while (IS_ERR(ctx) && PTR_ERR(ctx) != -ENOMEM); + + put_device(put_dev); + return ctx; +} +EXPORT_SYMBOL_GPL(tee_client_open_context); + +void tee_client_close_context(struct tee_context *ctx) +{ + teedev_close_context(ctx); +} +EXPORT_SYMBOL_GPL(tee_client_close_context); + +void tee_client_get_version(struct tee_context *ctx, + struct tee_ioctl_version_data *vers) +{ + ctx->teedev->desc->ops->get_version(ctx->teedev, vers); +} +EXPORT_SYMBOL_GPL(tee_client_get_version); + +int tee_client_open_session(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param) +{ + if (!ctx->teedev->desc->ops->open_session) + return -EINVAL; + return ctx->teedev->desc->ops->open_session(ctx, arg, param); +} +EXPORT_SYMBOL_GPL(tee_client_open_session); + +int tee_client_close_session(struct tee_context *ctx, u32 session) +{ + if (!ctx->teedev->desc->ops->close_session) + return -EINVAL; + return ctx->teedev->desc->ops->close_session(ctx, session); +} +EXPORT_SYMBOL_GPL(tee_client_close_session); + +int tee_client_invoke_func(struct tee_context *ctx, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param) +{ + if (!ctx->teedev->desc->ops->invoke_func) + return -EINVAL; + return ctx->teedev->desc->ops->invoke_func(ctx, arg, param); +} +EXPORT_SYMBOL_GPL(tee_client_invoke_func); + static int __init tee_init(void) { int rc; diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index cb889afe576b34..3502172e033afe 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -275,4 +275,77 @@ int tee_shm_get_id(struct tee_shm *shm); */ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id); +/** + * tee_client_open_context() - Open a TEE context + * @start: if not NULL, continue search after this context + * @match: function to check TEE device + * @data: data for match function + * @vers: if not NULL, version data of TEE device of the context returned + * + * This function does an operation similar to open("/dev/teeX") in user space. + * A returned context must be released with tee_client_close_context(). + * + * Returns a TEE context of the first TEE device matched by the match() + * callback or an ERR_PTR. + */ +struct tee_context * +tee_client_open_context(struct tee_context *start, + int (*match)(struct tee_ioctl_version_data *, + const void *), + const void *data, struct tee_ioctl_version_data *vers); + +/** + * tee_client_close_context() - Close a TEE context + * @ctx: TEE context to close + * + * Note that all sessions previously opened with this context will be + * closed when this function is called. + */ +void tee_client_close_context(struct tee_context *ctx); + +/** + * tee_client_get_version() - Query version of TEE + * @ctx: TEE context to TEE to query + * @vers: Pointer to version data + */ +void tee_client_get_version(struct tee_context *ctx, + struct tee_ioctl_version_data *vers); + +/** + * tee_client_open_session() - Open a session to a Trusted Application + * @ctx: TEE context + * @arg: Open session arguments, see description of + * struct tee_ioctl_open_session_arg + * @param: Parameters passed to the Trusted Application + * + * Returns < 0 on error else see @arg->ret for result. If @arg->ret + * is TEEC_SUCCESS the session identifier is available in @arg->session. + */ +int tee_client_open_session(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param); + +/** + * tee_client_close_session() - Close a session to a Trusted Application + * @ctx: TEE Context + * @session: Session id + * + * Return < 0 on error else 0, regardless the session will not be + * valid after this function has returned. + */ +int tee_client_close_session(struct tee_context *ctx, u32 session); + +/** + * tee_client_invoke_func() - Invoke a function in a Trusted Application + * @ctx: TEE Context + * @arg: Invoke arguments, see description of + * struct tee_ioctl_invoke_arg + * @param: Parameters passed to the Trusted Application + * + * Returns < 0 on error else see @arg->ret for result. + */ +int tee_client_invoke_func(struct tee_context *ctx, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param); + #endif /*__TEE_DRV_H*/ From b599994290e1e799ecdd1d2cb8449c283b705e69 Mon Sep 17 00:00:00 2001 From: Joakim Bech Date: Fri, 1 Apr 2016 14:39:54 +0200 Subject: [PATCH 09/45] 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 6922252f317bca..4182b2cbd0657f 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi @@ -339,15 +339,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 70a42209ad3288c2269511bd7664dc119c3782f6 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 17 Aug 2016 14:01:03 +0200 Subject: [PATCH 10/45] arm64: dt: PSCI for foundation-v8 **not for mainline** Enables use of PSCI for foundation-v8. Signed-off-by: Jens Wiklander --- arch/arm64/boot/dts/arm/foundation-v8.dtsi | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dtsi b/arch/arm64/boot/dts/arm/foundation-v8.dtsi index 7cfa8e414e7f51..0fe7b26178f463 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dtsi +++ b/arch/arm64/boot/dts/arm/foundation-v8.dtsi @@ -24,6 +24,15 @@ serial3 = &v2m_serial3; }; + psci { + compatible = "arm,psci"; + method = "smc"; + cpu_suspend = <0xc4000001>; + cpu_off = <0x84000002>; + cpu_on = <0xc4000003>; + }; + + cpus { #address-cells = <2>; #size-cells = <0>; @@ -32,32 +41,28 @@ device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x0>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; next-level-cache = <&L2_0>; }; cpu@1 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x1>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; next-level-cache = <&L2_0>; }; cpu@2 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x2>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; next-level-cache = <&L2_0>; }; cpu@3 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x3>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; next-level-cache = <&L2_0>; }; From 365afd8da7706584a48168a5961053431f5c9822 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 17 Aug 2016 14:02:51 +0200 Subject: [PATCH 11/45] arm64: dt: use GICv2 for foundation-v8 **not for mainline** Uses GICv2 for foundation-v8. Signed-off-by: Jens Wiklander --- arch/arm64/boot/dts/arm/foundation-v8.dts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dts b/arch/arm64/boot/dts/arm/foundation-v8.dts index 71168077312d5c..3fefc41c43d0d7 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dts +++ b/arch/arm64/boot/dts/arm/foundation-v8.dts @@ -7,15 +7,15 @@ #include "foundation-v8.dtsi" / { - gic: interrupt-controller@2c001000 { + gic: interrupt-controller@2f000000 { compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; #interrupt-cells = <3>; #address-cells = <2>; interrupt-controller; - reg = <0x0 0x2c001000 0 0x1000>, - <0x0 0x2c002000 0 0x2000>, - <0x0 0x2c004000 0 0x2000>, - <0x0 0x2c006000 0 0x2000>; + reg = <0x0 0x2f000000 0 0x10000>, + <0x0 0x2c000000 0 0x2000>, + <0x0 0x2c010000 0 0x2000>, + <0x0 0x2c02F000 0 0x2000>; interrupts = <1 9 0xf04>; }; }; From 413090840d7dc55566e1f254068ff4d07d7525f2 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 17 Aug 2016 14:08:08 +0200 Subject: [PATCH 12/45] arm64: dt: OP-TEE for foundation-v8 **not for mainline** Configures foundation-v8 with OP-TEE. Signed-off-by: Jens Wiklander --- 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 0fe7b26178f463..53be2bbbc62eff 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dtsi +++ b/arch/arm64/boot/dts/arm/foundation-v8.dtsi @@ -19,7 +19,6 @@ aliases { serial0 = &v2m_serial0; - serial1 = &v2m_serial1; serial2 = &v2m_serial2; serial3 = &v2m_serial3; }; @@ -77,6 +76,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 = <1 13 0xf08>, @@ -207,14 +216,6 @@ clock-names = "uartclk", "apb_pclk"; }; - v2m_serial1: uart@0a0000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x0a0000 0x1000>; - interrupts = <6>; - clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; - clock-names = "uartclk", "apb_pclk"; - }; - v2m_serial2: uart@0b0000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x0b0000 0x1000>; @@ -238,4 +239,12 @@ }; }; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; + }; From 5ca01d01dbe83446e641511aacf807bb738aae3f Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 29 Mar 2016 11:01:28 +0200 Subject: [PATCH 13/45] arm64: dt: OP-TEE for Juno **not for mainline** Configures Juno with OP-TEE. Reviewed-by: Pascal Brand Signed-off-by: Jens Wiklander --- arch/arm64/boot/dts/arm/juno-base.dtsi | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index bfe7d683a42e1b..c06c669fad1d3d 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -699,6 +699,18 @@ <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>; + }; + }; + + smb@8000000 { compatible = "simple-bus"; #address-cells = <2>; @@ -738,4 +750,11 @@ interrupt-map-mask = <0 0>; interrupt-map = <0 0 &gic 0 0 0 168 IRQ_TYPE_LEVEL_HIGH>; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; From a92b7faba0304a3db87c1480c4eb67f97f2eb200 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Fri, 2 Dec 2016 09:45:32 +0100 Subject: [PATCH 14/45] 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 53be2bbbc62eff..a6eedf1b0bea98 100644 --- a/arch/arm64/boot/dts/arm/foundation-v8.dtsi +++ b/arch/arm64/boot/dts/arm/foundation-v8.dtsi @@ -83,6 +83,7 @@ optee@0x83000000 { reg = <0x00000000 0x83000000 0 0x01000000>; + no-map; }; }; From 3186541ec3185a418c1bdeb11715e696d7256b44 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 24 May 2016 13:16:44 +0200 Subject: [PATCH 15/45] include/linux/arm-smccc.h: avoid sign extension problem Prior to this patch the ARM_SMCCC_FAST_CALL constant was of an unsigned type causing unwanted sign extension. This patch explicitly selects an unsigned type for the constant. Reviewed-by: Pascal Brand Tested-by: Jens Wiklander (QEMU Aarch64) Reported-by: Saksham Jain Signed-off-by: Jens Wiklander --- include/linux/arm-smccc.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 4c5bca38c6533e..1b38b7b3728bea 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -20,8 +20,9 @@ * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html */ -#define ARM_SMCCC_STD_CALL 0 -#define ARM_SMCCC_FAST_CALL 1 +/* This constant is shifted by 31, make sure it's of an unsigned type */ +#define ARM_SMCCC_STD_CALL 0UL +#define ARM_SMCCC_FAST_CALL 1UL #define ARM_SMCCC_TYPE_SHIFT 31 #define ARM_SMCCC_SMC_32 0 From bbfaaeb02d1884f1082afb3f4e3babf863b963e0 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Thu, 27 Oct 2016 23:18:35 +0200 Subject: [PATCH 16/45] 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 --- drivers/tee/tee_core.c | 38 +++++++++++ drivers/tee/tee_shm.c | 136 ++++++++++++++++++++++++++++++++++++--- include/linux/tee_drv.h | 15 ++++- include/uapi/linux/tee.h | 29 +++++++++ 4 files changed, 207 insertions(+), 11 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index bcc39e263bdd49..6c3bb0772c0dc4 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -151,6 +151,42 @@ static int tee_ioctl_shm_alloc(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) @@ -599,6 +635,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return tee_ioctl_version(ctx, uarg); case TEE_IOC_SHM_ALLOC: return tee_ioctl_shm_alloc(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 4bc7956cefc4af..a67741724c4a78 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -20,10 +20,17 @@ #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->teedev; - struct tee_shm_pool_mgr *poolm; mutex_lock(&teedev->mutex); idr_remove(&teedev->idr, shm->id); @@ -31,14 +38,26 @@ static void tee_shm_release(struct tee_shm *shm) list_del(&shm->link); mutex_unlock(&teedev->mutex); - if (shm->flags & TEE_SHM_DMA_BUF) - poolm = &teedev->pool->dma_buf_mgr; - else - poolm = &teedev->pool->private_mgr; + if (shm->flags & TEE_SHM_EXT_DMA_BUF) { + struct tee_shm_dmabuf_ref *ref; - poolm->ops->free(poolm, shm); - kfree(shm); + 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 { + struct tee_shm_pool_mgr *poolm; + + if (shm->flags & TEE_SHM_DMA_BUF) + poolm = &teedev->pool->dma_buf_mgr; + else + poolm = &teedev->pool->private_mgr; + + poolm->ops->free(poolm, shm); + } + kfree(shm); tee_device_put(teedev); } @@ -190,6 +209,100 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) } EXPORT_SYMBOL_GPL(tee_shm_alloc); +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); + + ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) { + rc = ERR_PTR(-ENOMEM); + goto err; + } + + ref->shm.ctx = ctx; + ref->shm.teedev = ctx->teedev; + 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, &ref->shm.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(&ref->shm.teedev->mutex); + ref->shm.id = idr_alloc(&ref->shm.teedev->idr, &ref->shm, + 1, 0, GFP_KERNEL); + mutex_unlock(&ref->shm.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; + } + + mutex_lock(&ref->shm.teedev->mutex); + list_add_tail(&ref->shm.link, &ctx->list_shm); + mutex_unlock(&ref->shm.teedev->mutex); + + 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); + 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 @@ -197,10 +310,9 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc); */ int tee_shm_get_fd(struct tee_shm *shm) { - u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF; int fd; - if ((shm->flags & req_flags) != req_flags) + if (!(shm->flags & TEE_SHM_DMA_BUF)) return -EINVAL; fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC); @@ -238,6 +350,8 @@ EXPORT_SYMBOL_GPL(tee_shm_free); */ int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa) { + if (!(shm->flags & TEE_SHM_MAPPED)) + return -EINVAL; /* Check that we're in the range of the shm */ if ((char *)va < (char *)shm->kaddr) return -EINVAL; @@ -258,6 +372,8 @@ EXPORT_SYMBOL_GPL(tee_shm_va2pa); */ int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va) { + if (!(shm->flags & TEE_SHM_MAPPED)) + return -EINVAL; /* Check that we're in the range of the shm */ if (pa < shm->paddr) return -EINVAL; @@ -284,6 +400,8 @@ EXPORT_SYMBOL_GPL(tee_shm_pa2va); */ void *tee_shm_get_va(struct tee_shm *shm, size_t offs) { + if (!(shm->flags & TEE_SHM_MAPPED)) + return ERR_PTR(-EINVAL); if (offs >= shm->size) return ERR_PTR(-EINVAL); return (char *)shm->kaddr + offs; diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 3502172e033afe..bfcca888fa66b4 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -25,8 +25,9 @@ * specific TEE driver. */ -#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */ -#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */ +#define TEE_SHM_MAPPED BIT(0) /* Memory mapped by the kernel */ +#define TEE_SHM_DMA_BUF BIT(1) /* Memory with dma-buf handle */ +#define TEE_SHM_EXT_DMA_BUF BIT(2) /* Memory with dma-buf handle */ struct device; struct tee_device; @@ -210,6 +211,16 @@ void *tee_get_drvdata(struct tee_device *teedev); */ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, 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_free() - Free shared memory * @shm: Handle to shared memory to free diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 688782e901407c..81ee3ffbbac479 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -116,6 +116,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 3b7c6c4494c26b2b8aeb9ccef8057f735c11900b Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 3 Jan 2017 17:09:23 +0100 Subject: [PATCH 17/45] 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 c06c669fad1d3d..4eda3fd12e6a46 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -707,6 +707,7 @@ /* Shared memory between secure and non-secure world */ optee@0xfee00000 { reg = <0x00000000 0xfee00000 0 0x00200000>; + no-map; }; }; From e38b5919316c0b513018e1f6c94465f3a7deaf74 Mon Sep 17 00:00:00 2001 From: Joakim Bech Date: Thu, 24 Mar 2016 09:50:14 +0100 Subject: [PATCH 18/45] 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 1c3634fa94bf4e..64dffc8bbf6f42 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts @@ -68,6 +68,13 @@ gpio = <&pio 9 GPIO_ACTIVE_HIGH>; enable-active-high; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; }; &cec { From 962de97d611a66534e08b2cf0fa77e93615bf588 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Fri, 23 Dec 2016 13:13:27 +0100 Subject: [PATCH 19/45] tee: add tee_param_is_memref() for driver use Reviewed-by: Etienne Carriere Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 16 ++-------------- include/linux/tee_drv.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 6c3bb0772c0dc4..2f47909ba42075 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -270,18 +270,6 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, return 0; } -static bool param_is_memref(struct tee_param *param) -{ - switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { - case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: - case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: - case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: - return true; - default: - return false; - } -} - static int tee_ioctl_open_session(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { @@ -345,7 +333,7 @@ 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 (param_is_memref(params + n) && + if (tee_param_is_memref(params + n) && params[n].u.memref.shm) tee_shm_put(params[n].u.memref.shm); kfree(params); @@ -407,7 +395,7 @@ static int tee_ioctl_invoke(struct tee_context *ctx, if (params) { /* Decrease ref count for all valid shared memory pointers */ for (n = 0; n < arg.num_params; n++) - if (param_is_memref(params + n) && + if (tee_param_is_memref(params + n) && params[n].u.memref.shm) tee_shm_put(params[n].u.memref.shm); kfree(params); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index bfcca888fa66b4..b02f91f1285331 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -286,6 +286,18 @@ int tee_shm_get_id(struct tee_shm *shm); */ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id); +static inline bool tee_param_is_memref(struct tee_param *param) +{ + switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + return true; + default: + return false; + } +} + /** * tee_client_open_context() - Open a TEE context * @start: if not NULL, continue search after this context From 4cc5bc19fed279c024dda35850616ef74933a49c Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Fri, 23 Dec 2016 13:13:34 +0100 Subject: [PATCH 20/45] tee: add TEE_IOCTL_PARAM_ATTR_META Adds TEE_IOCTL_PARAM_ATTR_META with can be used to indicate meta parameters when communicating with user space. These meta parameters can be used by supplicant support multiple parallel requests at a time. Reviewed-by: Etienne Carriere Signed-off-by: Jens Wiklander --- drivers/tee/optee/supp.c | 25 +++++++++++++++++++++++++ drivers/tee/tee_core.c | 16 ++++++++++------ include/uapi/linux/tee.h | 7 +++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index b4ea0678a43627..56aa8b929b8cc4 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c @@ -119,6 +119,27 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, return ret; } +static int supp_check_recv_params(size_t num_params, struct tee_param *params) +{ + size_t n; + + /* + * If there's memrefs we need to decrease those as they where + * increased earlier and we'll even refuse to accept any below. + */ + for (n = 0; n < num_params; n++) + if (tee_param_is_memref(params + n) && params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + + /* + * We only expect parameters as TEE_IOCTL_PARAM_ATTR_TYPE_NONE (0). + */ + for (n = 0; n < num_params; n++) + if (params[n].attr) + return -EINVAL; + return 0; +} + /** * optee_supp_recv() - receive request for supplicant * @ctx: context receiving the request @@ -137,6 +158,10 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct optee_supp *supp = &optee->supp; int rc; + rc = supp_check_recv_params(*num_params, param); + if (rc) + return rc; + /* * In case two threads in one supplicant is calling this function * simultaneously we need to protect the data with a mutex which diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 2f47909ba42075..0ed3f5b30a3169 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -201,11 +201,11 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, return -EFAULT; /* All unused attribute bits has to be zero */ - if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK) + if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) return -EINVAL; params[n].attr = ip.attr; - switch (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_OUTPUT: break; @@ -443,8 +443,8 @@ static int params_to_supp(struct tee_context *ctx, struct tee_ioctl_param ip; struct tee_param *p = params + n; - ip.attr = p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK; - switch (p->attr) { + ip.attr = p->attr; + 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: ip.a = p->u.value.a; @@ -508,6 +508,10 @@ static int tee_ioctl_supp_recv(struct tee_context *ctx, if (!params) return -ENOMEM; + rc = params_from_user(ctx, params, num_params, uarg->params); + if (rc) + goto out; + rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params); if (rc) goto out; @@ -537,11 +541,11 @@ static int params_from_supp(struct tee_param *params, size_t num_params, return -EFAULT; /* All unused attribute bits has to be zero */ - if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK) + if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) return -EINVAL; p->attr = ip.attr; - switch (ip.attr) { + switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: /* Only out and in/out values can be updated */ diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 81ee3ffbbac479..31f71276f41376 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -183,6 +183,13 @@ struct tee_ioctl_buf_data { */ #define TEE_IOCTL_PARAM_ATTR_TYPE_MASK 0xff +/* Meta parameter carrying extra information about the message. */ +#define TEE_IOCTL_PARAM_ATTR_META 0x100 + +/* Mask of all known attr bits */ +#define TEE_IOCTL_PARAM_ATTR_MASK \ + (TEE_IOCTL_PARAM_ATTR_TYPE_MASK | TEE_IOCTL_PARAM_ATTR_META) + /* * Matches TEEC_LOGIN_* in GP TEE Client API * Are only defined for GP compliant TEEs From abe2d0c0c18b358a9814fad3a903b62578e3e2bd Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Fri, 23 Dec 2016 13:13:39 +0100 Subject: [PATCH 21/45] optee: support asynchronous supplicant requests Adds support for asynchronous supplicant requests, meaning that the supplicant can process several requests in parallel or block in a request for some time. Acked-by: Etienne Carriere Tested-by: Etienne Carriere (b2260 pager=y/n) Signed-off-by: Jens Wiklander --- drivers/tee/optee/core.c | 11 +- drivers/tee/optee/optee_private.h | 43 ++-- drivers/tee/optee/rpc.c | 4 +- drivers/tee/optee/supp.c | 358 ++++++++++++++++++------------ 4 files changed, 243 insertions(+), 173 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 7952357df9c862..b7492da925672a 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -187,12 +187,12 @@ static int optee_open(struct tee_context *ctx) if (teedev == optee->supp_teedev) { bool busy = true; - mutex_lock(&optee->supp.ctx_mutex); + mutex_lock(&optee->supp.mutex); if (!optee->supp.ctx) { busy = false; optee->supp.ctx = ctx; } - mutex_unlock(&optee->supp.ctx_mutex); + mutex_unlock(&optee->supp.mutex); if (busy) { kfree(ctxdata); return -EBUSY; @@ -252,11 +252,8 @@ static void optee_release(struct tee_context *ctx) ctx->data = NULL; - if (teedev == optee->supp_teedev) { - mutex_lock(&optee->supp.ctx_mutex); - optee->supp.ctx = NULL; - mutex_unlock(&optee->supp.ctx_mutex); - } + if (teedev == optee->supp_teedev) + optee_supp_release(&optee->supp); } static const struct tee_driver_ops optee_ops = { diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index c374cd5943141f..3e7da187acbe59 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -53,36 +53,24 @@ struct optee_wait_queue { * @ctx the context of current connected supplicant. * if !NULL the supplicant device is available for use, * else busy - * @ctx_mutex: held while accessing @ctx - * @func: supplicant function id to call - * @ret: call return value - * @num_params: number of elements in @param - * @param: parameters for @func - * @req_posted: if true, a request has been posted to the supplicant - * @supp_next_send: if true, next step is for supplicant to send response - * @thrd_mutex: held by the thread doing a request to supplicant - * @supp_mutex: held by supplicant while operating on this struct - * @data_to_supp: supplicant is waiting on this for next request - * @data_from_supp: requesting thread is waiting on this to get the result + * @mutex: held while accessing content of this struct + * @req_id: current request id if supplicant is doing synchronous + * communication, else -1 + * @reqs: queued request not yet retrieved by supplicant + * @idr: IDR holding all requests currently being processed + * by supplicant + * @reqs_c: completion used by supplicant when waiting for a + * request to be queued. */ struct optee_supp { + /* Serializes access to this struct */ + struct mutex mutex; struct tee_context *ctx; - /* Serializes access of ctx */ - struct mutex ctx_mutex; - - u32 func; - u32 ret; - size_t num_params; - struct tee_param *param; - - bool req_posted; - bool supp_next_send; - /* Serializes access to this struct for requesting thread */ - struct mutex thrd_mutex; - /* Serializes access to this struct for supplicant threads */ - struct mutex supp_mutex; - struct completion data_to_supp; - struct completion data_from_supp; + + int req_id; + struct list_head reqs; + struct idr idr; + struct completion reqs_c; }; /** @@ -142,6 +130,7 @@ int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len); int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len); void optee_supp_init(struct optee_supp *supp); void optee_supp_uninit(struct optee_supp *supp); +void optee_supp_release(struct optee_supp *supp); int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_param *param); diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index cef417f4f4d286..c6df4317ca9f4e 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -192,10 +192,10 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) if (ret) return ERR_PTR(-ENOMEM); - mutex_lock(&optee->supp.ctx_mutex); + mutex_lock(&optee->supp.mutex); /* Increases count as secure world doesn't have a reference */ shm = tee_shm_get_from_id(optee->supp.ctx, param.u.value.c); - mutex_unlock(&optee->supp.ctx_mutex); + mutex_unlock(&optee->supp.mutex); return shm; } diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index 56aa8b929b8cc4..df35fc01fd3e5e 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c @@ -16,21 +16,61 @@ #include #include "optee_private.h" +struct optee_supp_req { + struct list_head link; + + bool busy; + u32 func; + u32 ret; + size_t num_params; + struct tee_param *param; + + struct completion c; +}; + void optee_supp_init(struct optee_supp *supp) { memset(supp, 0, sizeof(*supp)); - mutex_init(&supp->ctx_mutex); - mutex_init(&supp->thrd_mutex); - mutex_init(&supp->supp_mutex); - init_completion(&supp->data_to_supp); - init_completion(&supp->data_from_supp); + mutex_init(&supp->mutex); + init_completion(&supp->reqs_c); + idr_init(&supp->idr); + INIT_LIST_HEAD(&supp->reqs); + supp->req_id = -1; } void optee_supp_uninit(struct optee_supp *supp) { - mutex_destroy(&supp->ctx_mutex); - mutex_destroy(&supp->thrd_mutex); - mutex_destroy(&supp->supp_mutex); + mutex_destroy(&supp->mutex); + idr_destroy(&supp->idr); +} + +void optee_supp_release(struct optee_supp *supp) +{ + int id; + struct optee_supp_req *req; + struct optee_supp_req *req_tmp; + + mutex_lock(&supp->mutex); + + /* Abort all request retrieved by supplicant */ + idr_for_each_entry(&supp->idr, req, id) { + req->busy = false; + idr_remove(&supp->idr, id); + req->ret = TEEC_ERROR_COMMUNICATION; + complete(&req->c); + } + + /* Abort all queued requests */ + list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { + list_del(&req->link); + req->ret = TEEC_ERROR_COMMUNICATION; + complete(&req->c); + } + + supp->ctx = NULL; + supp->req_id = -1; + + mutex_unlock(&supp->mutex); } /** @@ -44,53 +84,42 @@ void optee_supp_uninit(struct optee_supp *supp) */ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, struct tee_param *param) + { - bool interruptable; struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_supp *supp = &optee->supp; + struct optee_supp_req *req = kzalloc(sizeof(*req), GFP_KERNEL); + bool interruptable; u32 ret; - /* - * Other threads blocks here until we've copied our answer from - * supplicant. - */ - while (mutex_lock_interruptible(&supp->thrd_mutex)) { - /* See comment below on when the RPC can be interrupted. */ - mutex_lock(&supp->ctx_mutex); - interruptable = !supp->ctx; - mutex_unlock(&supp->ctx_mutex); - if (interruptable) - return TEEC_ERROR_COMMUNICATION; - } + if (!req) + return TEEC_ERROR_OUT_OF_MEMORY; - /* - * We have exclusive access now since the supplicant at this - * point is either doing a - * wait_for_completion_interruptible(&supp->data_to_supp) or is in - * userspace still about to do the ioctl() to enter - * optee_supp_recv() below. - */ + init_completion(&req->c); + req->func = func; + req->num_params = num_params; + req->param = param; - supp->func = func; - supp->num_params = num_params; - supp->param = param; - supp->req_posted = true; + /* Insert the request in the request list */ + mutex_lock(&supp->mutex); + list_add_tail(&req->link, &supp->reqs); + mutex_unlock(&supp->mutex); - /* Let supplicant get the data */ - complete(&supp->data_to_supp); + /* Tell an eventual waiter there's a new request */ + complete(&supp->reqs_c); /* * Wait for supplicant to process and return result, once we've - * returned from wait_for_completion(data_from_supp) we have + * returned from wait_for_completion(&req->c) successfully we have * exclusive access again. */ - while (wait_for_completion_interruptible(&supp->data_from_supp)) { - mutex_lock(&supp->ctx_mutex); + while (wait_for_completion_interruptible(&req->c)) { + mutex_lock(&supp->mutex); interruptable = !supp->ctx; if (interruptable) { /* * There's no supplicant available and since the - * supp->ctx_mutex currently is held none can + * supp->mutex currently is held none can * become available until the mutex released * again. * @@ -101,28 +130,65 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, * will serve all requests in a timely manner and * interrupting then wouldn't make sense. */ - supp->ret = TEEC_ERROR_COMMUNICATION; - init_completion(&supp->data_to_supp); + interruptable = !req->busy; + if (!req->busy) + list_del(&req->link); } - mutex_unlock(&supp->ctx_mutex); - if (interruptable) + mutex_unlock(&supp->mutex); + + if (interruptable) { + req->ret = TEEC_ERROR_COMMUNICATION; break; + } } - ret = supp->ret; - supp->param = NULL; - supp->req_posted = false; - - /* We're done, let someone else talk to the supplicant now. */ - mutex_unlock(&supp->thrd_mutex); + ret = req->ret; + kfree(req); return ret; } -static int supp_check_recv_params(size_t num_params, struct tee_param *params) +static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, + int num_params, int *id) +{ + struct optee_supp_req *req; + + if (supp->req_id != -1) { + /* + * Supplicant should not mix synchronous and asnynchronous + * requests. + */ + return ERR_PTR(-EINVAL); + } + + if (list_empty(&supp->reqs)) + return NULL; + + req = list_first_entry(&supp->reqs, struct optee_supp_req, link); + + if (num_params < req->num_params) { + /* Not enough room for parameters */ + return ERR_PTR(-EINVAL); + } + + *id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); + if (*id < 0) + return ERR_PTR(-ENOMEM); + + list_del(&req->link); + req->busy = true; + + return req; +} + +static int supp_check_recv_params(size_t num_params, struct tee_param *params, + size_t *num_meta) { size_t n; + if (!num_params) + return -EINVAL; + /* * If there's memrefs we need to decrease those as they where * increased earlier and we'll even refuse to accept any below. @@ -132,11 +198,20 @@ static int supp_check_recv_params(size_t num_params, struct tee_param *params) tee_shm_put(params[n].u.memref.shm); /* - * We only expect parameters as TEE_IOCTL_PARAM_ATTR_TYPE_NONE (0). + * We only expect parameters as TEE_IOCTL_PARAM_ATTR_TYPE_NONE with + * or without the TEE_IOCTL_PARAM_ATTR_META bit set. */ for (n = 0; n < num_params; n++) - if (params[n].attr) + if (params[n].attr && + params[n].attr != TEE_IOCTL_PARAM_ATTR_META) return -EINVAL; + + /* At most we'll need one meta parameter so no need to check for more */ + if (params->attr == TEE_IOCTL_PARAM_ATTR_META) + *num_meta = 1; + else + *num_meta = 0; + return 0; } @@ -156,69 +231,99 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); struct optee_supp *supp = &optee->supp; + struct optee_supp_req *req = NULL; + int id; + size_t num_meta; int rc; - rc = supp_check_recv_params(*num_params, param); + rc = supp_check_recv_params(*num_params, param, &num_meta); if (rc) return rc; - /* - * In case two threads in one supplicant is calling this function - * simultaneously we need to protect the data with a mutex which - * we'll release before returning. - */ - mutex_lock(&supp->supp_mutex); + while (true) { + mutex_lock(&supp->mutex); + req = supp_pop_entry(supp, *num_params - num_meta, &id); + mutex_unlock(&supp->mutex); + + if (req) { + if (IS_ERR(req)) + return PTR_ERR(req); + break; + } - if (supp->supp_next_send) { /* - * optee_supp_recv() has been called again without - * a optee_supp_send() in between. Supplicant has - * probably been restarted before it was able to - * write back last result. Abort last request and - * wait for a new. + * If we didn't get a request we'll block in + * wait_for_completion() to avoid needless spinning. + * + * This is where supplicant will be hanging most of + * the time, let's make this interruptable so we + * can easily restart supplicant if needed. */ - if (supp->req_posted) { - supp->ret = TEEC_ERROR_COMMUNICATION; - supp->supp_next_send = false; - complete(&supp->data_from_supp); - } + if (wait_for_completion_interruptible(&supp->reqs_c)) + return -ERESTARTSYS; } - /* - * This is where supplicant will be hanging most of the - * time, let's make this interruptable so we can easily - * restart supplicant if needed. - */ - if (wait_for_completion_interruptible(&supp->data_to_supp)) { - rc = -ERESTARTSYS; - goto out; + if (num_meta) { + /* + * tee-supplicant support meta parameters -> requsts can be + * processed asynchronously. + */ + param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | + TEE_IOCTL_PARAM_ATTR_META; + param->u.value.a = id; + param->u.value.b = 0; + param->u.value.c = 0; + } else { + mutex_lock(&supp->mutex); + supp->req_id = id; + mutex_unlock(&supp->mutex); } - /* We have exlusive access to the data */ + *func = req->func; + *num_params = req->num_params + num_meta; + memcpy(param + num_meta, req->param, + sizeof(struct tee_param) * req->num_params); - if (*num_params < supp->num_params) { - /* - * Not enough room for parameters, tell supplicant - * it failed and abort last request. - */ - supp->ret = TEEC_ERROR_COMMUNICATION; - rc = -EINVAL; - complete(&supp->data_from_supp); - goto out; + return 0; +} + +static struct optee_supp_req *supp_pop_req(struct optee_supp *supp, + size_t num_params, + struct tee_param *param, + size_t *num_meta) +{ + struct optee_supp_req *req; + int id; + size_t nm; + const u32 attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | + TEE_IOCTL_PARAM_ATTR_META; + + if (!num_params) + return ERR_PTR(-EINVAL); + + if (supp->req_id == -1) { + if (param->attr != attr) + return ERR_PTR(-EINVAL); + id = param->u.value.a; + nm = 1; + } else { + id = supp->req_id; + nm = 0; } - *func = supp->func; - *num_params = supp->num_params; - memcpy(param, supp->param, - sizeof(struct tee_param) * supp->num_params); + req = idr_find(&supp->idr, id); + if (!req) + return ERR_PTR(-ENOENT); + + if ((num_params - nm) != req->num_params) + return ERR_PTR(-EINVAL); - /* Allow optee_supp_send() below to do its work */ - supp->supp_next_send = true; + req->busy = false; + idr_remove(&supp->idr, id); + supp->req_id = -1; + *num_meta = nm; - rc = 0; -out: - mutex_unlock(&supp->supp_mutex); - return rc; + return req; } /** @@ -236,63 +341,42 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); struct optee_supp *supp = &optee->supp; + struct optee_supp_req *req; size_t n; - int rc = 0; + size_t num_meta; - /* - * We still have exclusive access to the data since that's how we - * left it when returning from optee_supp_read(). - */ + mutex_lock(&supp->mutex); + req = supp_pop_req(supp, num_params, param, &num_meta); + mutex_unlock(&supp->mutex); - /* See comment on mutex in optee_supp_read() above */ - mutex_lock(&supp->supp_mutex); - - if (!supp->supp_next_send) { - /* - * Something strange is going on, supplicant shouldn't - * enter optee_supp_send() in this state - */ - rc = -ENOENT; - goto out; - } - - if (num_params != supp->num_params) { - /* - * Something is wrong, let supplicant restart. Next call to - * optee_supp_recv() will give an error to the requesting - * thread and release it. - */ - rc = -EINVAL; - goto out; + if (IS_ERR(req)) { + /* Something is wrong, let supplicant restart. */ + return PTR_ERR(req); } /* Update out and in/out parameters */ - for (n = 0; n < num_params; n++) { - struct tee_param *p = supp->param + n; + for (n = 0; n < req->num_params; n++) { + struct tee_param *p = req->param + 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: - p->u.value.a = param[n].u.value.a; - p->u.value.b = param[n].u.value.b; - p->u.value.c = param[n].u.value.c; + p->u.value.a = param[n + num_meta].u.value.a; + p->u.value.b = param[n + num_meta].u.value.b; + p->u.value.c = param[n + num_meta].u.value.c; break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: - p->u.memref.size = param[n].u.memref.size; + p->u.memref.size = param[n + num_meta].u.memref.size; break; default: break; } } - supp->ret = ret; - - /* Allow optee_supp_recv() above to do its work */ - supp->supp_next_send = false; + req->ret = ret; /* Let the requesting thread continue */ - complete(&supp->data_from_supp); -out: - mutex_unlock(&supp->supp_mutex); - return rc; + complete(&req->c); + + return 0; } From e8c8bf849169fcff4e3543409e559fb80e7cbfa8 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 25 Oct 2016 19:03:38 +0300 Subject: [PATCH 22/45] OP-TEE Benchmark **not for mainline** Add Benchmark support Reviewed-by: Joakim Bech Signed-off-by: Igor Opaniuk --- 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 | 5 + drivers/tee/optee/optee_bench.h | 69 ++++++++++++++ drivers/tee/optee/optee_msg.h | 8 ++ drivers/tee/optee/rpc.c | 49 ++++++++++ 8 files changed, 301 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 0126de898036cd..524c7a80fde4e8 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -5,3 +5,10 @@ config OPTEE help This implements the OP-TEE Trusted Execution Environment (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 92fe5789bcce29..47b6a67dbe6783 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -3,3 +3,4 @@ optee-objs += core.o optee-objs += call.o optee-objs += rpc.o optee-objs += supp.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 f7b7b404c990bf..4ccaa729333866 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -21,6 +21,7 @@ #include #include "optee_private.h" #include "optee_smc.h" +#include "optee_bench.h" struct optee_call_waiter { struct list_head list_node; @@ -144,10 +145,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 b7492da925672a..ff3940ceb5d2ff 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -26,6 +26,7 @@ #include #include #include +#include "optee_bench.h" #include "optee_private.h" #include "optee_smc.h" @@ -599,6 +600,8 @@ static int __init optee_driver_init(void) optee_svc = optee; + optee_bm_enable(); + return 0; } module_init(optee_driver_init); @@ -610,6 +613,8 @@ static void __exit optee_driver_exit(void) optee_svc = NULL; if (optee) optee_remove(optee); + + optee_bm_disable(); } module_exit(optee_driver_exit); diff --git a/drivers/tee/optee/optee_bench.h b/drivers/tee/optee/optee_bench.h new file mode 100644 index 00000000000000..16453a085ceccf --- /dev/null +++ b/drivers/tee/optee/optee_bench.h @@ -0,0 +1,69 @@ +/* + * 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 +#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 dd7a06ee04629e..67444122e32143 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -415,4 +415,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 c6df4317ca9f4e..4a4f66a89e2963 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -16,8 +16,10 @@ #include #include +#include #include #include +#include "optee_bench.h" #include "optee_private.h" #include "optee_smc.h" @@ -307,6 +309,50 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, arg->ret = TEEC_SUCCESS; } +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) { @@ -334,6 +380,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 ed6ea82bdcbe946e4842c46c2371436b035701f1 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Wed, 31 May 2017 10:42:55 +0200 Subject: [PATCH 23/45] tee: optee: optee_bench.h: remove useless include **not for mainline** is not used by the benchmark code and happens to introduce a compile warning if it is included without (or, more exactly, ). In file included from drivers/tee/optee/optee_bench.h:19:0, from drivers/tee/optee/bench.c:15: ./include/linux/tee_drv.h:127:16: warning: 'struct device' declared inside parameter list will not be visible outside of this definition or declaration struct device *dev, ^~~~~~ Fixes: 4867f93a5701 ("OP-TEE Benchmark **not for mainline**") Signed-off-by: Jerome Forissier Reviewed-by: Joakim Bech Acked-by: Jens Wiklander --- drivers/tee/optee/optee_bench.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tee/optee/optee_bench.h b/drivers/tee/optee/optee_bench.h index 16453a085ceccf..985e6a011f58f7 100644 --- a/drivers/tee/optee/optee_bench.h +++ b/drivers/tee/optee/optee_bench.h @@ -16,7 +16,6 @@ #define _OPTEE_BENCH_H #include -#include /* * Cycle count divider is enabled (in PMCR), From cef44f4d4f992a9a5d0f7ebfd8188de65b91b331 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 14 Aug 2017 17:13:34 +0200 Subject: [PATCH 24/45] 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 60fd760bc37de7235ea602f0176022ea1dbfbe4a Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 14 Aug 2017 17:16:39 +0200 Subject: [PATCH 25/45] 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 | 260 ++++++++++++++++++ drivers/staging/android/uapi/ion.h | 3 + 5 files changed, 296 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 a517b2d29f1bb6..d5aa7e404b9d8c 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -42,3 +42,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 eb7eeed6ae409b..4dae85e58f2459 100644 --- a/drivers/staging/android/ion/Makefile +++ b/drivers/staging/android/ion/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_ION_SYSTEM_HEAP) += ion_system_heap.o ion_page_pool.o obj-$(CONFIG_ION_CARVEOUT_HEAP) += ion_carveout_heap.o obj-$(CONFIG_ION_CHUNK_HEAP) += ion_chunk_heap.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..fe8eae5a6f05d9 --- /dev/null +++ b/drivers/staging/android/ion/ion_unmapped_heap.c @@ -0,0 +1,260 @@ +/* + * 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(struct ion_platform_heap *pheap) +{ + struct ion_unmapped_heap *umh; + + if (pheap->type != ION_HEAP_TYPE_UNMAPPED) + return NULL; + + 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 = pheap->base; + umh->size = pheap->size; + + gen_pool_add(umh->pool, umh->base, pheap->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; + struct ion_platform_heap pheap = { + .type = ION_HEAP_TYPE_UNMAPPED, + .base = CONFIG_ION_DUMMY_UNMAPPED_BASE, + .size = CONFIG_ION_DUMMY_UNMAPPED_SIZE, + }; + + heap = ion_unmapped_heap_create(&pheap); + 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 b76db1b2e197ce..4173d81cc8d305 100644 --- a/drivers/staging/android/uapi/ion.h +++ b/drivers/staging/android/uapi/ion.h @@ -28,6 +28,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 @@ -38,6 +40,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 9a5d477a02b7d4bae85c5383fdcda9aa7f153d0e Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Fri, 8 Sep 2017 13:53:10 +0200 Subject: [PATCH 26/45] 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 d5aa7e404b9d8c..a38824843ef21a 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -45,7 +45,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 b5d85ec58d535244e46e89d15e0f53f96589f536 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Thu, 14 Sep 2017 17:52:51 +0200 Subject: [PATCH 27/45] 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 a38824843ef21a..075683bd8493e0 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -45,7 +45,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 76b0502f8b749a98c32a1c307c9016a9c7c1b348 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 19 Jul 2017 12:11:32 +0200 Subject: [PATCH 28/45] [NEW SHM]tee: flexible shared memory pool creation Makes creation of shm pools more flexible by adding new more primitive functions to allocate a shm pool. This makes it easier to add driver specific shm pool management. Signed-off-by: Jens Wiklander --- drivers/tee/tee_private.h | 57 +------------ drivers/tee/tee_shm.c | 8 +- drivers/tee/tee_shm_pool.c | 165 +++++++++++++++++++++++-------------- include/linux/tee_drv.h | 91 ++++++++++++++++++++ 4 files changed, 199 insertions(+), 122 deletions(-) diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h index 21cb6be8bce94d..2bc2b5ab1661ac 100644 --- a/drivers/tee/tee_private.h +++ b/drivers/tee/tee_private.h @@ -21,68 +21,15 @@ #include #include -struct tee_device; - -/** - * struct tee_shm - shared memory object - * @teedev: device used to allocate the object - * @ctx: context using the object, if NULL the context is gone - * @link link element - * @paddr: physical address of the shared memory - * @kaddr: virtual address of the shared memory - * @size: size of shared memory - * @dmabuf: dmabuf used to for exporting to user space - * @flags: defined by TEE_SHM_* in tee_drv.h - * @id: unique id of a shared memory object on this device - */ -struct tee_shm { - struct tee_device *teedev; - struct tee_context *ctx; - struct list_head link; - phys_addr_t paddr; - void *kaddr; - size_t size; - struct dma_buf *dmabuf; - u32 flags; - int id; -}; - -struct tee_shm_pool_mgr; - -/** - * struct tee_shm_pool_mgr_ops - shared memory pool manager operations - * @alloc: called when allocating shared memory - * @free: called when freeing shared memory - */ -struct tee_shm_pool_mgr_ops { - int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm, - size_t size); - void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm); -}; - -/** - * struct tee_shm_pool_mgr - shared memory manager - * @ops: operations - * @private_data: private data for the shared memory manager - */ -struct tee_shm_pool_mgr { - const struct tee_shm_pool_mgr_ops *ops; - void *private_data; -}; - /** * struct tee_shm_pool - shared memory pool * @private_mgr: pool manager for shared memory only between kernel * and secure world * @dma_buf_mgr: pool manager for shared memory exported to user space - * @destroy: called when destroying the pool - * @private_data: private data for the pool */ struct tee_shm_pool { - struct tee_shm_pool_mgr private_mgr; - struct tee_shm_pool_mgr dma_buf_mgr; - void (*destroy)(struct tee_shm_pool *pool); - void *private_data; + struct tee_shm_pool_mgr *private_mgr; + struct tee_shm_pool_mgr *dma_buf_mgr; }; #define TEE_DEVICE_FLAG_REGISTERED 0x1 diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index a67741724c4a78..53893331b4f421 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -50,9 +50,9 @@ static void tee_shm_release(struct tee_shm *shm) struct tee_shm_pool_mgr *poolm; if (shm->flags & TEE_SHM_DMA_BUF) - poolm = &teedev->pool->dma_buf_mgr; + poolm = teedev->pool->dma_buf_mgr; else - poolm = &teedev->pool->private_mgr; + poolm = teedev->pool->private_mgr; poolm->ops->free(poolm, shm); } @@ -158,9 +158,9 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) shm->teedev = teedev; shm->ctx = ctx; if (flags & TEE_SHM_DMA_BUF) - poolm = &teedev->pool->dma_buf_mgr; + poolm = teedev->pool->dma_buf_mgr; else - poolm = &teedev->pool->private_mgr; + poolm = teedev->pool->private_mgr; rc = poolm->ops->alloc(poolm, shm, size); if (rc) { diff --git a/drivers/tee/tee_shm_pool.c b/drivers/tee/tee_shm_pool.c index fb4f8522a5269c..e6d4b9e4a86494 100644 --- a/drivers/tee/tee_shm_pool.c +++ b/drivers/tee/tee_shm_pool.c @@ -44,49 +44,18 @@ static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm, shm->kaddr = NULL; } +static void pool_op_gen_destroy_poolmgr(struct tee_shm_pool_mgr *poolm) +{ + gen_pool_destroy(poolm->private_data); + kfree(poolm); +} + static const struct tee_shm_pool_mgr_ops pool_ops_generic = { .alloc = pool_op_gen_alloc, .free = pool_op_gen_free, + .destroy_poolmgr = pool_op_gen_destroy_poolmgr, }; -static void pool_res_mem_destroy(struct tee_shm_pool *pool) -{ - gen_pool_destroy(pool->private_mgr.private_data); - gen_pool_destroy(pool->dma_buf_mgr.private_data); -} - -static int pool_res_mem_mgr_init(struct tee_shm_pool_mgr *mgr, - struct tee_shm_pool_mem_info *info, - int min_alloc_order) -{ - size_t page_mask = PAGE_SIZE - 1; - struct gen_pool *genpool = NULL; - int rc; - - /* - * Start and end must be page aligned - */ - if ((info->vaddr & page_mask) || (info->paddr & page_mask) || - (info->size & page_mask)) - return -EINVAL; - - genpool = gen_pool_create(min_alloc_order, -1); - if (!genpool) - return -ENOMEM; - - gen_pool_set_algo(genpool, gen_pool_best_fit, NULL); - rc = gen_pool_add_virt(genpool, info->vaddr, info->paddr, info->size, - -1); - if (rc) { - gen_pool_destroy(genpool); - return rc; - } - - mgr->private_data = genpool; - mgr->ops = &pool_ops_generic; - return 0; -} - /** * tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved * memory range @@ -104,42 +73,109 @@ struct tee_shm_pool * tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info, struct tee_shm_pool_mem_info *dmabuf_info) { - struct tee_shm_pool *pool = NULL; - int ret; - - pool = kzalloc(sizeof(*pool), GFP_KERNEL); - if (!pool) { - ret = -ENOMEM; - goto err; - } + struct tee_shm_pool_mgr *priv_mgr; + struct tee_shm_pool_mgr *dmabuf_mgr; + void *rc; /* * Create the pool for driver private shared memory */ - ret = pool_res_mem_mgr_init(&pool->private_mgr, priv_info, - 3 /* 8 byte aligned */); - if (ret) - goto err; + rc = tee_shm_pool_mgr_alloc_res_mem(priv_info->vaddr, priv_info->paddr, + priv_info->size, + 3 /* 8 byte aligned */); + if (IS_ERR(rc)) + return rc; + priv_mgr = rc; /* * Create the pool for dma_buf shared memory */ - ret = pool_res_mem_mgr_init(&pool->dma_buf_mgr, dmabuf_info, - PAGE_SHIFT); - if (ret) + rc = tee_shm_pool_mgr_alloc_res_mem(dmabuf_info->vaddr, + dmabuf_info->paddr, + dmabuf_info->size, PAGE_SHIFT); + if (IS_ERR(rc)) + goto err_free_priv_mgr; + dmabuf_mgr = rc; + + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); + if (IS_ERR(rc)) + goto err_free_dmabuf_mgr; + + return rc; + +err_free_dmabuf_mgr: + tee_shm_pool_mgr_destroy(dmabuf_mgr); +err_free_priv_mgr: + tee_shm_pool_mgr_destroy(priv_mgr); + + return rc; +} +EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem); + +struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr, + phys_addr_t paddr, + size_t size, + int min_alloc_order) +{ + const size_t page_mask = PAGE_SIZE - 1; + struct tee_shm_pool_mgr *mgr; + int rc; + + /* Start and end must be page aligned */ + if (vaddr & page_mask || paddr & page_mask || size & page_mask) + return ERR_PTR(-EINVAL); + + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + if (!mgr) + return ERR_PTR(-ENOMEM); + + mgr->private_data = gen_pool_create(min_alloc_order, -1); + if (!mgr->private_data) { + rc = -ENOMEM; goto err; + } - pool->destroy = pool_res_mem_destroy; - return pool; + gen_pool_set_algo(mgr->private_data, gen_pool_best_fit, NULL); + rc = gen_pool_add_virt(mgr->private_data, vaddr, paddr, size, -1); + if (rc) { + gen_pool_destroy(mgr->private_data); + goto err; + } + + mgr->ops = &pool_ops_generic; + + return mgr; err: - if (ret == -ENOMEM) - pr_err("%s: can't allocate memory for res_mem shared memory pool\n", __func__); - if (pool && pool->private_mgr.private_data) - gen_pool_destroy(pool->private_mgr.private_data); - kfree(pool); - return ERR_PTR(ret); + kfree(mgr); + + return ERR_PTR(rc); } -EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem); +EXPORT_SYMBOL_GPL(tee_shm_pool_mgr_alloc_res_mem); + +static bool check_mgr_ops(struct tee_shm_pool_mgr *mgr) +{ + return mgr && mgr->ops && mgr->ops->alloc && mgr->ops->free && + mgr->ops->destroy_poolmgr; +} + +struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr, + struct tee_shm_pool_mgr *dmabuf_mgr) +{ + struct tee_shm_pool *pool; + + if (!check_mgr_ops(priv_mgr) || !check_mgr_ops(dmabuf_mgr)) + return ERR_PTR(-EINVAL); + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return ERR_PTR(-ENOMEM); + + pool->private_mgr = priv_mgr; + pool->dma_buf_mgr = dmabuf_mgr; + + return pool; +} +EXPORT_SYMBOL_GPL(tee_shm_pool_alloc); /** * tee_shm_pool_free() - Free a shared memory pool @@ -150,7 +186,10 @@ EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem); */ void tee_shm_pool_free(struct tee_shm_pool *pool) { - pool->destroy(pool); + if (pool->private_mgr) + tee_shm_pool_mgr_destroy(pool->private_mgr); + if (pool->dma_buf_mgr) + tee_shm_pool_mgr_destroy(pool->dma_buf_mgr); kfree(pool); } EXPORT_SYMBOL_GPL(tee_shm_pool_free); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index b02f91f1285331..ce3c239ad34f57 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -150,6 +150,97 @@ int tee_device_register(struct tee_device *teedev); */ void tee_device_unregister(struct tee_device *teedev); +/** + * struct tee_shm - shared memory object + * @teedev: device used to allocate the object + * @ctx: context using the object, if NULL the context is gone + * @link link element + * @paddr: physical address of the shared memory + * @kaddr: virtual address of the shared memory + * @size: size of shared memory + * @offset: offset of buffer in user space + * @pages: locked pages from userspace + * @num_pages: number of locked pages + * @dmabuf: dmabuf used to for exporting to user space + * @flags: defined by TEE_SHM_* in tee_drv.h + * @id: unique id of a shared memory object on this device + * + * This pool is only supposed to be accessed directly from the TEE + * subsystem and from drivers that implements their own shm pool manager. + */ +struct tee_shm { + struct tee_device *teedev; + struct tee_context *ctx; + struct list_head link; + phys_addr_t paddr; + void *kaddr; + size_t size; + unsigned int offset; + struct page **pages; + size_t num_pages; + struct dma_buf *dmabuf; + u32 flags; + int id; +}; + +/** + * struct tee_shm_pool_mgr - shared memory manager + * @ops: operations + * @private_data: private data for the shared memory manager + */ +struct tee_shm_pool_mgr { + const struct tee_shm_pool_mgr_ops *ops; + void *private_data; +}; + +/** + * struct tee_shm_pool_mgr_ops - shared memory pool manager operations + * @alloc: called when allocating shared memory + * @free: called when freeing shared memory + * @destroy_poolmgr: called when destroying the pool manager + */ +struct tee_shm_pool_mgr_ops { + int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm, + size_t size); + void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm); + void (*destroy_poolmgr)(struct tee_shm_pool_mgr *poolmgr); +}; + +/** + * tee_shm_pool_alloc() - Create a shared memory pool from shm managers + * @priv_mgr: manager for driver private shared memory allocations + * @dmabuf_mgr: manager for dma-buf shared memory allocations + * + * Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied + * in @dmabuf, others will use the range provided by @priv. + * + * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure. + */ +struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr, + struct tee_shm_pool_mgr *dmabuf_mgr); + +/* + * tee_shm_pool_mgr_alloc_res_mem() - Create a shm manager for reserved + * memory + * @vaddr: Virtual address of start of pool + * @paddr: Physical address of start of pool + * @size: Size in bytes of the pool + * + * @returns pointer to a 'struct tee_shm_pool_mgr' or an ERR_PTR on failure. + */ +struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr, + phys_addr_t paddr, + size_t size, + int min_alloc_order); + +/** + * tee_shm_pool_mgr_destroy() - Free a shared memory manager + */ +static inline void tee_shm_pool_mgr_destroy(struct tee_shm_pool_mgr *poolm) +{ + poolm->ops->destroy_poolmgr(poolm); +} + /** * struct tee_shm_pool_mem_info - holds information needed to create a shared * memory pool From db3638f5b3c7228f23f2e95d1b5b2327b28e3aae Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Thu, 20 Oct 2016 16:02:12 +0200 Subject: [PATCH 29/45] [NEW SHM]tee: add register user memory Added new ioctl to allow users register own buffers as a shared memory. Signed-off-by: Jens Wiklander Signed-off-by: Volodymyr Babchuk --- drivers/tee/tee_core.c | 41 ++++++++- drivers/tee/tee_shm.c | 187 +++++++++++++++++++++++++++++++++++---- include/linux/tee_drv.h | 39 ++++++++ include/uapi/linux/tee.h | 31 +++++++ 4 files changed, 277 insertions(+), 21 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 0ed3f5b30a3169..45acb27e5af2bc 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -127,8 +127,6 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx, if (data.flags) return -EINVAL; - data.id = -1; - shm = tee_shm_alloc(ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); if (IS_ERR(shm)) return PTR_ERR(shm); @@ -151,6 +149,43 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx, return ret; } +static int +tee_ioctl_shm_register(struct tee_context *ctx, + struct tee_ioctl_shm_register_data __user *udata) +{ + long ret; + 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) + return -EINVAL; + + shm = tee_shm_register(ctx, data.addr, data.length, + TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + data.id = shm->id; + data.flags = shm->flags; + data.length = 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 tee_ioctl_shm_register_fd(struct tee_context *ctx, struct tee_ioctl_shm_register_fd_data __user *udata) { @@ -627,6 +662,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return tee_ioctl_version(ctx, uarg); case TEE_IOC_SHM_ALLOC: 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: diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 53893331b4f421..623e792664a44a 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -46,7 +46,7 @@ static void tee_shm_release(struct tee_shm *shm) DMA_BIDIRECTIONAL); dma_buf_detach(shm->dmabuf, ref->attach); dma_buf_put(ref->dmabuf); - } else { + } else if (shm->flags & TEE_SHM_POOL) { struct tee_shm_pool_mgr *poolm; if (shm->flags & TEE_SHM_DMA_BUF) @@ -55,6 +55,18 @@ static void tee_shm_release(struct tee_shm *shm) poolm = teedev->pool->private_mgr; poolm->ops->free(poolm, shm); + } else if (shm->flags & TEE_SHM_REGISTER) { + size_t n; + int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm); + + if (rc) + dev_err(teedev->dev.parent, + "unregister shm %p failed: %d", shm, rc); + + for (n = 0; n < shm->num_pages; n++) + put_page(shm->pages[n]); + + kfree(shm->pages); } kfree(shm); @@ -95,6 +107,10 @@ static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) struct tee_shm *shm = dmabuf->priv; size_t size = vma->vm_end - vma->vm_start; + /* Refuse sharing shared memory provided by application */ + if (shm->flags & TEE_SHM_REGISTER) + return -EINVAL; + return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, size, vma->vm_page_prot); } @@ -108,26 +124,20 @@ static const struct dma_buf_ops tee_shm_dma_buf_ops = { .mmap = tee_shm_op_mmap, }; -/** - * tee_shm_alloc() - Allocate shared memory - * @ctx: Context that allocates the shared memory - * @size: Requested size of shared memory - * @flags: Flags setting properties for the requested shared memory. - * - * Memory allocated as global shared memory is automatically freed when the - * TEE file pointer is closed. The @flags field uses the bits defined by - * TEE_SHM_* in . TEE_SHM_MAPPED must currently always be - * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and - * associated with a dma-buf handle, else driver private memory. - */ -struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) +struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, + struct tee_device *teedev, + size_t size, u32 flags) { - struct tee_device *teedev = ctx->teedev; struct tee_shm_pool_mgr *poolm = NULL; struct tee_shm *shm; void *ret; int rc; + if (ctx && ctx->teedev != teedev) { + dev_err(teedev->dev.parent, "ctx and teedev mismatch\n"); + return ERR_PTR(-EINVAL); + } + if (!(flags & TEE_SHM_MAPPED)) { dev_err(teedev->dev.parent, "only mapped allocations supported\n"); @@ -154,7 +164,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) goto err_dev_put; } - shm->flags = flags; + shm->flags = flags | TEE_SHM_POOL; shm->teedev = teedev; shm->ctx = ctx; if (flags & TEE_SHM_DMA_BUF) @@ -190,9 +200,12 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) goto err_rem; } } - mutex_lock(&teedev->mutex); - list_add_tail(&shm->link, &ctx->list_shm); - mutex_unlock(&teedev->mutex); + + if (ctx) { + mutex_lock(&teedev->mutex); + list_add_tail(&shm->link, &ctx->list_shm); + mutex_unlock(&teedev->mutex); + } return shm; err_rem: @@ -207,8 +220,138 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) tee_device_put(teedev); return ret; } +/** + * tee_shm_alloc() - Allocate shared memory + * @ctx: Context that allocates the shared memory + * @size: Requested size of shared memory + * @flags: Flags setting properties for the requested shared memory. + * + * Memory allocated as global shared memory is automatically freed when the + * TEE file pointer is closed. The @flags field uses the bits defined by + * TEE_SHM_* in . TEE_SHM_MAPPED must currently always be + * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and + * associated with a dma-buf handle, else driver private memory. + */ +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) +{ + return __tee_shm_alloc(ctx, ctx->teedev, size, flags); +} EXPORT_SYMBOL_GPL(tee_shm_alloc); +struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size) +{ + return __tee_shm_alloc(NULL, teedev, size, TEE_SHM_MAPPED); +} +EXPORT_SYMBOL_GPL(tee_shm_priv_alloc); + +struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, + size_t length, u32 flags) +{ + struct tee_device *teedev = ctx->teedev; + const u32 req_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED; + struct tee_shm *shm; + void *ret; + int rc; + int num_pages; + unsigned long start; + + if (flags != req_flags) { + dev_err(teedev->dev.parent, "invliad shm flags %#x", flags); + return ERR_PTR(-EINVAL); + } + + if (!tee_device_get(teedev)) + return ERR_PTR(-EINVAL); + + if (!teedev->desc->ops->shm_register || + !teedev->desc->ops->shm_unregister) { + dev_err(teedev->dev.parent, + "register shared memory unspported by device"); + tee_device_put(teedev); + return ERR_PTR(-EINVAL); + } + + shm = kzalloc(sizeof(*shm), GFP_KERNEL); + if (!shm) { + ret = ERR_PTR(-ENOMEM); + goto err; + } + + shm->flags = flags | TEE_SHM_REGISTER; + shm->teedev = teedev; + shm->ctx = ctx; + shm->id = -1; + start = rounddown(addr, PAGE_SIZE); + shm->offset = addr - start; + shm->size = length; + num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE; + shm->pages = kcalloc(num_pages, sizeof(struct page), GFP_KERNEL); + if (!shm->pages) { + ret = ERR_PTR(-ENOMEM); + goto err; + } + + rc = get_user_pages_fast(start, num_pages, 1, shm->pages); + if (rc > 0) + shm->num_pages = rc; + if (rc != num_pages) { + if (rc > 0) + rc = -ENOMEM; + ret = ERR_PTR(rc); + goto err; + } + + mutex_lock(&teedev->mutex); + shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); + mutex_unlock(&teedev->mutex); + + rc = teedev->desc->ops->shm_register(ctx, shm, shm->pages, + shm->num_pages); + if (rc) { + ret = ERR_PTR(rc); + goto err; + } + + if (flags & TEE_SHM_DMA_BUF) { + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + exp_info.ops = &tee_shm_dma_buf_ops; + exp_info.size = shm->size; + exp_info.flags = O_RDWR; + exp_info.priv = shm; + + shm->dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(shm->dmabuf)) { + ret = ERR_CAST(shm->dmabuf); + teedev->desc->ops->shm_unregister(ctx, shm); + goto err; + } + } + + mutex_lock(&teedev->mutex); + list_add_tail(&shm->link, &ctx->list_shm); + mutex_unlock(&teedev->mutex); + + return shm; +err: + if (shm) { + size_t n; + + if (shm->id >= 0) { + mutex_lock(&teedev->mutex); + idr_remove(&teedev->idr, shm->id); + mutex_unlock(&teedev->mutex); + } + for (n = 0; n < shm->num_pages; n++) + put_page(shm->pages[n]); + kfree(shm->pages); + } + kfree(shm); + tee_device_put(teedev); + return ret; +} +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; @@ -453,6 +596,12 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) } EXPORT_SYMBOL_GPL(tee_shm_get_from_id); +bool tee_shm_is_registered(struct tee_shm *shm) +{ + return shm && (shm->flags & TEE_SHM_REGISTER); +} +EXPORT_SYMBOL_GPL(tee_shm_is_registered); + /** * tee_shm_get_id() - Get id of a shared memory object * @shm: Shared memory handle diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index ce3c239ad34f57..5caaf058505c04 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -28,6 +28,9 @@ #define TEE_SHM_MAPPED BIT(0) /* Memory mapped by the kernel */ #define TEE_SHM_DMA_BUF BIT(1) /* Memory with dma-buf handle */ #define TEE_SHM_EXT_DMA_BUF BIT(2) /* Memory with dma-buf handle */ +#define TEE_SHM_REGISTER BIT(3) /* Memory registered in secure world */ +#define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */ +#define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */ struct device; struct tee_device; @@ -77,6 +80,8 @@ struct tee_param { * @cancel_req: request cancel of an ongoing invoke or open * @supp_revc: called for supplicant to get a command * @supp_send: called for supplicant to send a response + * @shm_register: register shared memory buffer in TEE + * @shm_unregister: unregister shared memory buffer in TEE */ struct tee_driver_ops { void (*get_version)(struct tee_device *teedev, @@ -95,6 +100,9 @@ struct tee_driver_ops { struct tee_param *param); int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_param *param); + int (*shm_register)(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages); + int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm); }; /** @@ -302,6 +310,30 @@ void *tee_get_drvdata(struct tee_device *teedev); */ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags); +/** + * tee_shm_priv_alloc() - Allocate shared memory privately + * @dev: Device that allocates the shared memory + * @size: Requested size of shared memory + * + * Allocates shared memory buffer that is not associated with any client + * context. Such buffers are owned by TEE driver and used for internal calls. + * + * @returns a pointer to 'struct tee_shm' + */ +struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size); + +/** + * tee_shm_register() - Register shared memory buffer + * @ctx: Context that registers the shared memory + * @addr: Address is userspace of the shared buffer + * @length: Length of the shared buffer + * @flags: Flags setting properties for the requested shared memory. + * + * @returns a pointer to 'struct tee_shm' + */ +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 * @@ -389,6 +421,13 @@ static inline bool tee_param_is_memref(struct tee_param *param) } } +/** + * tee_shm_is_registered() - Check if shared memory object in registered in TEE + * @shm: Shared memory handle + * @returns true if object is registered in TEE + */ +bool tee_shm_is_registered(struct tee_shm *shm); + /** * tee_client_open_context() - Open a TEE context * @start: if not NULL, continue search after this context diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 31f71276f41376..d3e93689f8d555 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -50,6 +50,7 @@ #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 */ /* * TEE Implementation ID @@ -145,6 +146,36 @@ struct tee_ioctl_shm_register_fd_data { #define TEE_IOC_SHM_REGISTER_FD _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 8, \ struct tee_ioctl_shm_register_fd_data) +/** + * struct tee_ioctl_shm_register_data - Shared memory register argument + * @addr: [in] Start address of shared memory to register + * @length: [in/out] Length of shared memory to register + * @flags: [in/out] Flags to/from registration. + * @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_REGISTER below. + */ +struct tee_ioctl_shm_register_data { + __u64 addr; + __u64 length; + __u32 flags; + __s32 id; +}; + +/** + * TEE_IOC_SHM_REGISTER - Register shared memory argument + * + * Registers shared memory between the user space process and secure OS. + * + * Returns a file descriptor on success or < 0 on failure + * + * The shared memory is unregisterred when the descriptor is closed. + */ +#define TEE_IOC_SHM_REGISTER _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 9, \ + struct tee_ioctl_shm_register_data) + /** * struct tee_ioctl_buf_data - Variable sized buffer * @buf_ptr: [in] A __user pointer to a buffer From 3a0188b91dd5693309920777436aa9d05b429fba Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Tue, 29 Nov 2016 20:49:41 +0200 Subject: [PATCH 30/45] [NEW SHM]tee: shm: add accessors for buffer size and page offset These two function will be needed for shared memory registration in OP-TEE Signed-off-by: Volodymyr Babchuk --- include/linux/tee_drv.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 5caaf058505c04..1c13a643cd7f49 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -393,6 +393,26 @@ void *tee_shm_get_va(struct tee_shm *shm, size_t offs); */ int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa); +/** + * tee_shm_get_size() - Get size of shared memory buffer + * @shm: Shared memory handle + * @returns size of shared memory + */ +static inline size_t tee_shm_get_size(struct tee_shm *shm) +{ + return shm->size; +} + +/** + * tee_shm_get_page_offset() - Get shared buffer offset from page start + * @shm: Shared memory handle + * @returns page offset of shared buffer + */ +static inline size_t tee_shm_get_page_offset(struct tee_shm *shm) +{ + return shm->offset; +} + /** * tee_shm_get_id() - Get id of a shared memory object * @shm: Shared memory handle From 18156eb5e8c185ab931e2ea19164e70355e6ff81 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Wed, 5 Apr 2017 15:27:20 +0300 Subject: [PATCH 31/45] [NEW SHM]tee: shm: add page accessor functions In order to register a shared buffer in TEE, we need accessor function that return list of pages for that buffer. Signed-off-by: Volodymyr Babchuk --- include/linux/tee_drv.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 1c13a643cd7f49..b3ba3677f70b46 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -403,6 +403,20 @@ static inline size_t tee_shm_get_size(struct tee_shm *shm) return shm->size; } +/** + * tee_shm_get_pages() - Get list of pages that hold shared buffer + * @shm: Shared memory handle + * @num_pages: Number of pages will be stored there + * @returns pointer to pages array + */ +static inline struct page **tee_shm_get_pages(struct tee_shm *shm, + size_t *num_pages) +{ + if (num_pages) + *num_pages = shm->num_pages; + return shm->pages; +} + /** * tee_shm_get_page_offset() - Get shared buffer offset from page start * @shm: Shared memory handle From 68f26e31942127bd688ee4ff7142b07c049fcd91 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Thu, 20 Jul 2017 18:23:26 +0300 Subject: [PATCH 32/45] [NEW SHM]tee: optee: Update protocol definitions There were changes in REE<->OP-TEE ABI recently. Now ABI allows us to pass non-contiguous memory buffers as list of pages to OP-TEE. This can be achieved by using new parameter attribute OPTEE_MSG_ATTR_NONCONTIG. OP-TEE also is able to use all non-secure RAM for shared buffers. This new capability is enabled with OPTEE_SMC_SEC_CAP_DYNAMIC_SHM flag. This patch adds necessary definitions to the protocol definition files at Linux side. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/optee_msg.h | 38 +++++++++++++++++++++++++++++------ drivers/tee/optee/optee_smc.h | 7 +++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 67444122e32143..8f9669a557c20a 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -67,11 +67,32 @@ #define OPTEE_MSG_ATTR_META BIT(8) /* - * The temporary shared memory object is not physically contigous and this - * temp memref is followed by another fragment until the last temp memref - * that doesn't have this bit set. + * Pointer to a list of pages used to register user-defined SHM buffer. + * Used with OPTEE_MSG_ATTR_TYPE_TMEM_*. + * buf_ptr should point to the beginning of the buffer. Buffer will contain + * list of page addresses. OP-TEE core can reconstruct contiguous buffer from + * that page addresses list. Page addresses are stored as 64 bit values. + * Last entry on a page should point to the next page of buffer. + * Every entry in buffer should point to a 4k page beginning (12 least + * significant bits must be equal to zero). + * + * 12 least significant bints of optee_msg_param.u.tmem.buf_ptr should hold page + * offset of the user buffer. + * + * So, entries should be placed like members of this structure: + * + * struct page_data { + * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1]; + * uint64_t next_page_data; + * }; + * + * Structure is designed to exactly fit into the page size + * OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page. + * + * The size of 4KB is chosen because this is the smallest page size for ARM + * architectures. If REE uses larger pages, it should divide them to 4KB ones. */ -#define OPTEE_MSG_ATTR_FRAGMENT BIT(9) +#define OPTEE_MSG_ATTR_NONCONTIG BIT(9) /* * Memory attributes for caching passed with temp memrefs. The actual value @@ -94,6 +115,11 @@ #define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005 #define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006 +/* + * Page size used in non-contiguous buffer entries + */ +#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096 + /** * struct optee_msg_param_tmem - temporary memory reference parameter * @buf_ptr: Address of the buffer @@ -145,8 +171,8 @@ struct optee_msg_param_value { * * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value, - * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates tmem and - * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates rmem. + * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and + * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem, * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used. */ struct optee_msg_param { diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 069c8e1429de8d..7cd327243ada91 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -222,6 +222,13 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0) /* Secure world can communicate via previously unregistered shared memory */ #define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1) + +/* + * Secure world supports commands "register/unregister shared memory", + * secure world accepts command buffers located in any parts of non-secure RAM + */ +#define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2) + #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) From 0e118026603d7ad5c7daf44b76b279747adc95da Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Thu, 20 Jul 2017 21:03:16 +0300 Subject: [PATCH 33/45] [NEW SHM]tee: optee: add page list manipulation functions These functions will be used to pass information about shared buffers to OP-TEE. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/call.c | 48 +++++++++++++++++++++++++++++++ drivers/tee/optee/optee_private.h | 4 +++ 2 files changed, 52 insertions(+) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 4ccaa729333866..4a278f7e136153 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. * */ +#include #include #include #include @@ -447,3 +448,50 @@ void optee_disable_shm_cache(struct optee *optee) } optee_cq_wait_final(&optee->call_queue, &w); } + +/** + * optee_fill_pages_list() - write list of user pages to given shared + * buffer. + * + * @dst: page-aligned buffer where list of pages will be stored + * @pages: array of pages that represents shared buffer + * @num_pages: number of entries in @pages + * + * @dst should be big enough to hold list of user page addresses and + * links to the next pages of buffer + */ +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages) +{ + size_t i; + + /* TODO: add support for RichOS page sizes that != 4096 */ + BUILD_BUG_ON(PAGE_SIZE != OPTEE_MSG_NONCONTIG_PAGE_SIZE); + for (i = 0; i < num_pages; i++, dst++) { + /* Check if we are going to roll over the page boundary */ + if (IS_ALIGNED((uintptr_t)(dst + 1), + OPTEE_MSG_NONCONTIG_PAGE_SIZE)) { + *dst = virt_to_phys(dst + 1); + dst++; + } + *dst = page_to_phys(pages[i]); + } +} + +static size_t get_pages_array_size(size_t num_entries) +{ + /* Number of user pages + number of pages to hold list of user pages */ + return sizeof(u64) * + (num_entries + (sizeof(u64) * num_entries) / + OPTEE_MSG_NONCONTIG_PAGE_SIZE); +} + +u64 *optee_allocate_pages_array(size_t num_entries) +{ + return alloc_pages_exact(get_pages_array_size(num_entries), GFP_KERNEL); +} + +void optee_free_pages_array(void *array, size_t num_entries) +{ + free_pages_exact(array, get_pages_array_size(num_entries)); +} + diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 3e7da187acbe59..aead68f7d713cc 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -154,6 +154,10 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, const struct tee_param *params); +u64 *optee_allocate_pages_array(size_t num_entries); +void optee_free_pages_array(void *array, size_t num_entries); +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages); + /* * Small helpers */ From 91fa8f2e153b68846d62fac1f2a5d5f5285ba2c1 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 21 Jul 2017 17:47:14 +0300 Subject: [PATCH 34/45] [NEW SHM]tee: optee: add shared buffer registration functions This change adds ops for shm_(un)register functions in tee interface. Client application can use these functions to (un)register an own shared buffer in OP-TEE address space. This allows zero copy data sharing between Normal and Secure Worlds. Please note that while those functions were added to optee code, it does not report to userspace that those functions are available. OP-TEE code does not set TEE_GEN_CAP_REG_MEM flag. This flag will be enabled only after all other features of dynamic shared memory will be implemented in subsequent patches. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/call.c | 64 +++++++++++++++++++++++++++++++ drivers/tee/optee/core.c | 2 + drivers/tee/optee/optee_private.h | 4 ++ 3 files changed, 70 insertions(+) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 4a278f7e136153..894aec52142284 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -495,3 +495,67 @@ void optee_free_pages_array(void *array, size_t num_entries) free_pages_exact(array, get_pages_array_size(num_entries)); } +int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages) +{ + struct tee_shm *shm_arg = NULL; + struct optee_msg_arg *msg_arg; + u64 *pages_array; + phys_addr_t msg_parg; + int rc = 0; + + if (!num_pages) + return -EINVAL; + + pages_array = optee_allocate_pages_array(num_pages); + if (!pages_array) + return -ENOMEM; + + shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); + if (IS_ERR(shm_arg)) { + rc = PTR_ERR(shm_arg); + goto out; + } + + optee_fill_pages_list(pages_array, pages, num_pages); + + msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; + msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | + OPTEE_MSG_ATTR_NONCONTIG; + msg_arg->params->u.tmem.shm_ref = (unsigned long)shm; + msg_arg->params->u.tmem.size = tee_shm_get_size(shm); + msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_array) | + tee_shm_get_page_offset(shm); + + if (optee_do_call_with_arg(ctx, msg_parg) || + msg_arg->ret != TEEC_SUCCESS) + rc = -EINVAL; + + tee_shm_free(shm_arg); +out: + optee_free_pages_array(pages_array, num_pages); + return rc; +} + +int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) +{ + struct tee_shm *shm_arg; + struct optee_msg_arg *msg_arg; + phys_addr_t msg_parg; + int rc = 0; + + shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); + if (IS_ERR(shm_arg)) + return PTR_ERR(shm_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; + + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; + + if (optee_do_call_with_arg(ctx, msg_parg) || + msg_arg->ret != TEEC_SUCCESS) + rc = -EINVAL; + tee_shm_free(shm_arg); + return rc; +} diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index ff3940ceb5d2ff..eaa4cadf0045b1 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -265,6 +265,8 @@ static const struct tee_driver_ops optee_ops = { .close_session = optee_close_session, .invoke_func = optee_invoke_func, .cancel_req = optee_cancel_req, + .shm_register = optee_shm_register, + .shm_unregister = optee_shm_unregister, }; static const struct tee_desc optee_desc = { diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index aead68f7d713cc..46e430d51671bb 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -149,6 +149,10 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); void optee_enable_shm_cache(struct optee *optee); void optee_disable_shm_cache(struct optee *optee); +int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages); +int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm); + 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, From 8d3d56ee9011037d0b7b91389727567ed81743ee Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 21 Jul 2017 18:08:53 +0300 Subject: [PATCH 35/45] [NEW SHM]tee: optee: add registered shared parameters handling Now, when client applications can register own shared buffers in OP-TEE, we need to extend ABI for parameter passing to/from OP-TEE. So, if OP-TEE core detects that parameter belongs to registered shared memory, it will use corresponding parameter attribute. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/core.c | 86 +++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index eaa4cadf0045b1..2c13a2463be557 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -98,6 +98,33 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, return rc; } break; + case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + p->u.memref.size = mp->u.rmem.size; + shm = (struct tee_shm *)(unsigned long) + mp->u.rmem.shm_ref; + + if (!shm) { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + break; + } + p->u.memref.shm_offs = mp->u.rmem.offs; + p->u.memref.shm = shm; + + /* Check that the memref is covered by the shm object */ + if (p->u.memref.size) { + size_t o = p->u.memref.shm_offs + + p->u.memref.size; + + if (o > tee_shm_get_size(shm)) + return -EINVAL; + } + break; + default: return -EINVAL; } @@ -105,6 +132,46 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, return 0; } +static int to_msg_param_tmp_mem(struct optee_msg_param *mp, + const struct tee_param *p) +{ + int rc; + phys_addr_t pa; + + mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + + mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; + mp->u.tmem.size = p->u.memref.size; + + if (!p->u.memref.shm) { + mp->u.tmem.buf_ptr = 0; + return 0; + } + + rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa); + if (rc) + return rc; + + mp->u.tmem.buf_ptr = pa; + mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << + OPTEE_MSG_ATTR_CACHE_SHIFT; + + return 0; +} + +static int to_msg_param_reg_mem(struct optee_msg_param *mp, + const struct tee_param *p) +{ + mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + + mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm; + mp->u.rmem.size = p->u.memref.size; + mp->u.rmem.offs = p->u.memref.shm_offs; + return 0; +} + /** * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters * @msg_params: OPTEE_MSG parameters @@ -117,7 +184,6 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, { int rc; size_t n; - phys_addr_t pa; for (n = 0; n < num_params; n++) { const struct tee_param *p = params + n; @@ -140,22 +206,12 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_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: - mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + - p->attr - - TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; - mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; - mp->u.tmem.size = p->u.memref.size; - if (!p->u.memref.shm) { - mp->u.tmem.buf_ptr = 0; - break; - } - rc = tee_shm_get_pa(p->u.memref.shm, - p->u.memref.shm_offs, &pa); + if (tee_shm_is_registered(p->u.memref.shm)) + rc = to_msg_param_reg_mem(mp, p); + else + rc = to_msg_param_tmp_mem(mp, p); if (rc) return rc; - mp->u.tmem.buf_ptr = pa; - mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << - OPTEE_MSG_ATTR_CACHE_SHIFT; break; default: return -EINVAL; From 76aa3121050fef4d3654ea1a76c03be0fec68b10 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 21 Jul 2017 18:21:58 +0300 Subject: [PATCH 36/45] [NEW SHM]tee: optee: add registered buffers handling into RPC calls With latest changes to OP-TEE we can use any buffers as a shared memory. Thus, it is possible for supplicant to provide part of own memory when OP-TEE asks to allocate a shared buffer. This patch adds support for such feature into RPC handling code. Now when OP-TEE asks supplicant to allocate shared buffer, supplicant can use TEE_IOC_SHM_REGISTER to provide such buffer. RPC handler is aware of this, so it will pass list of allocated pages to OP-TEE. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/call.c | 19 +++++++- drivers/tee/optee/core.c | 2 + drivers/tee/optee/optee_private.h | 15 ++++++- drivers/tee/optee/rpc.c | 72 +++++++++++++++++++++++++++---- 4 files changed, 97 insertions(+), 11 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 894aec52142284..d2126de8d234f8 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -137,6 +137,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_call_waiter w; struct optee_rpc_param param = { }; + struct optee_call_ctx call_ctx = { }; u32 ret; param.a0 = OPTEE_SMC_CALL_WITH_ARG; @@ -165,13 +166,14 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) param.a1 = res.a1; param.a2 = res.a2; param.a3 = res.a3; - optee_handle_rpc(ctx, ¶m); + optee_handle_rpc(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. @@ -559,3 +561,18 @@ int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) tee_shm_free(shm_arg); return rc; } + +int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages) +{ + /* + * We don't want to register supplicant memory in OP-TEE. + * Instead information about it will be passed in RPC code. + */ + return 0; +} + +int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm) +{ + return 0; +} diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 2c13a2463be557..80307f51dad20d 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -337,6 +337,8 @@ static const struct tee_driver_ops optee_supp_ops = { .release = optee_release, .supp_recv = optee_supp_recv, .supp_send = optee_supp_send, + .shm_register = optee_shm_register_supp, + .shm_unregister = optee_shm_unregister_supp, }; static const struct tee_desc optee_supp_desc = { diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 46e430d51671bb..0cbda7b041008d 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -118,7 +118,16 @@ struct optee_rpc_param { u32 a7; }; -void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param); +/* Holds context that is preserved during one STD call */ +struct optee_call_ctx { + /* information about page array used in last allocation */ + void *pages_array; + size_t num_entries; +}; + +void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, + struct optee_call_ctx *call_ctx); +void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx); void optee_wait_queue_init(struct optee_wait_queue *wq); void optee_wait_queue_exit(struct optee_wait_queue *wq); @@ -153,6 +162,10 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, struct page **pages, size_t num_pages); int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm); +int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages); +int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm); + 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, diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 4a4f66a89e2963..b835a87e7c4517 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -202,7 +202,8 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) } static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, - struct optee_msg_arg *arg) + struct optee_msg_arg *arg, + struct optee_call_ctx *call_ctx) { phys_addr_t pa; struct tee_shm *shm; @@ -247,10 +248,44 @@ static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, goto bad; } - 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; + sz = tee_shm_get_size(shm); + + if (tee_shm_is_registered(shm)) { + struct page **pages; + u64 *pages_array; + 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_array = optee_allocate_pages_array(page_num); + if (!pages_array) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + goto bad; + } + + call_ctx->pages_array = pages_array; + call_ctx->num_entries = page_num; + + arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | + OPTEE_MSG_ATTR_NONCONTIG; + arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_array) + + tee_shm_get_page_offset(shm); + + 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_array, pages, page_num); + } 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; + } + arg->ret = TEEC_SUCCESS; return; bad: @@ -353,8 +388,24 @@ static void handle_rpc_func_cmd_bm_reg(struct optee_msg_arg *arg) arg->ret = TEEC_ERROR_BAD_PARAMETERS; } +static void free_page_array(struct optee_call_ctx *call_ctx) +{ + if (call_ctx->pages_array) { + optee_free_pages_array(call_ctx->pages_array, + call_ctx->num_entries); + call_ctx->pages_array = NULL; + call_ctx->num_entries = 0; + } +} + +void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) +{ + free_page_array(call_ctx); +} + static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, - struct tee_shm *shm) + struct tee_shm *shm, + struct optee_call_ctx *call_ctx) { struct optee_msg_arg *arg; @@ -375,7 +426,8 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, handle_rpc_func_cmd_wait(arg); break; case OPTEE_MSG_RPC_CMD_SHM_ALLOC: - handle_rpc_func_cmd_shm_alloc(ctx, arg); + free_page_array(call_ctx); + handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx); break; case OPTEE_MSG_RPC_CMD_SHM_FREE: handle_rpc_func_cmd_shm_free(ctx, arg); @@ -392,10 +444,12 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, * optee_handle_rpc() - handle RPC from secure world * @ctx: context doing the RPC * @param: value of registers for the RPC + * @call_ctx: call context. Preserved during one OP-TEE invocation * * Result of RPC is written back into @param. */ -void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param) +void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, + struct optee_call_ctx *call_ctx) { struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); @@ -430,7 +484,7 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param) break; case OPTEE_SMC_RPC_FUNC_CMD: shm = reg_pair_to_ptr(param->a1, param->a2); - handle_rpc_func_cmd(ctx, optee, shm); + handle_rpc_func_cmd(ctx, optee, shm, call_ctx); break; default: pr_warn("Unknown RPC func 0x%x\n", From a7ea6e4c8a7b60aecffe3944896fade4100873da Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 21 Jul 2017 19:48:38 +0300 Subject: [PATCH 37/45] [NEW SHM]tee: optee: store OP-TEE capabilities in private data Those capabilities will be used in subsequent patches. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/core.c | 1 + drivers/tee/optee/optee_private.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 80307f51dad20d..879e718fcaa25b 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -551,6 +551,7 @@ static struct optee *optee_probe(struct device_node *np) } optee->invoke_fn = invoke_fn; + optee->sec_caps = sec_caps; teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); if (IS_ERR(teedev)) { diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 0cbda7b041008d..266ee5e121c180 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -84,6 +84,8 @@ struct optee_supp { * @supp: supplicant synchronization struct for RPC to supplicant * @pool: shared memory pool * @memremaped_shm virtual address of memory in shared memory pool + * @sec_caps: secure world capabilities defined by + * OPTEE_SMC_SEC_CAP_* in optee_smc.h */ struct optee { struct tee_device *supp_teedev; @@ -94,6 +96,7 @@ struct optee { struct optee_supp supp; struct tee_shm_pool *pool; void *memremaped_shm; + u32 sec_caps; }; struct optee_session { From df883686f8b16b65badc8e29fbfe592d81802405 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 21 Jul 2017 19:51:08 +0300 Subject: [PATCH 38/45] [NEW SHM]tee: optee: add optee-specific shared pool implementation This is simple pool that uses kernel page allocator. This pool can be used in case OP-TEE supports dynamic shared memory. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/Makefile | 1 + drivers/tee/optee/shm_pool.c | 75 ++++++++++++++++++++++++++++++++++++ drivers/tee/optee/shm_pool.h | 23 +++++++++++ 3 files changed, 99 insertions(+) create mode 100644 drivers/tee/optee/shm_pool.c create mode 100644 drivers/tee/optee/shm_pool.h diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 47b6a67dbe6783..7e437ea0aa9ded 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -4,3 +4,4 @@ optee-objs += call.o optee-objs += rpc.o optee-objs += supp.o optee-objs += bench.o +optee-objs += shm_pool.o diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c new file mode 100644 index 00000000000000..49397813fff1e5 --- /dev/null +++ b/drivers/tee/optee/shm_pool.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2017, EPAM Systems + * + * 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 "optee_private.h" +#include "optee_smc.h" +#include "shm_pool.h" + +static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + unsigned int order = get_order(size); + struct page *page; + + page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); + if (!page) + return -ENOMEM; + + shm->kaddr = page_address(page); + shm->paddr = page_to_phys(page); + shm->size = PAGE_SIZE << order; + + return 0; +} + +static void pool_op_free(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm) +{ + free_pages((unsigned long)shm->kaddr, get_order(shm->size)); + shm->kaddr = NULL; +} + +static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm) +{ + kfree(poolm); +} + +static const struct tee_shm_pool_mgr_ops pool_ops = { + .alloc = pool_op_alloc, + .free = pool_op_free, + .destroy_poolmgr = pool_op_destroy_poolmgr, +}; + +/** + * optee_shm_pool_alloc_pages() - create page-based allocator pool + * + * This pool is used when OP-TEE supports dymanic SHM. In this case + * command buffers and such are allocated from kernel's own memory. + */ +struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void) +{ + struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + if (!mgr) + return ERR_PTR(-ENOMEM); + + mgr->ops = &pool_ops; + + return mgr; +} diff --git a/drivers/tee/optee/shm_pool.h b/drivers/tee/optee/shm_pool.h new file mode 100644 index 00000000000000..4e753c3bf7ec29 --- /dev/null +++ b/drivers/tee/optee/shm_pool.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2016, EPAM Systems + * + * 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 SHM_POOL_H +#define SHM_POOL_H + +#include + +struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void); + +#endif From 3ca9562008f8ffcf3451dd56bb0cd3e723fabd65 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 21 Jul 2017 19:56:39 +0300 Subject: [PATCH 39/45] [NEW SHM]tee: optee: enable dynamic SHM support Previous patches added various features that are needed for dynamic SHM. Dynamic SHM allows Normal World to share any buffers with OP-TEE. While original design suggested to use pre-allocated region (usually of 1M to 2M of size), this new approach allows to use all non-secure RAM for command buffers, RPC allocations and TA parameters. This patch checks capability OPTEE_SMC_SEC_CAP_DYNAMIC_SHM. If it was set by OP-TEE, then kernel part of OP-TEE will use kernel page allocator to allocate command buffers. Also it will set TEE_GEN_CAP_REG_MEM capability to tell userspace that it supports shared memory registration. Signed-off-by: Volodymyr Babchuk --- drivers/tee/optee/core.c | 69 +++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 879e718fcaa25b..a2c81425b886b7 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -29,6 +29,7 @@ #include "optee_bench.h" #include "optee_private.h" #include "optee_smc.h" +#include "shm_pool.h" #define DRIVER_NAME "optee" @@ -228,6 +229,10 @@ static void optee_get_version(struct tee_device *teedev, .impl_caps = TEE_OPTEE_CAP_TZ, .gen_caps = TEE_GEN_CAP_GP, }; + struct optee *optee = tee_get_drvdata(teedev); + + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + v.gen_caps |= TEE_GEN_CAP_REG_MEM; *vers = v; } @@ -403,21 +408,22 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, } static struct tee_shm_pool * -optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) +optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm, + u32 sec_caps) { union { struct arm_smccc_res smccc; struct optee_smc_get_shm_config_result result; } res; - struct tee_shm_pool *pool; unsigned long vaddr; phys_addr_t paddr; size_t size; phys_addr_t begin; phys_addr_t end; void *va; - struct tee_shm_pool_mem_info priv_info; - struct tee_shm_pool_mem_info dmabuf_info; + struct tee_shm_pool_mgr *priv_mgr; + struct tee_shm_pool_mgr *dmabuf_mgr; + void *rc; invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); if (res.result.status != OPTEE_SMC_RETURN_OK) { @@ -447,22 +453,49 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) } vaddr = (unsigned long)va; - priv_info.vaddr = vaddr; - priv_info.paddr = paddr; - priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; - dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; - dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; - dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; - - pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info); - if (IS_ERR(pool)) { - memunmap(va); - goto out; + /* + * If OP-TEE can work with unregistered SHM, we will use own pool + * for private shm + */ + if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) { + rc = optee_shm_pool_alloc_pages(); + if (IS_ERR(rc)) + goto err_memunmap; + priv_mgr = rc; + } else { + const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; + + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, + 3 /* 8 bytes aligned */); + if (IS_ERR(rc)) + goto err_memunmap; + priv_mgr = rc; + + vaddr += sz; + paddr += sz; + size -= sz; } + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); + if (IS_ERR(rc)) + goto err_free_priv_mgr; + dmabuf_mgr = rc; + + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); + if (IS_ERR(rc)) + goto err_free_dmabuf_mgr; + *memremaped_shm = va; -out: - return pool; + + return rc; + +err_free_dmabuf_mgr: + tee_shm_pool_mgr_destroy(dmabuf_mgr); +err_free_priv_mgr: + tee_shm_pool_mgr_destroy(priv_mgr); +err_memunmap: + memunmap(va); + return rc; } /* Simple wrapper functions to be able to use a function pointer */ @@ -540,7 +573,7 @@ static struct optee *optee_probe(struct device_node *np) if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) return ERR_PTR(-EINVAL); - pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); + pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm, sec_caps); if (IS_ERR(pool)) return (void *)pool; From 8862a5dcc411fef7cc9812ffbe3f3f42e95acd88 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Mon, 24 Jul 2017 18:45:30 +0300 Subject: [PATCH 40/45] [NEW SHM]tee: use reference counting for tee_context We need to ensure that tee_context is present until last shared buffer will be freed. Signed-off-by: Volodymyr Babchuk --- drivers/tee/tee_core.c | 33 ++++++++++++++++++++++++++------- drivers/tee/tee_private.h | 3 +++ drivers/tee/tee_shm.c | 10 ++++++++++ include/linux/tee_drv.h | 7 +++++++ 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 45acb27e5af2bc..02139b73516312 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -52,6 +52,7 @@ static struct tee_context *teedev_open(struct tee_device *teedev) goto err; } + kref_init(&ctx->refcount); ctx->teedev = teedev; INIT_LIST_HEAD(&ctx->list_shm); rc = teedev->desc->ops->open(ctx); @@ -66,19 +67,37 @@ static struct tee_context *teedev_open(struct tee_device *teedev) } -static void teedev_close_context(struct tee_context *ctx) +void teedev_ctx_get(struct tee_context *ctx) { - struct tee_shm *shm; + if (ctx->releasing) + return; + + kref_get(&ctx->refcount); +} +static void teedev_ctx_release(struct kref *ref) +{ + struct tee_context *ctx = container_of(ref, struct tee_context, + refcount); + ctx->releasing = true; ctx->teedev->desc->ops->release(ctx); - mutex_lock(&ctx->teedev->mutex); - list_for_each_entry(shm, &ctx->list_shm, link) - shm->ctx = NULL; - mutex_unlock(&ctx->teedev->mutex); - tee_device_put(ctx->teedev); kfree(ctx); } +void teedev_ctx_put(struct tee_context *ctx) +{ + if (ctx->releasing) + return; + + kref_put(&ctx->refcount, teedev_ctx_release); +} + +static void teedev_close_context(struct tee_context *ctx) +{ + tee_device_put(ctx->teedev); + teedev_ctx_put(ctx); +} + static int tee_open(struct inode *inode, struct file *filp) { struct tee_context *ctx; diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h index 2bc2b5ab1661ac..85d99d621603d7 100644 --- a/drivers/tee/tee_private.h +++ b/drivers/tee/tee_private.h @@ -73,4 +73,7 @@ int tee_shm_get_fd(struct tee_shm *shm); bool tee_device_get(struct tee_device *teedev); void tee_device_put(struct tee_device *teedev); +void teedev_ctx_get(struct tee_context *ctx); +void teedev_ctx_put(struct tee_context *ctx); + #endif /*TEE_PRIVATE_H*/ diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 623e792664a44a..7efc57e97194f0 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -69,6 +69,9 @@ static void tee_shm_release(struct tee_shm *shm) kfree(shm->pages); } + if (shm->ctx) + teedev_ctx_put(shm->ctx); + kfree(shm); tee_device_put(teedev); } @@ -202,6 +205,7 @@ struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, } if (ctx) { + teedev_ctx_get(ctx); mutex_lock(&teedev->mutex); list_add_tail(&shm->link, &ctx->list_shm); mutex_unlock(&teedev->mutex); @@ -271,6 +275,8 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, return ERR_PTR(-EINVAL); } + teedev_ctx_get(ctx); + shm = kzalloc(sizeof(*shm), GFP_KERNEL); if (!shm) { ret = ERR_PTR(-ENOMEM); @@ -347,6 +353,7 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, kfree(shm->pages); } kfree(shm); + teedev_ctx_put(ctx); tee_device_put(teedev); return ret; } @@ -361,6 +368,8 @@ struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd) 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); @@ -441,6 +450,7 @@ struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd) dma_buf_put(ref->dmabuf); } kfree(ref); + teedev_ctx_put(ctx); tee_device_put(ctx->teedev); return rc; } diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index b3ba3677f70b46..e8aeb51a1807c3 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -42,11 +43,17 @@ struct tee_shm_pool; * @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. */ struct tee_context { struct tee_device *teedev; struct list_head list_shm; void *data; + struct kref refcount; + bool releasing; }; struct tee_param_memref { From c7f82a04c8a486baa03808174ff90820af9607a5 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 25 Aug 2017 22:34:49 +0300 Subject: [PATCH 41/45] [NEW SHM]tee: shm: inline tee_shm getter functions Now, when struct tee_shm is defined in public header, we can inline small getter functions. Signed-off-by: Volodymyr Babchuk --- drivers/tee/tee_shm.c | 17 ----------------- include/linux/tee_drv.h | 10 ++++++++-- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 7efc57e97194f0..0040ad037e30a5 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -606,23 +606,6 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) } EXPORT_SYMBOL_GPL(tee_shm_get_from_id); -bool tee_shm_is_registered(struct tee_shm *shm) -{ - return shm && (shm->flags & TEE_SHM_REGISTER); -} -EXPORT_SYMBOL_GPL(tee_shm_is_registered); - -/** - * tee_shm_get_id() - Get id of a shared memory object - * @shm: Shared memory handle - * @returns id - */ -int tee_shm_get_id(struct tee_shm *shm) -{ - return shm->id; -} -EXPORT_SYMBOL_GPL(tee_shm_get_id); - /** * 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 e8aeb51a1807c3..a7be8ce84bde45 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -439,7 +439,10 @@ static inline size_t tee_shm_get_page_offset(struct tee_shm *shm) * @shm: Shared memory handle * @returns id */ -int tee_shm_get_id(struct tee_shm *shm); +static inline int tee_shm_get_id(struct tee_shm *shm) +{ + return shm->id; +} /** * tee_shm_get_from_id() - Find shared memory object and increase reference @@ -467,7 +470,10 @@ static inline bool tee_param_is_memref(struct tee_param *param) * @shm: Shared memory handle * @returns true if object is registered in TEE */ -bool tee_shm_is_registered(struct tee_shm *shm); +static inline bool tee_shm_is_registered(struct tee_shm *shm) +{ + return shm && (shm->flags & TEE_SHM_REGISTER); +} /** * tee_client_open_context() - Open a TEE context From 7e7a654d1813f858e4b5e74986752e432f4f4f87 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Mon, 9 Oct 2017 15:09:06 +0300 Subject: [PATCH 42/45] optee: remove registered shm argument size check We don't need to return error if output shm size is larger than allocated buffer. This is pefrectly fine. In this way TEE or TA reports that it needs larger buffer (as described in TEE Client API Specification v1.0, section 3.2.5.). Signed-off-by: Volodymyr Babchuk Tested-by: Jerome Forissier (HiKey) Reviewed-by: Joakim Bech --- drivers/tee/optee/core.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index a2c81425b886b7..9ddcfe47e212e6 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -116,14 +116,6 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, p->u.memref.shm_offs = mp->u.rmem.offs; p->u.memref.shm = shm; - /* Check that the memref is covered by the shm object */ - if (p->u.memref.size) { - size_t o = p->u.memref.shm_offs + - p->u.memref.size; - - if (o > tee_shm_get_size(shm)) - return -EINVAL; - } break; default: From 6e954e2f2cbd412f7bc874bb9145f69713194e52 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 9 Oct 2017 13:47:40 +0200 Subject: [PATCH 43/45] 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 --- 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 03d3a4fce0e298..051e0a283f095d 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -218,6 +218,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, struct device *dev, @@ -237,6 +238,9 @@ static int ion_dma_buf_attach(struct dma_buf *dmabuf, struct device *dev, return -ENOMEM; } + if (buffer->heap->type == ION_HEAP_TYPE_UNMAPPED) + a->no_map = true; + a->table = table; a->dev = dev; INIT_LIST_HEAD(&a->list); @@ -274,6 +278,9 @@ static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, table = a->table; + if (a->no_map) + return table; + if (!dma_map_sg(attachment->dev, table->sgl, table->nents, direction)){ ret = -ENOMEM; @@ -290,6 +297,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_sg(attachment->dev, table->sgl, table->nents, direction); } From eae92bd5ff8f8be4a4afdec6f6b9c16f1a4807f3 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Mon, 20 Nov 2017 13:52:13 +0100 Subject: [PATCH 44/45] tee: optee: GET_OS_REVISION: document a2 as a build identifier In the OPTEE_SMC_CALL_GET_OS_REVISION request, the previously reserved parameter a2 is now documented as being an optional build identifier (such as an SCM revision or commit ID, for instance). A new structure optee_smc_call_get_os_revision_result is introduced to be used when querying the secure OS version, instead of re-using the struct defined for OPTEE_SMC_CALLS_REVISION. Signed-off-by: Jerome Forissier --- drivers/tee/optee/optee_smc.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 7cd327243ada91..bbf0cf028c1620 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -112,12 +112,20 @@ struct optee_smc_calls_revision_result { * Trusted OS, not of the API. * * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION - * described above. + * described above. May optionally return a 32-bit build identifier in a2, + * with zero meaning unspecified. */ #define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEE_MSG_FUNCID_GET_OS_REVISION #define OPTEE_SMC_CALL_GET_OS_REVISION \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION) +struct optee_smc_call_get_os_revision_result { + unsigned long major; + unsigned long minor; + unsigned long build_id; + unsigned long reserved1; +}; + /* * Call with struct optee_msg_arg as argument * From 3a701ecf1b4424edae616a40c33534e3aeeb77b3 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Mon, 20 Nov 2017 13:55:51 +0100 Subject: [PATCH 45/45] tee: optee: report OP-TEE revision information When the driver initializes, report the following information about the OP-TEE OS: - major and minor version, - build identifier (if available). Signed-off-by: Jerome Forissier --- drivers/tee/optee/core.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 9ddcfe47e212e6..da89217483913e 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -357,6 +357,27 @@ static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) return false; } +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_call_get_os_revision_result result; + } res = { + .result = { + .build_id = 0 + } + }; + + invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, + &res.smccc); + + if (res.result.build_id) + pr_info("revision %lu.%lu (%08lx)", res.result.major, + res.result.minor, res.result.build_id); + else + pr_info("revision %lu.%lu", res.result.major, res.result.minor); +} + static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) { union { @@ -548,6 +569,8 @@ static struct optee *optee_probe(struct device_node *np) return ERR_PTR(-EINVAL); } + optee_msg_get_os_revision(invoke_fn); + if (!optee_msg_api_revision_is_compatible(invoke_fn)) { pr_warn("api revision mismatch\n"); return ERR_PTR(-EINVAL);