forked from mirrors/linux
		
	tee: generic TEE subsystem
Initial patch for generic TEE subsystem. This subsystem provides: * Registration/un-registration of TEE drivers. * Shared memory between normal world and secure world. * Ioctl interface for interaction with user space. * Sysfs implementation_id of TEE driver A TEE (Trusted Execution Environment) driver is a driver that interfaces with a trusted OS running in some secure environment, for example, TrustZone on ARM cpus, or a separate secure co-processor etc. The TEE subsystem can serve a TEE driver for a Global Platform compliant TEE, but it's not limited to only Global Platform TEEs. This patch builds on other similar implementations trying to solve the same problem: * "optee_linuxdriver" by among others Jean-michel DELORME<jean-michel.delorme@st.com> and Emmanuel MICHEL <emmanuel.michel@st.com> * "Generic TrustZone Driver" by Javier González <javier@javigon.com> Acked-by: Andreas Dannenberg <dannenberg@ti.com> Tested-by: Jerome Forissier <jerome.forissier@linaro.org> (HiKey) Tested-by: Volodymyr Babchuk <vlad.babchuk@gmail.com> (RCAR H3) Tested-by: Scott Branden <scott.branden@broadcom.com> Reviewed-by: Javier González <javier@javigon.com> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
This commit is contained in:
		
							parent
							
								
									c8bfafb159
								
							
						
					
					
						commit
						967c9cca2c
					
				
					 12 changed files with 2182 additions and 0 deletions
				
			
		|  | @ -308,6 +308,7 @@ Code  Seq#(hex)	Include File		Comments | |||
| 0xA3	80-8F	Port ACL		in development: | ||||
| 					<mailto:tlewis@mindspring.com> | ||||
| 0xA3	90-9F	linux/dtlk.h | ||||
| 0xA4	00-1F	uapi/linux/tee.h	Generic TEE subsystem | ||||
| 0xAA	00-3F	linux/uapi/linux/userfaultfd.h | ||||
| 0xAB	00-1F	linux/nbd.h | ||||
| 0xAC	00-1F	linux/raw.h | ||||
|  |  | |||
|  | @ -11086,6 +11086,13 @@ F:	drivers/hwtracing/stm/ | |||
| F:	include/linux/stm.h | ||||
| F:	include/uapi/linux/stm.h | ||||
| 
 | ||||
| TEE SUBSYSTEM | ||||
| M:	Jens Wiklander <jens.wiklander@linaro.org> | ||||
| S:	Maintained | ||||
| F:	include/linux/tee_drv.h | ||||
| F:	include/uapi/linux/tee.h | ||||
| F:	drivers/tee/ | ||||
| 
 | ||||
| THUNDERBOLT DRIVER | ||||
| M:	Andreas Noever <andreas.noever@gmail.com> | ||||
| S:	Maintained | ||||
|  |  | |||
|  | @ -204,4 +204,6 @@ source "drivers/fpga/Kconfig" | |||
| 
 | ||||
| source "drivers/fsi/Kconfig" | ||||
| 
 | ||||
| source "drivers/tee/Kconfig" | ||||
| 
 | ||||
| endmenu | ||||
|  |  | |||
|  | @ -177,3 +177,4 @@ obj-$(CONFIG_ANDROID)		+= android/ | |||
| obj-$(CONFIG_NVMEM)		+= nvmem/ | ||||
| obj-$(CONFIG_FPGA)		+= fpga/ | ||||
| obj-$(CONFIG_FSI)		+= fsi/ | ||||
| obj-$(CONFIG_TEE)		+= tee/ | ||||
|  |  | |||
							
								
								
									
										8
									
								
								drivers/tee/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								drivers/tee/Kconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| # Generic Trusted Execution Environment Configuration | ||||
| config TEE | ||||
| 	tristate "Trusted Execution Environment support" | ||||
| 	select DMA_SHARED_BUFFER | ||||
| 	select GENERIC_ALLOCATOR | ||||
| 	help | ||||
| 	  This implements a generic interface towards a Trusted Execution | ||||
| 	  Environment (TEE). | ||||
							
								
								
									
										4
									
								
								drivers/tee/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								drivers/tee/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| obj-$(CONFIG_TEE) += tee.o | ||||
| tee-objs += tee_core.o | ||||
| tee-objs += tee_shm.o | ||||
| tee-objs += tee_shm_pool.o | ||||
							
								
								
									
										893
									
								
								drivers/tee/tee_core.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										893
									
								
								drivers/tee/tee_core.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,893 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015-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. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) "%s: " fmt, __func__ | ||||
| 
 | ||||
| #include <linux/cdev.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/idr.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/tee_drv.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include "tee_private.h" | ||||
| 
 | ||||
| #define TEE_NUM_DEVICES	32 | ||||
| 
 | ||||
| #define TEE_IOCTL_PARAM_SIZE(x) (sizeof(struct tee_param) * (x)) | ||||
| 
 | ||||
| /*
 | ||||
|  * Unprivileged devices in the lower half range and privileged devices in | ||||
|  * the upper half range. | ||||
|  */ | ||||
| static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES); | ||||
| 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) | ||||
| { | ||||
| 	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; | ||||
| 
 | ||||
| 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||||
| 	if (!ctx) { | ||||
| 		rc = -ENOMEM; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	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; | ||||
| err: | ||||
| 	kfree(ctx); | ||||
| 	tee_device_put(teedev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int tee_release(struct inode *inode, struct file *filp) | ||||
| { | ||||
| 	struct tee_context *ctx = filp->private_data; | ||||
| 	struct tee_device *teedev = ctx->teedev; | ||||
| 	struct tee_shm *shm; | ||||
| 
 | ||||
| 	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); | ||||
| 	kfree(ctx); | ||||
| 	tee_device_put(teedev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tee_ioctl_version(struct tee_context *ctx, | ||||
| 			     struct tee_ioctl_version_data __user *uvers) | ||||
| { | ||||
| 	struct tee_ioctl_version_data vers; | ||||
| 
 | ||||
| 	ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); | ||||
| 	if (copy_to_user(uvers, &vers, sizeof(vers))) | ||||
| 		return -EFAULT; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tee_ioctl_shm_alloc(struct tee_context *ctx, | ||||
| 			       struct tee_ioctl_shm_alloc_data __user *udata) | ||||
| { | ||||
| 	long ret; | ||||
| 	struct tee_ioctl_shm_alloc_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; | ||||
| 
 | ||||
| 	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); | ||||
| 
 | ||||
| 	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) | ||||
| { | ||||
| 	size_t n; | ||||
| 
 | ||||
| 	for (n = 0; n < num_params; n++) { | ||||
| 		struct tee_shm *shm; | ||||
| 		struct tee_ioctl_param ip; | ||||
| 
 | ||||
| 		if (copy_from_user(&ip, uparams + n, sizeof(ip))) | ||||
| 			return -EFAULT; | ||||
| 
 | ||||
| 		/* All unused attribute bits has to be zero */ | ||||
| 		if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		params[n].attr = ip.attr; | ||||
| 		switch (ip.attr) { | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: | ||||
| 			break; | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: | ||||
| 			params[n].u.value.a = ip.a; | ||||
| 			params[n].u.value.b = ip.b; | ||||
| 			params[n].u.value.c = ip.c; | ||||
| 			break; | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: | ||||
| 			/*
 | ||||
| 			 * If we fail to get a pointer to a shared memory | ||||
| 			 * object (and increase the ref count) from an | ||||
| 			 * identifier we return an error. All pointers that | ||||
| 			 * has been added in params have an increased ref | ||||
| 			 * count. It's the callers responibility to do | ||||
| 			 * tee_shm_put() on all resolved pointers. | ||||
| 			 */ | ||||
| 			shm = tee_shm_get_from_id(ctx, ip.c); | ||||
| 			if (IS_ERR(shm)) | ||||
| 				return PTR_ERR(shm); | ||||
| 
 | ||||
| 			params[n].u.memref.shm_offs = ip.a; | ||||
| 			params[n].u.memref.size = ip.b; | ||||
| 			params[n].u.memref.shm = shm; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* Unknown attribute */ | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int params_to_user(struct tee_ioctl_param __user *uparams, | ||||
| 			  size_t num_params, struct tee_param *params) | ||||
| { | ||||
| 	size_t n; | ||||
| 
 | ||||
| 	for (n = 0; n < num_params; n++) { | ||||
| 		struct tee_ioctl_param __user *up = uparams + n; | ||||
| 		struct tee_param *p = params + n; | ||||
| 
 | ||||
| 		switch (p->attr) { | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: | ||||
| 			if (put_user(p->u.value.a, &up->a) || | ||||
| 			    put_user(p->u.value.b, &up->b) || | ||||
| 			    put_user(p->u.value.c, &up->c)) | ||||
| 				return -EFAULT; | ||||
| 			break; | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: | ||||
| 			if (put_user((u64)p->u.memref.size, &up->b)) | ||||
| 				return -EFAULT; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	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) | ||||
| { | ||||
| 	int rc; | ||||
| 	size_t n; | ||||
| 	struct tee_ioctl_buf_data buf; | ||||
| 	struct tee_ioctl_open_session_arg __user *uarg; | ||||
| 	struct tee_ioctl_open_session_arg arg; | ||||
| 	struct tee_ioctl_param __user *uparams = NULL; | ||||
| 	struct tee_param *params = NULL; | ||||
| 	bool have_session = false; | ||||
| 
 | ||||
| 	if (!ctx->teedev->desc->ops->open_session) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (copy_from_user(&buf, ubuf, sizeof(buf))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (buf.buf_len > TEE_MAX_ARG_SIZE || | ||||
| 	    buf.buf_len < sizeof(struct tee_ioctl_open_session_arg)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	uarg = u64_to_user_ptr(buf.buf_ptr); | ||||
| 	if (copy_from_user(&arg, uarg, sizeof(arg))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (arg.num_params) { | ||||
| 		params = kcalloc(arg.num_params, sizeof(struct tee_param), | ||||
| 				 GFP_KERNEL); | ||||
| 		if (!params) | ||||
| 			return -ENOMEM; | ||||
| 		uparams = uarg->params; | ||||
| 		rc = params_from_user(ctx, params, arg.num_params, uparams); | ||||
| 		if (rc) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params); | ||||
| 	if (rc) | ||||
| 		goto out; | ||||
| 	have_session = true; | ||||
| 
 | ||||
| 	if (put_user(arg.session, &uarg->session) || | ||||
| 	    put_user(arg.ret, &uarg->ret) || | ||||
| 	    put_user(arg.ret_origin, &uarg->ret_origin)) { | ||||
| 		rc = -EFAULT; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	rc = params_to_user(uparams, arg.num_params, params); | ||||
| out: | ||||
| 	/*
 | ||||
| 	 * If we've succeeded to open the session but failed to communicate | ||||
| 	 * it back to user space, close the session again to avoid leakage. | ||||
| 	 */ | ||||
| 	if (rc && have_session && ctx->teedev->desc->ops->close_session) | ||||
| 		ctx->teedev->desc->ops->close_session(ctx, arg.session); | ||||
| 
 | ||||
| 	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) && | ||||
| 			    params[n].u.memref.shm) | ||||
| 				tee_shm_put(params[n].u.memref.shm); | ||||
| 		kfree(params); | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int tee_ioctl_invoke(struct tee_context *ctx, | ||||
| 			    struct tee_ioctl_buf_data __user *ubuf) | ||||
| { | ||||
| 	int rc; | ||||
| 	size_t n; | ||||
| 	struct tee_ioctl_buf_data buf; | ||||
| 	struct tee_ioctl_invoke_arg __user *uarg; | ||||
| 	struct tee_ioctl_invoke_arg arg; | ||||
| 	struct tee_ioctl_param __user *uparams = NULL; | ||||
| 	struct tee_param *params = NULL; | ||||
| 
 | ||||
| 	if (!ctx->teedev->desc->ops->invoke_func) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (copy_from_user(&buf, ubuf, sizeof(buf))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (buf.buf_len > TEE_MAX_ARG_SIZE || | ||||
| 	    buf.buf_len < sizeof(struct tee_ioctl_invoke_arg)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	uarg = u64_to_user_ptr(buf.buf_ptr); | ||||
| 	if (copy_from_user(&arg, uarg, sizeof(arg))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (arg.num_params) { | ||||
| 		params = kcalloc(arg.num_params, sizeof(struct tee_param), | ||||
| 				 GFP_KERNEL); | ||||
| 		if (!params) | ||||
| 			return -ENOMEM; | ||||
| 		uparams = uarg->params; | ||||
| 		rc = params_from_user(ctx, params, arg.num_params, uparams); | ||||
| 		if (rc) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params); | ||||
| 	if (rc) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (put_user(arg.ret, &uarg->ret) || | ||||
| 	    put_user(arg.ret_origin, &uarg->ret_origin)) { | ||||
| 		rc = -EFAULT; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	rc = params_to_user(uparams, arg.num_params, params); | ||||
| out: | ||||
| 	if (params) { | ||||
| 		/* Decrease ref count for all valid shared memory pointers */ | ||||
| 		for (n = 0; n < arg.num_params; n++) | ||||
| 			if (param_is_memref(params + n) && | ||||
| 			    params[n].u.memref.shm) | ||||
| 				tee_shm_put(params[n].u.memref.shm); | ||||
| 		kfree(params); | ||||
| 	} | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int tee_ioctl_cancel(struct tee_context *ctx, | ||||
| 			    struct tee_ioctl_cancel_arg __user *uarg) | ||||
| { | ||||
| 	struct tee_ioctl_cancel_arg arg; | ||||
| 
 | ||||
| 	if (!ctx->teedev->desc->ops->cancel_req) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (copy_from_user(&arg, uarg, sizeof(arg))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	return ctx->teedev->desc->ops->cancel_req(ctx, arg.cancel_id, | ||||
| 						  arg.session); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| tee_ioctl_close_session(struct tee_context *ctx, | ||||
| 			struct tee_ioctl_close_session_arg __user *uarg) | ||||
| { | ||||
| 	struct tee_ioctl_close_session_arg arg; | ||||
| 
 | ||||
| 	if (!ctx->teedev->desc->ops->close_session) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (copy_from_user(&arg, uarg, sizeof(arg))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	return ctx->teedev->desc->ops->close_session(ctx, arg.session); | ||||
| } | ||||
| 
 | ||||
| static int params_to_supp(struct tee_context *ctx, | ||||
| 			  struct tee_ioctl_param __user *uparams, | ||||
| 			  size_t num_params, struct tee_param *params) | ||||
| { | ||||
| 	size_t n; | ||||
| 
 | ||||
| 	for (n = 0; n < num_params; n++) { | ||||
| 		struct tee_ioctl_param ip; | ||||
| 		struct tee_param *p = params + n; | ||||
| 
 | ||||
| 		ip.attr = p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK; | ||||
| 		switch (p->attr) { | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: | ||||
| 			ip.a = p->u.value.a; | ||||
| 			ip.b = p->u.value.b; | ||||
| 			ip.c = p->u.value.c; | ||||
| 			break; | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: | ||||
| 			ip.b = p->u.memref.size; | ||||
| 			if (!p->u.memref.shm) { | ||||
| 				ip.a = 0; | ||||
| 				ip.c = (u64)-1; /* invalid shm id */ | ||||
| 				break; | ||||
| 			} | ||||
| 			ip.a = p->u.memref.shm_offs; | ||||
| 			ip.c = p->u.memref.shm->id; | ||||
| 			break; | ||||
| 		default: | ||||
| 			ip.a = 0; | ||||
| 			ip.b = 0; | ||||
| 			ip.c = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (copy_to_user(uparams + n, &ip, sizeof(ip))) | ||||
| 			return -EFAULT; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tee_ioctl_supp_recv(struct tee_context *ctx, | ||||
| 			       struct tee_ioctl_buf_data __user *ubuf) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct tee_ioctl_buf_data buf; | ||||
| 	struct tee_iocl_supp_recv_arg __user *uarg; | ||||
| 	struct tee_param *params; | ||||
| 	u32 num_params; | ||||
| 	u32 func; | ||||
| 
 | ||||
| 	if (!ctx->teedev->desc->ops->supp_recv) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (copy_from_user(&buf, ubuf, sizeof(buf))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (buf.buf_len > TEE_MAX_ARG_SIZE || | ||||
| 	    buf.buf_len < sizeof(struct tee_iocl_supp_recv_arg)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	uarg = u64_to_user_ptr(buf.buf_ptr); | ||||
| 	if (get_user(num_params, &uarg->num_params)) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (sizeof(*uarg) + TEE_IOCTL_PARAM_SIZE(num_params) != buf.buf_len) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	params = kcalloc(num_params, sizeof(struct tee_param), GFP_KERNEL); | ||||
| 	if (!params) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params); | ||||
| 	if (rc) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (put_user(func, &uarg->func) || | ||||
| 	    put_user(num_params, &uarg->num_params)) { | ||||
| 		rc = -EFAULT; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = params_to_supp(ctx, uarg->params, num_params, params); | ||||
| out: | ||||
| 	kfree(params); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int params_from_supp(struct tee_param *params, size_t num_params, | ||||
| 			    struct tee_ioctl_param __user *uparams) | ||||
| { | ||||
| 	size_t n; | ||||
| 
 | ||||
| 	for (n = 0; n < num_params; n++) { | ||||
| 		struct tee_param *p = params + n; | ||||
| 		struct tee_ioctl_param ip; | ||||
| 
 | ||||
| 		if (copy_from_user(&ip, uparams + n, sizeof(ip))) | ||||
| 			return -EFAULT; | ||||
| 
 | ||||
| 		/* All unused attribute bits has to be zero */ | ||||
| 		if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		p->attr = ip.attr; | ||||
| 		switch (ip.attr) { | ||||
| 		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 */ | ||||
| 			p->u.value.a = ip.a; | ||||
| 			p->u.value.b = ip.b; | ||||
| 			p->u.value.c = ip.c; | ||||
| 			break; | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: | ||||
| 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: | ||||
| 			/*
 | ||||
| 			 * Only the size of the memref can be updated. | ||||
| 			 * Since we don't have access to the original | ||||
| 			 * parameters here, only store the supplied size. | ||||
| 			 * The driver will copy the updated size into the | ||||
| 			 * original parameters. | ||||
| 			 */ | ||||
| 			p->u.memref.shm = NULL; | ||||
| 			p->u.memref.shm_offs = 0; | ||||
| 			p->u.memref.size = ip.b; | ||||
| 			break; | ||||
| 		default: | ||||
| 			memset(&p->u, 0, sizeof(p->u)); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tee_ioctl_supp_send(struct tee_context *ctx, | ||||
| 			       struct tee_ioctl_buf_data __user *ubuf) | ||||
| { | ||||
| 	long rc; | ||||
| 	struct tee_ioctl_buf_data buf; | ||||
| 	struct tee_iocl_supp_send_arg __user *uarg; | ||||
| 	struct tee_param *params; | ||||
| 	u32 num_params; | ||||
| 	u32 ret; | ||||
| 
 | ||||
| 	/* Not valid for this driver */ | ||||
| 	if (!ctx->teedev->desc->ops->supp_send) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (copy_from_user(&buf, ubuf, sizeof(buf))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (buf.buf_len > TEE_MAX_ARG_SIZE || | ||||
| 	    buf.buf_len < sizeof(struct tee_iocl_supp_send_arg)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	uarg = u64_to_user_ptr(buf.buf_ptr); | ||||
| 	if (get_user(ret, &uarg->ret) || | ||||
| 	    get_user(num_params, &uarg->num_params)) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	if (sizeof(*uarg) + TEE_IOCTL_PARAM_SIZE(num_params) > buf.buf_len) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	params = kcalloc(num_params, sizeof(struct tee_param), GFP_KERNEL); | ||||
| 	if (!params) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	rc = params_from_supp(params, num_params, uarg->params); | ||||
| 	if (rc) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	rc = ctx->teedev->desc->ops->supp_send(ctx, ret, num_params, params); | ||||
| out: | ||||
| 	kfree(params); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | ||||
| { | ||||
| 	struct tee_context *ctx = filp->private_data; | ||||
| 	void __user *uarg = (void __user *)arg; | ||||
| 
 | ||||
| 	switch (cmd) { | ||||
| 	case TEE_IOC_VERSION: | ||||
| 		return tee_ioctl_version(ctx, uarg); | ||||
| 	case TEE_IOC_SHM_ALLOC: | ||||
| 		return tee_ioctl_shm_alloc(ctx, uarg); | ||||
| 	case TEE_IOC_OPEN_SESSION: | ||||
| 		return tee_ioctl_open_session(ctx, uarg); | ||||
| 	case TEE_IOC_INVOKE: | ||||
| 		return tee_ioctl_invoke(ctx, uarg); | ||||
| 	case TEE_IOC_CANCEL: | ||||
| 		return tee_ioctl_cancel(ctx, uarg); | ||||
| 	case TEE_IOC_CLOSE_SESSION: | ||||
| 		return tee_ioctl_close_session(ctx, uarg); | ||||
| 	case TEE_IOC_SUPPL_RECV: | ||||
| 		return tee_ioctl_supp_recv(ctx, uarg); | ||||
| 	case TEE_IOC_SUPPL_SEND: | ||||
| 		return tee_ioctl_supp_send(ctx, uarg); | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct file_operations tee_fops = { | ||||
| 	.owner = THIS_MODULE, | ||||
| 	.open = tee_open, | ||||
| 	.release = tee_release, | ||||
| 	.unlocked_ioctl = tee_ioctl, | ||||
| 	.compat_ioctl = tee_ioctl, | ||||
| }; | ||||
| 
 | ||||
| static void tee_release_device(struct device *dev) | ||||
| { | ||||
| 	struct tee_device *teedev = container_of(dev, struct tee_device, dev); | ||||
| 
 | ||||
| 	spin_lock(&driver_lock); | ||||
| 	clear_bit(teedev->id, dev_mask); | ||||
| 	spin_unlock(&driver_lock); | ||||
| 	mutex_destroy(&teedev->mutex); | ||||
| 	idr_destroy(&teedev->idr); | ||||
| 	kfree(teedev); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_device_alloc() - Allocate a new struct tee_device instance | ||||
|  * @teedesc:	Descriptor for this driver | ||||
|  * @dev:	Parent device for this device | ||||
|  * @pool:	Shared memory pool, NULL if not used | ||||
|  * @driver_data: Private driver data for this device | ||||
|  * | ||||
|  * Allocates a new struct tee_device instance. The device is | ||||
|  * removed by tee_device_unregister(). | ||||
|  * | ||||
|  * @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure | ||||
|  */ | ||||
| struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, | ||||
| 				    struct device *dev, | ||||
| 				    struct tee_shm_pool *pool, | ||||
| 				    void *driver_data) | ||||
| { | ||||
| 	struct tee_device *teedev; | ||||
| 	void *ret; | ||||
| 	int rc; | ||||
| 	int offs = 0; | ||||
| 
 | ||||
| 	if (!teedesc || !teedesc->name || !teedesc->ops || | ||||
| 	    !teedesc->ops->get_version || !teedesc->ops->open || | ||||
| 	    !teedesc->ops->release || !pool) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	teedev = kzalloc(sizeof(*teedev), GFP_KERNEL); | ||||
| 	if (!teedev) { | ||||
| 		ret = ERR_PTR(-ENOMEM); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (teedesc->flags & TEE_DESC_PRIVILEGED) | ||||
| 		offs = TEE_NUM_DEVICES / 2; | ||||
| 
 | ||||
| 	spin_lock(&driver_lock); | ||||
| 	teedev->id = find_next_zero_bit(dev_mask, TEE_NUM_DEVICES, offs); | ||||
| 	if (teedev->id < TEE_NUM_DEVICES) | ||||
| 		set_bit(teedev->id, dev_mask); | ||||
| 	spin_unlock(&driver_lock); | ||||
| 
 | ||||
| 	if (teedev->id >= TEE_NUM_DEVICES) { | ||||
| 		ret = ERR_PTR(-ENOMEM); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	snprintf(teedev->name, sizeof(teedev->name), "tee%s%d", | ||||
| 		 teedesc->flags & TEE_DESC_PRIVILEGED ? "priv" : "", | ||||
| 		 teedev->id - offs); | ||||
| 
 | ||||
| 	teedev->dev.class = tee_class; | ||||
| 	teedev->dev.release = tee_release_device; | ||||
| 	teedev->dev.parent = dev; | ||||
| 
 | ||||
| 	teedev->dev.devt = MKDEV(MAJOR(tee_devt), teedev->id); | ||||
| 
 | ||||
| 	rc = dev_set_name(&teedev->dev, "%s", teedev->name); | ||||
| 	if (rc) { | ||||
| 		ret = ERR_PTR(rc); | ||||
| 		goto err_devt; | ||||
| 	} | ||||
| 
 | ||||
| 	cdev_init(&teedev->cdev, &tee_fops); | ||||
| 	teedev->cdev.owner = teedesc->owner; | ||||
| 	teedev->cdev.kobj.parent = &teedev->dev.kobj; | ||||
| 
 | ||||
| 	dev_set_drvdata(&teedev->dev, driver_data); | ||||
| 	device_initialize(&teedev->dev); | ||||
| 
 | ||||
| 	/* 1 as tee_device_unregister() does one final tee_device_put() */ | ||||
| 	teedev->num_users = 1; | ||||
| 	init_completion(&teedev->c_no_users); | ||||
| 	mutex_init(&teedev->mutex); | ||||
| 	idr_init(&teedev->idr); | ||||
| 
 | ||||
| 	teedev->desc = teedesc; | ||||
| 	teedev->pool = pool; | ||||
| 
 | ||||
| 	return teedev; | ||||
| err_devt: | ||||
| 	unregister_chrdev_region(teedev->dev.devt, 1); | ||||
| err: | ||||
| 	pr_err("could not register %s driver\n", | ||||
| 	       teedesc->flags & TEE_DESC_PRIVILEGED ? "privileged" : "client"); | ||||
| 	if (teedev && teedev->id < TEE_NUM_DEVICES) { | ||||
| 		spin_lock(&driver_lock); | ||||
| 		clear_bit(teedev->id, dev_mask); | ||||
| 		spin_unlock(&driver_lock); | ||||
| 	} | ||||
| 	kfree(teedev); | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_device_alloc); | ||||
| 
 | ||||
| static ssize_t implementation_id_show(struct device *dev, | ||||
| 				      struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct tee_device *teedev = container_of(dev, struct tee_device, dev); | ||||
| 	struct tee_ioctl_version_data vers; | ||||
| 
 | ||||
| 	teedev->desc->ops->get_version(teedev, &vers); | ||||
| 	return scnprintf(buf, PAGE_SIZE, "%d\n", vers.impl_id); | ||||
| } | ||||
| static DEVICE_ATTR_RO(implementation_id); | ||||
| 
 | ||||
| static struct attribute *tee_dev_attrs[] = { | ||||
| 	&dev_attr_implementation_id.attr, | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group tee_dev_group = { | ||||
| 	.attrs = tee_dev_attrs, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_device_register() - Registers a TEE device | ||||
|  * @teedev:	Device to register | ||||
|  * | ||||
|  * tee_device_unregister() need to be called to remove the @teedev if | ||||
|  * this function fails. | ||||
|  * | ||||
|  * @returns < 0 on failure | ||||
|  */ | ||||
| int tee_device_register(struct tee_device *teedev) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) { | ||||
| 		dev_err(&teedev->dev, "attempt to register twice\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = cdev_add(&teedev->cdev, teedev->dev.devt, 1); | ||||
| 	if (rc) { | ||||
| 		dev_err(&teedev->dev, | ||||
| 			"unable to cdev_add() %s, major %d, minor %d, err=%d\n", | ||||
| 			teedev->name, MAJOR(teedev->dev.devt), | ||||
| 			MINOR(teedev->dev.devt), rc); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = device_add(&teedev->dev); | ||||
| 	if (rc) { | ||||
| 		dev_err(&teedev->dev, | ||||
| 			"unable to device_add() %s, major %d, minor %d, err=%d\n", | ||||
| 			teedev->name, MAJOR(teedev->dev.devt), | ||||
| 			MINOR(teedev->dev.devt), rc); | ||||
| 		goto err_device_add; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = sysfs_create_group(&teedev->dev.kobj, &tee_dev_group); | ||||
| 	if (rc) { | ||||
| 		dev_err(&teedev->dev, | ||||
| 			"failed to create sysfs attributes, err=%d\n", rc); | ||||
| 		goto err_sysfs_create_group; | ||||
| 	} | ||||
| 
 | ||||
| 	teedev->flags |= TEE_DEVICE_FLAG_REGISTERED; | ||||
| 	return 0; | ||||
| 
 | ||||
| err_sysfs_create_group: | ||||
| 	device_del(&teedev->dev); | ||||
| err_device_add: | ||||
| 	cdev_del(&teedev->cdev); | ||||
| 	return rc; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_device_register); | ||||
| 
 | ||||
| void tee_device_put(struct tee_device *teedev) | ||||
| { | ||||
| 	mutex_lock(&teedev->mutex); | ||||
| 	/* Shouldn't put in this state */ | ||||
| 	if (!WARN_ON(!teedev->desc)) { | ||||
| 		teedev->num_users--; | ||||
| 		if (!teedev->num_users) { | ||||
| 			teedev->desc = NULL; | ||||
| 			complete(&teedev->c_no_users); | ||||
| 		} | ||||
| 	} | ||||
| 	mutex_unlock(&teedev->mutex); | ||||
| } | ||||
| 
 | ||||
| bool tee_device_get(struct tee_device *teedev) | ||||
| { | ||||
| 	mutex_lock(&teedev->mutex); | ||||
| 	if (!teedev->desc) { | ||||
| 		mutex_unlock(&teedev->mutex); | ||||
| 		return false; | ||||
| 	} | ||||
| 	teedev->num_users++; | ||||
| 	mutex_unlock(&teedev->mutex); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_device_unregister() - Removes a TEE device | ||||
|  * @teedev:	Device to unregister | ||||
|  * | ||||
|  * This function should be called to remove the @teedev even if | ||||
|  * tee_device_register() hasn't been called yet. Does nothing if | ||||
|  * @teedev is NULL. | ||||
|  */ | ||||
| void tee_device_unregister(struct tee_device *teedev) | ||||
| { | ||||
| 	if (!teedev) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) { | ||||
| 		sysfs_remove_group(&teedev->dev.kobj, &tee_dev_group); | ||||
| 		cdev_del(&teedev->cdev); | ||||
| 		device_del(&teedev->dev); | ||||
| 	} | ||||
| 
 | ||||
| 	tee_device_put(teedev); | ||||
| 	wait_for_completion(&teedev->c_no_users); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * No need to take a mutex any longer now since teedev->desc was | ||||
| 	 * set to NULL before teedev->c_no_users was completed. | ||||
| 	 */ | ||||
| 
 | ||||
| 	teedev->pool = NULL; | ||||
| 
 | ||||
| 	put_device(&teedev->dev); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_device_unregister); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_get_drvdata() - Return driver_data pointer | ||||
|  * @teedev:	Device containing the driver_data pointer | ||||
|  * @returns the driver_data pointer supplied to tee_register(). | ||||
|  */ | ||||
| void *tee_get_drvdata(struct tee_device *teedev) | ||||
| { | ||||
| 	return dev_get_drvdata(&teedev->dev); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_get_drvdata); | ||||
| 
 | ||||
| static int __init tee_init(void) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	tee_class = class_create(THIS_MODULE, "tee"); | ||||
| 	if (IS_ERR(tee_class)) { | ||||
| 		pr_err("couldn't create class\n"); | ||||
| 		return PTR_ERR(tee_class); | ||||
| 	} | ||||
| 
 | ||||
| 	rc = alloc_chrdev_region(&tee_devt, 0, TEE_NUM_DEVICES, "tee"); | ||||
| 	if (rc) { | ||||
| 		pr_err("failed to allocate char dev region\n"); | ||||
| 		class_destroy(tee_class); | ||||
| 		tee_class = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static void __exit tee_exit(void) | ||||
| { | ||||
| 	class_destroy(tee_class); | ||||
| 	tee_class = NULL; | ||||
| 	unregister_chrdev_region(tee_devt, TEE_NUM_DEVICES); | ||||
| } | ||||
| 
 | ||||
| subsys_initcall(tee_init); | ||||
| module_exit(tee_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR("Linaro"); | ||||
| MODULE_DESCRIPTION("TEE Driver"); | ||||
| MODULE_VERSION("1.0"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
							
								
								
									
										129
									
								
								drivers/tee/tee_private.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								drivers/tee/tee_private.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,129 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015-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 TEE_PRIVATE_H | ||||
| #define TEE_PRIVATE_H | ||||
| 
 | ||||
| #include <linux/cdev.h> | ||||
| #include <linux/completion.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/kref.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| 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; | ||||
| }; | ||||
| 
 | ||||
| #define TEE_DEVICE_FLAG_REGISTERED	0x1 | ||||
| #define TEE_MAX_DEV_NAME_LEN		32 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_device - TEE Device representation | ||||
|  * @name:	name of device | ||||
|  * @desc:	description of device | ||||
|  * @id:		unique id of device | ||||
|  * @flags:	represented by TEE_DEVICE_FLAG_REGISTERED above | ||||
|  * @dev:	embedded basic device structure | ||||
|  * @cdev:	embedded cdev | ||||
|  * @num_users:	number of active users of this device | ||||
|  * @c_no_user:	completion used when unregistering the device | ||||
|  * @mutex:	mutex protecting @num_users and @idr | ||||
|  * @idr:	register of shared memory object allocated on this device | ||||
|  * @pool:	shared memory pool | ||||
|  */ | ||||
| struct tee_device { | ||||
| 	char name[TEE_MAX_DEV_NAME_LEN]; | ||||
| 	const struct tee_desc *desc; | ||||
| 	int id; | ||||
| 	unsigned int flags; | ||||
| 
 | ||||
| 	struct device dev; | ||||
| 	struct cdev cdev; | ||||
| 
 | ||||
| 	size_t num_users; | ||||
| 	struct completion c_no_users; | ||||
| 	struct mutex mutex;	/* protects num_users and idr */ | ||||
| 
 | ||||
| 	struct idr idr; | ||||
| 	struct tee_shm_pool *pool; | ||||
| }; | ||||
| 
 | ||||
| int tee_shm_init(void); | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| #endif /*TEE_PRIVATE_H*/ | ||||
							
								
								
									
										358
									
								
								drivers/tee/tee_shm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								drivers/tee/tee_shm.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,358 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015-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. | ||||
|  * | ||||
|  */ | ||||
| #include <linux/device.h> | ||||
| #include <linux/dma-buf.h> | ||||
| #include <linux/fdtable.h> | ||||
| #include <linux/idr.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/tee_drv.h> | ||||
| #include "tee_private.h" | ||||
| 
 | ||||
| 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); | ||||
| 	if (shm->ctx) | ||||
| 		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; | ||||
| 
 | ||||
| 	poolm->ops->free(poolm, shm); | ||||
| 	kfree(shm); | ||||
| 
 | ||||
| 	tee_device_put(teedev); | ||||
| } | ||||
| 
 | ||||
| static struct sg_table *tee_shm_op_map_dma_buf(struct dma_buf_attachment | ||||
| 			*attach, enum dma_data_direction dir) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void tee_shm_op_unmap_dma_buf(struct dma_buf_attachment *attach, | ||||
| 				     struct sg_table *table, | ||||
| 				     enum dma_data_direction dir) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void tee_shm_op_release(struct dma_buf *dmabuf) | ||||
| { | ||||
| 	struct tee_shm *shm = dmabuf->priv; | ||||
| 
 | ||||
| 	tee_shm_release(shm); | ||||
| } | ||||
| 
 | ||||
| static void *tee_shm_op_kmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void *tee_shm_op_kmap(struct dma_buf *dmabuf, unsigned long pgnum) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 	return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, | ||||
| 			       size, vma->vm_page_prot); | ||||
| } | ||||
| 
 | ||||
| static 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, | ||||
| 	.kmap_atomic = tee_shm_op_kmap_atomic, | ||||
| 	.kmap = tee_shm_op_kmap, | ||||
| 	.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 <linux/tee_drv.h>. 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_device *teedev = ctx->teedev; | ||||
| 	struct tee_shm_pool_mgr *poolm = NULL; | ||||
| 	struct tee_shm *shm; | ||||
| 	void *ret; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	if (!(flags & TEE_SHM_MAPPED)) { | ||||
| 		dev_err(teedev->dev.parent, | ||||
| 			"only mapped allocations supported\n"); | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 
 | ||||
| 	if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) { | ||||
| 		dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags); | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!tee_device_get(teedev)) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	if (!teedev->pool) { | ||||
| 		/* teedev has been detached from driver */ | ||||
| 		ret = ERR_PTR(-EINVAL); | ||||
| 		goto err_dev_put; | ||||
| 	} | ||||
| 
 | ||||
| 	shm = kzalloc(sizeof(*shm), GFP_KERNEL); | ||||
| 	if (!shm) { | ||||
| 		ret = ERR_PTR(-ENOMEM); | ||||
| 		goto err_dev_put; | ||||
| 	} | ||||
| 
 | ||||
| 	shm->flags = flags; | ||||
| 	shm->teedev = teedev; | ||||
| 	shm->ctx = ctx; | ||||
| 	if (flags & TEE_SHM_DMA_BUF) | ||||
| 		poolm = &teedev->pool->dma_buf_mgr; | ||||
| 	else | ||||
| 		poolm = &teedev->pool->private_mgr; | ||||
| 
 | ||||
| 	rc = poolm->ops->alloc(poolm, shm, size); | ||||
| 	if (rc) { | ||||
| 		ret = ERR_PTR(rc); | ||||
| 		goto err_kfree; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_lock(&teedev->mutex); | ||||
| 	shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); | ||||
| 	mutex_unlock(&teedev->mutex); | ||||
| 	if (shm->id < 0) { | ||||
| 		ret = ERR_PTR(shm->id); | ||||
| 		goto err_pool_free; | ||||
| 	} | ||||
| 
 | ||||
| 	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); | ||||
| 			goto err_rem; | ||||
| 		} | ||||
| 	} | ||||
| 	mutex_lock(&teedev->mutex); | ||||
| 	list_add_tail(&shm->link, &ctx->list_shm); | ||||
| 	mutex_unlock(&teedev->mutex); | ||||
| 
 | ||||
| 	return shm; | ||||
| err_rem: | ||||
| 	mutex_lock(&teedev->mutex); | ||||
| 	idr_remove(&teedev->idr, shm->id); | ||||
| 	mutex_unlock(&teedev->mutex); | ||||
| err_pool_free: | ||||
| 	poolm->ops->free(poolm, shm); | ||||
| err_kfree: | ||||
| 	kfree(shm); | ||||
| err_dev_put: | ||||
| 	tee_device_put(teedev); | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_alloc); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_get_fd() - Increase reference count and return file descriptor | ||||
|  * @shm:	Shared memory handle | ||||
|  * @returns user space file descriptor to shared memory | ||||
|  */ | ||||
| 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) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC); | ||||
| 	if (fd >= 0) | ||||
| 		get_dma_buf(shm->dmabuf); | ||||
| 	return fd; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_free() - Free shared memory | ||||
|  * @shm:	Handle to shared memory to free | ||||
|  */ | ||||
| void tee_shm_free(struct tee_shm *shm) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * dma_buf_put() decreases the dmabuf reference counter and will | ||||
| 	 * call tee_shm_release() when the last reference is gone. | ||||
| 	 * | ||||
| 	 * In the case of driver private memory we call tee_shm_release | ||||
| 	 * directly instead as it doesn't have a reference counter. | ||||
| 	 */ | ||||
| 	if (shm->flags & TEE_SHM_DMA_BUF) | ||||
| 		dma_buf_put(shm->dmabuf); | ||||
| 	else | ||||
| 		tee_shm_release(shm); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_free); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_va2pa() - Get physical address of a virtual address | ||||
|  * @shm:	Shared memory handle | ||||
|  * @va:		Virtual address to tranlsate | ||||
|  * @pa:		Returned physical address | ||||
|  * @returns 0 on success and < 0 on failure | ||||
|  */ | ||||
| int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa) | ||||
| { | ||||
| 	/* Check that we're in the range of the shm */ | ||||
| 	if ((char *)va < (char *)shm->kaddr) | ||||
| 		return -EINVAL; | ||||
| 	if ((char *)va >= ((char *)shm->kaddr + shm->size)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return tee_shm_get_pa( | ||||
| 			shm, (unsigned long)va - (unsigned long)shm->kaddr, pa); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_va2pa); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_pa2va() - Get virtual address of a physical address | ||||
|  * @shm:	Shared memory handle | ||||
|  * @pa:		Physical address to tranlsate | ||||
|  * @va:		Returned virtual address | ||||
|  * @returns 0 on success and < 0 on failure | ||||
|  */ | ||||
| int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va) | ||||
| { | ||||
| 	/* Check that we're in the range of the shm */ | ||||
| 	if (pa < shm->paddr) | ||||
| 		return -EINVAL; | ||||
| 	if (pa >= (shm->paddr + shm->size)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (va) { | ||||
| 		void *v = tee_shm_get_va(shm, pa - shm->paddr); | ||||
| 
 | ||||
| 		if (IS_ERR(v)) | ||||
| 			return PTR_ERR(v); | ||||
| 		*va = v; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_pa2va); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_get_va() - Get virtual address of a shared memory plus an offset | ||||
|  * @shm:	Shared memory handle | ||||
|  * @offs:	Offset from start of this shared memory | ||||
|  * @returns virtual address of the shared memory + offs if offs is within | ||||
|  *	the bounds of this shared memory, else an ERR_PTR | ||||
|  */ | ||||
| void *tee_shm_get_va(struct tee_shm *shm, size_t offs) | ||||
| { | ||||
| 	if (offs >= shm->size) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	return (char *)shm->kaddr + offs; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_get_va); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_get_pa() - Get physical address of a shared memory plus an offset | ||||
|  * @shm:	Shared memory handle | ||||
|  * @offs:	Offset from start of this shared memory | ||||
|  * @pa:		Physical address to return | ||||
|  * @returns 0 if offs is within the bounds of this shared memory, else an | ||||
|  *	error code. | ||||
|  */ | ||||
| int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa) | ||||
| { | ||||
| 	if (offs >= shm->size) | ||||
| 		return -EINVAL; | ||||
| 	if (pa) | ||||
| 		*pa = shm->paddr + offs; | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_get_pa); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_get_from_id() - Find shared memory object and increase reference | ||||
|  * count | ||||
|  * @ctx:	Context owning the shared memory | ||||
|  * @id:		Id of shared memory object | ||||
|  * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure | ||||
|  */ | ||||
| struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) | ||||
| { | ||||
| 	struct tee_device *teedev; | ||||
| 	struct tee_shm *shm; | ||||
| 
 | ||||
| 	if (!ctx) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	teedev = ctx->teedev; | ||||
| 	mutex_lock(&teedev->mutex); | ||||
| 	shm = idr_find(&teedev->idr, id); | ||||
| 	if (!shm || shm->ctx != ctx) | ||||
| 		shm = ERR_PTR(-EINVAL); | ||||
| 	else if (shm->flags & TEE_SHM_DMA_BUF) | ||||
| 		get_dma_buf(shm->dmabuf); | ||||
| 	mutex_unlock(&teedev->mutex); | ||||
| 	return shm; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_get_from_id); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 | ||||
|  */ | ||||
| void tee_shm_put(struct tee_shm *shm) | ||||
| { | ||||
| 	if (shm->flags & TEE_SHM_DMA_BUF) | ||||
| 		dma_buf_put(shm->dmabuf); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_put); | ||||
							
								
								
									
										156
									
								
								drivers/tee/tee_shm_pool.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								drivers/tee/tee_shm_pool.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,156 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015, Linaro Limited | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  */ | ||||
| #include <linux/device.h> | ||||
| #include <linux/dma-buf.h> | ||||
| #include <linux/genalloc.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/tee_drv.h> | ||||
| #include "tee_private.h" | ||||
| 
 | ||||
| static int pool_op_gen_alloc(struct tee_shm_pool_mgr *poolm, | ||||
| 			     struct tee_shm *shm, size_t size) | ||||
| { | ||||
| 	unsigned long va; | ||||
| 	struct gen_pool *genpool = poolm->private_data; | ||||
| 	size_t s = roundup(size, 1 << genpool->min_alloc_order); | ||||
| 
 | ||||
| 	va = gen_pool_alloc(genpool, s); | ||||
| 	if (!va) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	memset((void *)va, 0, s); | ||||
| 	shm->kaddr = (void *)va; | ||||
| 	shm->paddr = gen_pool_virt_to_phys(genpool, va); | ||||
| 	shm->size = s; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm, | ||||
| 			     struct tee_shm *shm) | ||||
| { | ||||
| 	gen_pool_free(poolm->private_data, (unsigned long)shm->kaddr, | ||||
| 		      shm->size); | ||||
| 	shm->kaddr = NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct tee_shm_pool_mgr_ops pool_ops_generic = { | ||||
| 	.alloc = pool_op_gen_alloc, | ||||
| 	.free = pool_op_gen_free, | ||||
| }; | ||||
| 
 | ||||
| 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 | ||||
|  * @priv_info:	Information for driver private shared memory pool | ||||
|  * @dmabuf_info: Information for dma-buf shared memory pool | ||||
|  * | ||||
|  * Start and end of pools will must be page aligned. | ||||
|  * | ||||
|  * 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_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; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Create the pool for dma_buf shared memory | ||||
| 	 */ | ||||
| 	ret = pool_res_mem_mgr_init(&pool->dma_buf_mgr, dmabuf_info, | ||||
| 				    PAGE_SHIFT); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	pool->destroy = pool_res_mem_destroy; | ||||
| 	return pool; | ||||
| 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); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_pool_free() - Free a shared memory pool | ||||
|  * @pool:	The shared memory pool to free | ||||
|  * | ||||
|  * There must be no remaining shared memory allocated from this pool when | ||||
|  * this function is called. | ||||
|  */ | ||||
| void tee_shm_pool_free(struct tee_shm_pool *pool) | ||||
| { | ||||
| 	pool->destroy(pool); | ||||
| 	kfree(pool); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tee_shm_pool_free); | ||||
							
								
								
									
										277
									
								
								include/linux/tee_drv.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								include/linux/tee_drv.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,277 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015-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 __TEE_DRV_H | ||||
| #define __TEE_DRV_H | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/idr.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/tee.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * The file describes the API provided by the generic TEE driver to the | ||||
|  * specific TEE driver. | ||||
|  */ | ||||
| 
 | ||||
| #define TEE_SHM_MAPPED		0x1	/* Memory mapped by the kernel */ | ||||
| #define TEE_SHM_DMA_BUF		0x2	/* Memory with dma-buf handle */ | ||||
| 
 | ||||
| struct tee_device; | ||||
| struct tee_shm; | ||||
| struct tee_shm_pool; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_context - driver specific context on file pointer data | ||||
|  * @teedev:	pointer to this drivers struct tee_device | ||||
|  * @list_shm:	List of shared memory object owned by this context | ||||
|  * @data:	driver specific context data, managed by the driver | ||||
|  */ | ||||
| struct tee_context { | ||||
| 	struct tee_device *teedev; | ||||
| 	struct list_head list_shm; | ||||
| 	void *data; | ||||
| }; | ||||
| 
 | ||||
| struct tee_param_memref { | ||||
| 	size_t shm_offs; | ||||
| 	size_t size; | ||||
| 	struct tee_shm *shm; | ||||
| }; | ||||
| 
 | ||||
| struct tee_param_value { | ||||
| 	u64 a; | ||||
| 	u64 b; | ||||
| 	u64 c; | ||||
| }; | ||||
| 
 | ||||
| struct tee_param { | ||||
| 	u64 attr; | ||||
| 	union { | ||||
| 		struct tee_param_memref memref; | ||||
| 		struct tee_param_value value; | ||||
| 	} u; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_driver_ops - driver operations vtable | ||||
|  * @get_version:	returns version of driver | ||||
|  * @open:		called when the device file is opened | ||||
|  * @release:		release this open file | ||||
|  * @open_session:	open a new session | ||||
|  * @close_session:	close a session | ||||
|  * @invoke_func:	invoke a trusted function | ||||
|  * @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 | ||||
|  */ | ||||
| struct tee_driver_ops { | ||||
| 	void (*get_version)(struct tee_device *teedev, | ||||
| 			    struct tee_ioctl_version_data *vers); | ||||
| 	int (*open)(struct tee_context *ctx); | ||||
| 	void (*release)(struct tee_context *ctx); | ||||
| 	int (*open_session)(struct tee_context *ctx, | ||||
| 			    struct tee_ioctl_open_session_arg *arg, | ||||
| 			    struct tee_param *param); | ||||
| 	int (*close_session)(struct tee_context *ctx, u32 session); | ||||
| 	int (*invoke_func)(struct tee_context *ctx, | ||||
| 			   struct tee_ioctl_invoke_arg *arg, | ||||
| 			   struct tee_param *param); | ||||
| 	int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session); | ||||
| 	int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params, | ||||
| 			 struct tee_param *param); | ||||
| 	int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params, | ||||
| 			 struct tee_param *param); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_desc - Describes the TEE driver to the subsystem | ||||
|  * @name:	name of driver | ||||
|  * @ops:	driver operations vtable | ||||
|  * @owner:	module providing the driver | ||||
|  * @flags:	Extra properties of driver, defined by TEE_DESC_* below | ||||
|  */ | ||||
| #define TEE_DESC_PRIVILEGED	0x1 | ||||
| struct tee_desc { | ||||
| 	const char *name; | ||||
| 	const struct tee_driver_ops *ops; | ||||
| 	struct module *owner; | ||||
| 	u32 flags; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_device_alloc() - Allocate a new struct tee_device instance | ||||
|  * @teedesc:	Descriptor for this driver | ||||
|  * @dev:	Parent device for this device | ||||
|  * @pool:	Shared memory pool, NULL if not used | ||||
|  * @driver_data: Private driver data for this device | ||||
|  * | ||||
|  * Allocates a new struct tee_device instance. The device is | ||||
|  * removed by tee_device_unregister(). | ||||
|  * | ||||
|  * @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure | ||||
|  */ | ||||
| struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, | ||||
| 				    struct device *dev, | ||||
| 				    struct tee_shm_pool *pool, | ||||
| 				    void *driver_data); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_device_register() - Registers a TEE device | ||||
|  * @teedev:	Device to register | ||||
|  * | ||||
|  * tee_device_unregister() need to be called to remove the @teedev if | ||||
|  * this function fails. | ||||
|  * | ||||
|  * @returns < 0 on failure | ||||
|  */ | ||||
| int tee_device_register(struct tee_device *teedev); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_device_unregister() - Removes a TEE device | ||||
|  * @teedev:	Device to unregister | ||||
|  * | ||||
|  * This function should be called to remove the @teedev even if | ||||
|  * tee_device_register() hasn't been called yet. Does nothing if | ||||
|  * @teedev is NULL. | ||||
|  */ | ||||
| void tee_device_unregister(struct tee_device *teedev); | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_shm_pool_mem_info - holds information needed to create a shared | ||||
|  * memory pool | ||||
|  * @vaddr:	Virtual address of start of pool | ||||
|  * @paddr:	Physical address of start of pool | ||||
|  * @size:	Size in bytes of the pool | ||||
|  */ | ||||
| struct tee_shm_pool_mem_info { | ||||
| 	unsigned long vaddr; | ||||
| 	phys_addr_t paddr; | ||||
| 	size_t size; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved | ||||
|  * memory range | ||||
|  * @priv_info:	 Information for driver private shared memory pool | ||||
|  * @dmabuf_info: Information for dma-buf shared memory pool | ||||
|  * | ||||
|  * Start and end of pools will must be page aligned. | ||||
|  * | ||||
|  * 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_res_mem(struct tee_shm_pool_mem_info *priv_info, | ||||
| 			   struct tee_shm_pool_mem_info *dmabuf_info); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_pool_free() - Free a shared memory pool | ||||
|  * @pool:	The shared memory pool to free | ||||
|  * | ||||
|  * The must be no remaining shared memory allocated from this pool when | ||||
|  * this function is called. | ||||
|  */ | ||||
| void tee_shm_pool_free(struct tee_shm_pool *pool); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_get_drvdata() - Return driver_data pointer | ||||
|  * @returns the driver_data pointer supplied to tee_register(). | ||||
|  */ | ||||
| void *tee_get_drvdata(struct tee_device *teedev); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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_* above. 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. | ||||
|  * | ||||
|  * @returns a pointer to 'struct tee_shm' | ||||
|  */ | ||||
| struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_free() - Free shared memory | ||||
|  * @shm:	Handle to shared memory to free | ||||
|  */ | ||||
| void tee_shm_free(struct tee_shm *shm); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_put() - Decrease reference count on a shared memory handle | ||||
|  * @shm:	Shared memory handle | ||||
|  */ | ||||
| void tee_shm_put(struct tee_shm *shm); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_va2pa() - Get physical address of a virtual address | ||||
|  * @shm:	Shared memory handle | ||||
|  * @va:		Virtual address to tranlsate | ||||
|  * @pa:		Returned physical address | ||||
|  * @returns 0 on success and < 0 on failure | ||||
|  */ | ||||
| int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_pa2va() - Get virtual address of a physical address | ||||
|  * @shm:	Shared memory handle | ||||
|  * @pa:		Physical address to tranlsate | ||||
|  * @va:		Returned virtual address | ||||
|  * @returns 0 on success and < 0 on failure | ||||
|  */ | ||||
| int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_get_va() - Get virtual address of a shared memory plus an offset | ||||
|  * @shm:	Shared memory handle | ||||
|  * @offs:	Offset from start of this shared memory | ||||
|  * @returns virtual address of the shared memory + offs if offs is within | ||||
|  *	the bounds of this shared memory, else an ERR_PTR | ||||
|  */ | ||||
| void *tee_shm_get_va(struct tee_shm *shm, size_t offs); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_get_pa() - Get physical address of a shared memory plus an offset | ||||
|  * @shm:	Shared memory handle | ||||
|  * @offs:	Offset from start of this shared memory | ||||
|  * @pa:		Physical address to return | ||||
|  * @returns 0 if offs is within the bounds of this shared memory, else an | ||||
|  *	error code. | ||||
|  */ | ||||
| int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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); | ||||
| 
 | ||||
| /**
 | ||||
|  * tee_shm_get_from_id() - Find shared memory object and increase reference | ||||
|  * count | ||||
|  * @ctx:	Context owning the shared memory | ||||
|  * @id:		Id of shared memory object | ||||
|  * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure | ||||
|  */ | ||||
| struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id); | ||||
| 
 | ||||
| #endif /*__TEE_DRV_H*/ | ||||
							
								
								
									
										346
									
								
								include/uapi/linux/tee.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								include/uapi/linux/tee.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,346 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015-2016, Linaro Limited | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright notice, | ||||
|  * this list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|  * this list of conditions and the following disclaimer in the documentation | ||||
|  * and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
|  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||
|  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
|  * POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __TEE_H | ||||
| #define __TEE_H | ||||
| 
 | ||||
| #include <linux/ioctl.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * This file describes the API provided by a TEE driver to user space. | ||||
|  * | ||||
|  * Each TEE driver defines a TEE specific protocol which is used for the | ||||
|  * data passed back and forth using TEE_IOC_CMD. | ||||
|  */ | ||||
| 
 | ||||
| /* Helpers to make the ioctl defines */ | ||||
| #define TEE_IOC_MAGIC	0xa4 | ||||
| #define TEE_IOC_BASE	0 | ||||
| 
 | ||||
| /* Flags relating to shared memory */ | ||||
| #define TEE_IOCTL_SHM_MAPPED	0x1	/* memory mapped in normal world */ | ||||
| #define TEE_IOCTL_SHM_DMA_BUF	0x2	/* dma-buf handle on shared memory */ | ||||
| 
 | ||||
| #define TEE_MAX_ARG_SIZE	1024 | ||||
| 
 | ||||
| #define TEE_GEN_CAP_GP		(1 << 0)/* GlobalPlatform compliant TEE */ | ||||
| 
 | ||||
| /*
 | ||||
|  * TEE Implementation ID | ||||
|  */ | ||||
| #define TEE_IMPL_ID_OPTEE	1 | ||||
| 
 | ||||
| /*
 | ||||
|  * OP-TEE specific capabilities | ||||
|  */ | ||||
| #define TEE_OPTEE_CAP_TZ	(1 << 0) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_version_data - TEE version | ||||
|  * @impl_id:	[out] TEE implementation id | ||||
|  * @impl_caps:	[out] Implementation specific capabilities | ||||
|  * @gen_caps:	[out] Generic capabilities, defined by TEE_GEN_CAPS_* above | ||||
|  * | ||||
|  * Identifies the TEE implementation, @impl_id is one of TEE_IMPL_ID_* above. | ||||
|  * @impl_caps is implementation specific, for example TEE_OPTEE_CAP_* | ||||
|  * is valid when @impl_id == TEE_IMPL_ID_OPTEE. | ||||
|  */ | ||||
| struct tee_ioctl_version_data { | ||||
| 	__u32 impl_id; | ||||
| 	__u32 impl_caps; | ||||
| 	__u32 gen_caps; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_VERSION - query version of TEE | ||||
|  * | ||||
|  * Takes a tee_ioctl_version_data struct and returns with the TEE version | ||||
|  * data filled in. | ||||
|  */ | ||||
| #define TEE_IOC_VERSION		_IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 0, \ | ||||
| 				     struct tee_ioctl_version_data) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_shm_alloc_data - Shared memory allocate argument | ||||
|  * @size:	[in/out] Size of shared memory to allocate | ||||
|  * @flags:	[in/out] 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_alloc_data { | ||||
| 	__u64 size; | ||||
| 	__u32 flags; | ||||
| 	__s32 id; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_SHM_ALLOC - allocate shared memory | ||||
|  * | ||||
|  * Allocates shared memory between the user space process and secure OS. | ||||
|  * | ||||
|  * Returns a file descriptor on success or < 0 on failure | ||||
|  * | ||||
|  * The returned file descriptor is used to map the shared memory into user | ||||
|  * space. The shared memory is freed when the descriptor is closed and the | ||||
|  * memory is unmapped. | ||||
|  */ | ||||
| #define TEE_IOC_SHM_ALLOC	_IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 1, \ | ||||
| 				     struct tee_ioctl_shm_alloc_data) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_buf_data - Variable sized buffer | ||||
|  * @buf_ptr:	[in] A __user pointer to a buffer | ||||
|  * @buf_len:	[in] Length of the buffer above | ||||
|  * | ||||
|  * Used as argument for TEE_IOC_OPEN_SESSION, TEE_IOC_INVOKE, | ||||
|  * TEE_IOC_SUPPL_RECV, and TEE_IOC_SUPPL_SEND below. | ||||
|  */ | ||||
| struct tee_ioctl_buf_data { | ||||
| 	__u64 buf_ptr; | ||||
| 	__u64 buf_len; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Attributes for struct tee_ioctl_param, selects field in the union | ||||
|  */ | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_NONE		0	/* parameter not used */ | ||||
| 
 | ||||
| /*
 | ||||
|  * These defines value parameters (struct tee_ioctl_param_value) | ||||
|  */ | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT	1 | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT	2 | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT	3	/* input and output */ | ||||
| 
 | ||||
| /*
 | ||||
|  * These defines shared memory reference parameters (struct | ||||
|  * tee_ioctl_param_memref) | ||||
|  */ | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT	5 | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT	6 | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT	7	/* input and output */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Mask for the type part of the attribute, leaves room for more types | ||||
|  */ | ||||
| #define TEE_IOCTL_PARAM_ATTR_TYPE_MASK		0xff | ||||
| 
 | ||||
| /*
 | ||||
|  * Matches TEEC_LOGIN_* in GP TEE Client API | ||||
|  * Are only defined for GP compliant TEEs | ||||
|  */ | ||||
| #define TEE_IOCTL_LOGIN_PUBLIC			0 | ||||
| #define TEE_IOCTL_LOGIN_USER			1 | ||||
| #define TEE_IOCTL_LOGIN_GROUP			2 | ||||
| #define TEE_IOCTL_LOGIN_APPLICATION		4 | ||||
| #define TEE_IOCTL_LOGIN_USER_APPLICATION	5 | ||||
| #define TEE_IOCTL_LOGIN_GROUP_APPLICATION	6 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_param - parameter | ||||
|  * @attr: attributes | ||||
|  * @a: if a memref, offset into the shared memory object, else a value parameter | ||||
|  * @b: if a memref, size of the buffer, else a value parameter | ||||
|  * @c: if a memref, shared memory identifier, else a value parameter | ||||
|  * | ||||
|  * @attr & TEE_PARAM_ATTR_TYPE_MASK indicates if memref or value is used in | ||||
|  * the union. TEE_PARAM_ATTR_TYPE_VALUE_* indicates value and | ||||
|  * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref. TEE_PARAM_ATTR_TYPE_NONE | ||||
|  * indicates that none of the members are used. | ||||
|  * | ||||
|  * Shared memory is allocated with TEE_IOC_SHM_ALLOC which returns an | ||||
|  * identifier representing the shared memory object. A memref can reference | ||||
|  * a part of a shared memory by specifying an offset (@a) and size (@b) of | ||||
|  * the object. To supply the entire shared memory object set the offset | ||||
|  * (@a) to 0 and size (@b) to the previously returned size of the object. | ||||
|  */ | ||||
| struct tee_ioctl_param { | ||||
| 	__u64 attr; | ||||
| 	__u64 a; | ||||
| 	__u64 b; | ||||
| 	__u64 c; | ||||
| }; | ||||
| 
 | ||||
| #define TEE_IOCTL_UUID_LEN		16 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_open_session_arg - Open session argument | ||||
|  * @uuid:	[in] UUID of the Trusted Application | ||||
|  * @clnt_uuid:	[in] UUID of client | ||||
|  * @clnt_login:	[in] Login class of client, TEE_IOCTL_LOGIN_* above | ||||
|  * @cancel_id:	[in] Cancellation id, a unique value to identify this request | ||||
|  * @session:	[out] Session id | ||||
|  * @ret:	[out] return value | ||||
|  * @ret_origin	[out] origin of the return value | ||||
|  * @num_params	[in] number of parameters following this struct | ||||
|  */ | ||||
| struct tee_ioctl_open_session_arg { | ||||
| 	__u8 uuid[TEE_IOCTL_UUID_LEN]; | ||||
| 	__u8 clnt_uuid[TEE_IOCTL_UUID_LEN]; | ||||
| 	__u32 clnt_login; | ||||
| 	__u32 cancel_id; | ||||
| 	__u32 session; | ||||
| 	__u32 ret; | ||||
| 	__u32 ret_origin; | ||||
| 	__u32 num_params; | ||||
| 	/* num_params tells the actual number of element in params */ | ||||
| 	struct tee_ioctl_param params[]; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_OPEN_SESSION - opens a session to a Trusted Application | ||||
|  * | ||||
|  * Takes a struct tee_ioctl_buf_data which contains a struct | ||||
|  * tee_ioctl_open_session_arg followed by any array of struct | ||||
|  * tee_ioctl_param | ||||
|  */ | ||||
| #define TEE_IOC_OPEN_SESSION	_IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 2, \ | ||||
| 				     struct tee_ioctl_buf_data) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_invoke_func_arg - Invokes a function in a Trusted | ||||
|  * Application | ||||
|  * @func:	[in] Trusted Application function, specific to the TA | ||||
|  * @session:	[in] Session id | ||||
|  * @cancel_id:	[in] Cancellation id, a unique value to identify this request | ||||
|  * @ret:	[out] return value | ||||
|  * @ret_origin	[out] origin of the return value | ||||
|  * @num_params	[in] number of parameters following this struct | ||||
|  */ | ||||
| struct tee_ioctl_invoke_arg { | ||||
| 	__u32 func; | ||||
| 	__u32 session; | ||||
| 	__u32 cancel_id; | ||||
| 	__u32 ret; | ||||
| 	__u32 ret_origin; | ||||
| 	__u32 num_params; | ||||
| 	/* num_params tells the actual number of element in params */ | ||||
| 	struct tee_ioctl_param params[]; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_INVOKE - Invokes a function in a Trusted Application | ||||
|  * | ||||
|  * Takes a struct tee_ioctl_buf_data which contains a struct | ||||
|  * tee_invoke_func_arg followed by any array of struct tee_param | ||||
|  */ | ||||
| #define TEE_IOC_INVOKE		_IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 3, \ | ||||
| 				     struct tee_ioctl_buf_data) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_cancel_arg - Cancels an open session or invoke ioctl | ||||
|  * @cancel_id:	[in] Cancellation id, a unique value to identify this request | ||||
|  * @session:	[in] Session id, if the session is opened, else set to 0 | ||||
|  */ | ||||
| struct tee_ioctl_cancel_arg { | ||||
| 	__u32 cancel_id; | ||||
| 	__u32 session; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_CANCEL - Cancels an open session or invoke | ||||
|  */ | ||||
| #define TEE_IOC_CANCEL		_IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 4, \ | ||||
| 				     struct tee_ioctl_cancel_arg) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_ioctl_close_session_arg - Closes an open session | ||||
|  * @session:	[in] Session id | ||||
|  */ | ||||
| struct tee_ioctl_close_session_arg { | ||||
| 	__u32 session; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_CLOSE_SESSION - Closes a session | ||||
|  */ | ||||
| #define TEE_IOC_CLOSE_SESSION	_IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 5, \ | ||||
| 				     struct tee_ioctl_close_session_arg) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_iocl_supp_recv_arg - Receive a request for a supplicant function | ||||
|  * @func:	[in] supplicant function | ||||
|  * @num_params	[in/out] number of parameters following this struct | ||||
|  * | ||||
|  * @num_params is the number of params that tee-supplicant has room to | ||||
|  * receive when input, @num_params is the number of actual params | ||||
|  * tee-supplicant receives when output. | ||||
|  */ | ||||
| struct tee_iocl_supp_recv_arg { | ||||
| 	__u32 func; | ||||
| 	__u32 num_params; | ||||
| 	/* num_params tells the actual number of element in params */ | ||||
| 	struct tee_ioctl_param params[]; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_SUPPL_RECV - Receive a request for a supplicant function | ||||
|  * | ||||
|  * Takes a struct tee_ioctl_buf_data which contains a struct | ||||
|  * tee_iocl_supp_recv_arg followed by any array of struct tee_param | ||||
|  */ | ||||
| #define TEE_IOC_SUPPL_RECV	_IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 6, \ | ||||
| 				     struct tee_ioctl_buf_data) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct tee_iocl_supp_send_arg - Send a response to a received request | ||||
|  * @ret:	[out] return value | ||||
|  * @num_params	[in] number of parameters following this struct | ||||
|  */ | ||||
| struct tee_iocl_supp_send_arg { | ||||
| 	__u32 ret; | ||||
| 	__u32 num_params; | ||||
| 	/* num_params tells the actual number of element in params */ | ||||
| 	struct tee_ioctl_param params[]; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * TEE_IOC_SUPPL_SEND - Receive a request for a supplicant function | ||||
|  * | ||||
|  * Takes a struct tee_ioctl_buf_data which contains a struct | ||||
|  * tee_iocl_supp_send_arg followed by any array of struct tee_param | ||||
|  */ | ||||
| #define TEE_IOC_SUPPL_SEND	_IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 7, \ | ||||
| 				     struct tee_ioctl_buf_data) | ||||
| 
 | ||||
| /*
 | ||||
|  * Five syscalls are used when communicating with the TEE driver. | ||||
|  * open(): opens the device associated with the driver | ||||
|  * ioctl(): as described above operating on the file descriptor from open() | ||||
|  * close(): two cases | ||||
|  *   - closes the device file descriptor | ||||
|  *   - closes a file descriptor connected to allocated shared memory | ||||
|  * mmap(): maps shared memory into user space using information from struct | ||||
|  *	   tee_ioctl_shm_alloc_data | ||||
|  * munmap(): unmaps previously shared memory | ||||
|  */ | ||||
| 
 | ||||
| #endif /*__TEE_H*/ | ||||
		Loading…
	
		Reference in a new issue
	
	 Jens Wiklander
						Jens Wiklander