forked from mirrors/linux
		
	tee: add OP-TEE driver
Adds a OP-TEE driver which also can be compiled as a loadable module. * Targets ARM and ARM64 * Supports using reserved memory from OP-TEE as shared memory * Probes OP-TEE version using SMCs * Accepts requests on privileged and unprivileged device * Uses OPTEE message protocol version 2 to communicate with secure world 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
							
								
									967c9cca2c
								
							
						
					
					
						commit
						4fb0a5eb36
					
				
					 12 changed files with 2814 additions and 0 deletions
				
			
		|  | @ -9369,6 +9369,11 @@ F:	arch/*/oprofile/ | ||||||
| F:	drivers/oprofile/ | F:	drivers/oprofile/ | ||||||
| F:	include/linux/oprofile.h | F:	include/linux/oprofile.h | ||||||
| 
 | 
 | ||||||
|  | OP-TEE DRIVER | ||||||
|  | M:	Jens Wiklander <jens.wiklander@linaro.org> | ||||||
|  | S:	Maintained | ||||||
|  | F:	drivers/tee/optee/ | ||||||
|  | 
 | ||||||
| ORACLE CLUSTER FILESYSTEM 2 (OCFS2) | ORACLE CLUSTER FILESYSTEM 2 (OCFS2) | ||||||
| M:	Mark Fasheh <mfasheh@versity.com> | M:	Mark Fasheh <mfasheh@versity.com> | ||||||
| M:	Joel Becker <jlbec@evilplan.org> | M:	Joel Becker <jlbec@evilplan.org> | ||||||
|  |  | ||||||
|  | @ -6,3 +6,13 @@ config TEE | ||||||
| 	help | 	help | ||||||
| 	  This implements a generic interface towards a Trusted Execution | 	  This implements a generic interface towards a Trusted Execution | ||||||
| 	  Environment (TEE). | 	  Environment (TEE). | ||||||
|  | 
 | ||||||
|  | if TEE | ||||||
|  | 
 | ||||||
|  | menu "TEE drivers" | ||||||
|  | 
 | ||||||
|  | source "drivers/tee/optee/Kconfig" | ||||||
|  | 
 | ||||||
|  | endmenu | ||||||
|  | 
 | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | @ -2,3 +2,4 @@ obj-$(CONFIG_TEE) += tee.o | ||||||
| tee-objs += tee_core.o | tee-objs += tee_core.o | ||||||
| tee-objs += tee_shm.o | tee-objs += tee_shm.o | ||||||
| tee-objs += tee_shm_pool.o | tee-objs += tee_shm_pool.o | ||||||
|  | obj-$(CONFIG_OPTEE) += optee/ | ||||||
|  |  | ||||||
							
								
								
									
										7
									
								
								drivers/tee/optee/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								drivers/tee/optee/Kconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | # OP-TEE Trusted Execution Environment Configuration | ||||||
|  | config OPTEE | ||||||
|  | 	tristate "OP-TEE" | ||||||
|  | 	depends on HAVE_ARM_SMCCC | ||||||
|  | 	help | ||||||
|  | 	  This implements the OP-TEE Trusted Execution Environment (TEE) | ||||||
|  | 	  driver. | ||||||
							
								
								
									
										5
									
								
								drivers/tee/optee/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								drivers/tee/optee/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | obj-$(CONFIG_OPTEE) += optee.o | ||||||
|  | optee-objs += core.o | ||||||
|  | optee-objs += call.o | ||||||
|  | optee-objs += rpc.o | ||||||
|  | optee-objs += supp.o | ||||||
							
								
								
									
										444
									
								
								drivers/tee/optee/call.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								drivers/tee/optee/call.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,444 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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/arm-smccc.h> | ||||||
|  | #include <linux/device.h> | ||||||
|  | #include <linux/err.h> | ||||||
|  | #include <linux/errno.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/tee_drv.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/uaccess.h> | ||||||
|  | #include "optee_private.h" | ||||||
|  | #include "optee_smc.h" | ||||||
|  | 
 | ||||||
|  | struct optee_call_waiter { | ||||||
|  | 	struct list_head list_node; | ||||||
|  | 	struct completion c; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void optee_cq_wait_init(struct optee_call_queue *cq, | ||||||
|  | 			       struct optee_call_waiter *w) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * We're preparing to make a call to secure world. In case we can't | ||||||
|  | 	 * allocate a thread in secure world we'll end up waiting in | ||||||
|  | 	 * optee_cq_wait_for_completion(). | ||||||
|  | 	 * | ||||||
|  | 	 * Normally if there's no contention in secure world the call will | ||||||
|  | 	 * complete and we can cleanup directly with optee_cq_wait_final(). | ||||||
|  | 	 */ | ||||||
|  | 	mutex_lock(&cq->mutex); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * We add ourselves to the queue, but we don't wait. This | ||||||
|  | 	 * guarantees that we don't lose a completion if secure world | ||||||
|  | 	 * returns busy and another thread just exited and try to complete | ||||||
|  | 	 * someone. | ||||||
|  | 	 */ | ||||||
|  | 	init_completion(&w->c); | ||||||
|  | 	list_add_tail(&w->list_node, &cq->waiters); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&cq->mutex); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void optee_cq_wait_for_completion(struct optee_call_queue *cq, | ||||||
|  | 					 struct optee_call_waiter *w) | ||||||
|  | { | ||||||
|  | 	wait_for_completion(&w->c); | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&cq->mutex); | ||||||
|  | 
 | ||||||
|  | 	/* Move to end of list to get out of the way for other waiters */ | ||||||
|  | 	list_del(&w->list_node); | ||||||
|  | 	reinit_completion(&w->c); | ||||||
|  | 	list_add_tail(&w->list_node, &cq->waiters); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&cq->mutex); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void optee_cq_complete_one(struct optee_call_queue *cq) | ||||||
|  | { | ||||||
|  | 	struct optee_call_waiter *w; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(w, &cq->waiters, list_node) { | ||||||
|  | 		if (!completion_done(&w->c)) { | ||||||
|  | 			complete(&w->c); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void optee_cq_wait_final(struct optee_call_queue *cq, | ||||||
|  | 				struct optee_call_waiter *w) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * We're done with the call to secure world. The thread in secure | ||||||
|  | 	 * world that was used for this call is now available for some | ||||||
|  | 	 * other task to use. | ||||||
|  | 	 */ | ||||||
|  | 	mutex_lock(&cq->mutex); | ||||||
|  | 
 | ||||||
|  | 	/* Get out of the list */ | ||||||
|  | 	list_del(&w->list_node); | ||||||
|  | 
 | ||||||
|  | 	/* Wake up one eventual waiting task */ | ||||||
|  | 	optee_cq_complete_one(cq); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * If we're completed we've got a completion from another task that | ||||||
|  | 	 * was just done with its call to secure world. Since yet another | ||||||
|  | 	 * thread now is available in secure world wake up another eventual | ||||||
|  | 	 * waiting task. | ||||||
|  | 	 */ | ||||||
|  | 	if (completion_done(&w->c)) | ||||||
|  | 		optee_cq_complete_one(cq); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&cq->mutex); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Requires the filpstate mutex to be held */ | ||||||
|  | static struct optee_session *find_session(struct optee_context_data *ctxdata, | ||||||
|  | 					  u32 session_id) | ||||||
|  | { | ||||||
|  | 	struct optee_session *sess; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(sess, &ctxdata->sess_list, list_node) | ||||||
|  | 		if (sess->session_id == session_id) | ||||||
|  | 			return sess; | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world | ||||||
|  |  * @ctx:	calling context | ||||||
|  |  * @parg:	physical address of message to pass to secure world | ||||||
|  |  * | ||||||
|  |  * Does and SMC to OP-TEE in secure world and handles eventual resulting | ||||||
|  |  * Remote Procedure Calls (RPC) from OP-TEE. | ||||||
|  |  * | ||||||
|  |  * Returns return code from secure world, 0 is OK | ||||||
|  |  */ | ||||||
|  | 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 = { }; | ||||||
|  | 	u32 ret; | ||||||
|  | 
 | ||||||
|  | 	param.a0 = OPTEE_SMC_CALL_WITH_ARG; | ||||||
|  | 	reg_pair_from_64(¶m.a1, ¶m.a2, parg); | ||||||
|  | 	/* Initialize waiter */ | ||||||
|  | 	optee_cq_wait_init(&optee->call_queue, &w); | ||||||
|  | 	while (true) { | ||||||
|  | 		struct arm_smccc_res res; | ||||||
|  | 
 | ||||||
|  | 		optee->invoke_fn(param.a0, param.a1, param.a2, param.a3, | ||||||
|  | 				 param.a4, param.a5, param.a6, param.a7, | ||||||
|  | 				 &res); | ||||||
|  | 
 | ||||||
|  | 		if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { | ||||||
|  | 			/*
 | ||||||
|  | 			 * Out of threads in secure world, wait for a thread | ||||||
|  | 			 * become available. | ||||||
|  | 			 */ | ||||||
|  | 			optee_cq_wait_for_completion(&optee->call_queue, &w); | ||||||
|  | 		} else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { | ||||||
|  | 			param.a0 = res.a0; | ||||||
|  | 			param.a1 = res.a1; | ||||||
|  | 			param.a2 = res.a2; | ||||||
|  | 			param.a3 = res.a3; | ||||||
|  | 			optee_handle_rpc(ctx, ¶m); | ||||||
|  | 		} else { | ||||||
|  | 			ret = res.a0; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * We're done with our thread in secure world, if there's any | ||||||
|  | 	 * thread waiters wake up one. | ||||||
|  | 	 */ | ||||||
|  | 	optee_cq_wait_final(&optee->call_queue, &w); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, | ||||||
|  | 				   struct optee_msg_arg **msg_arg, | ||||||
|  | 				   phys_addr_t *msg_parg) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	struct optee_msg_arg *ma; | ||||||
|  | 
 | ||||||
|  | 	shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params), | ||||||
|  | 			    TEE_SHM_MAPPED); | ||||||
|  | 	if (IS_ERR(shm)) | ||||||
|  | 		return shm; | ||||||
|  | 
 | ||||||
|  | 	ma = tee_shm_get_va(shm, 0); | ||||||
|  | 	if (IS_ERR(ma)) { | ||||||
|  | 		rc = PTR_ERR(ma); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rc = tee_shm_get_pa(shm, 0, msg_parg); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); | ||||||
|  | 	ma->num_params = num_params; | ||||||
|  | 	*msg_arg = ma; | ||||||
|  | out: | ||||||
|  | 	if (rc) { | ||||||
|  | 		tee_shm_free(shm); | ||||||
|  | 		return ERR_PTR(rc); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return shm; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int optee_open_session(struct tee_context *ctx, | ||||||
|  | 		       struct tee_ioctl_open_session_arg *arg, | ||||||
|  | 		       struct tee_param *param) | ||||||
|  | { | ||||||
|  | 	struct optee_context_data *ctxdata = ctx->data; | ||||||
|  | 	int rc; | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	struct optee_msg_arg *msg_arg; | ||||||
|  | 	phys_addr_t msg_parg; | ||||||
|  | 	struct optee_session *sess = NULL; | ||||||
|  | 
 | ||||||
|  | 	/* +2 for the meta parameters added below */ | ||||||
|  | 	shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg); | ||||||
|  | 	if (IS_ERR(shm)) | ||||||
|  | 		return PTR_ERR(shm); | ||||||
|  | 
 | ||||||
|  | 	msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; | ||||||
|  | 	msg_arg->cancel_id = arg->cancel_id; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Initialize and add the meta parameters needed when opening a | ||||||
|  | 	 * session. | ||||||
|  | 	 */ | ||||||
|  | 	msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | | ||||||
|  | 				  OPTEE_MSG_ATTR_META; | ||||||
|  | 	msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | | ||||||
|  | 				  OPTEE_MSG_ATTR_META; | ||||||
|  | 	memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid)); | ||||||
|  | 	memcpy(&msg_arg->params[1].u.value, arg->uuid, sizeof(arg->clnt_uuid)); | ||||||
|  | 	msg_arg->params[1].u.value.c = arg->clnt_login; | ||||||
|  | 
 | ||||||
|  | 	rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	sess = kzalloc(sizeof(*sess), GFP_KERNEL); | ||||||
|  | 	if (!sess) { | ||||||
|  | 		rc = -ENOMEM; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (optee_do_call_with_arg(ctx, msg_parg)) { | ||||||
|  | 		msg_arg->ret = TEEC_ERROR_COMMUNICATION; | ||||||
|  | 		msg_arg->ret_origin = TEEC_ORIGIN_COMMS; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (msg_arg->ret == TEEC_SUCCESS) { | ||||||
|  | 		/* A new session has been created, add it to the list. */ | ||||||
|  | 		sess->session_id = msg_arg->session; | ||||||
|  | 		mutex_lock(&ctxdata->mutex); | ||||||
|  | 		list_add(&sess->list_node, &ctxdata->sess_list); | ||||||
|  | 		mutex_unlock(&ctxdata->mutex); | ||||||
|  | 	} else { | ||||||
|  | 		kfree(sess); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (optee_from_msg_param(param, arg->num_params, msg_arg->params + 2)) { | ||||||
|  | 		arg->ret = TEEC_ERROR_COMMUNICATION; | ||||||
|  | 		arg->ret_origin = TEEC_ORIGIN_COMMS; | ||||||
|  | 		/* Close session again to avoid leakage */ | ||||||
|  | 		optee_close_session(ctx, msg_arg->session); | ||||||
|  | 	} else { | ||||||
|  | 		arg->session = msg_arg->session; | ||||||
|  | 		arg->ret = msg_arg->ret; | ||||||
|  | 		arg->ret_origin = msg_arg->ret_origin; | ||||||
|  | 	} | ||||||
|  | out: | ||||||
|  | 	tee_shm_free(shm); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int optee_close_session(struct tee_context *ctx, u32 session) | ||||||
|  | { | ||||||
|  | 	struct optee_context_data *ctxdata = ctx->data; | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	struct optee_msg_arg *msg_arg; | ||||||
|  | 	phys_addr_t msg_parg; | ||||||
|  | 	struct optee_session *sess; | ||||||
|  | 
 | ||||||
|  | 	/* Check that the session is valid and remove it from the list */ | ||||||
|  | 	mutex_lock(&ctxdata->mutex); | ||||||
|  | 	sess = find_session(ctxdata, session); | ||||||
|  | 	if (sess) | ||||||
|  | 		list_del(&sess->list_node); | ||||||
|  | 	mutex_unlock(&ctxdata->mutex); | ||||||
|  | 	if (!sess) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	kfree(sess); | ||||||
|  | 
 | ||||||
|  | 	shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); | ||||||
|  | 	if (IS_ERR(shm)) | ||||||
|  | 		return PTR_ERR(shm); | ||||||
|  | 
 | ||||||
|  | 	msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; | ||||||
|  | 	msg_arg->session = session; | ||||||
|  | 	optee_do_call_with_arg(ctx, msg_parg); | ||||||
|  | 
 | ||||||
|  | 	tee_shm_free(shm); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, | ||||||
|  | 		      struct tee_param *param) | ||||||
|  | { | ||||||
|  | 	struct optee_context_data *ctxdata = ctx->data; | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	struct optee_msg_arg *msg_arg; | ||||||
|  | 	phys_addr_t msg_parg; | ||||||
|  | 	struct optee_session *sess; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	/* Check that the session is valid */ | ||||||
|  | 	mutex_lock(&ctxdata->mutex); | ||||||
|  | 	sess = find_session(ctxdata, arg->session); | ||||||
|  | 	mutex_unlock(&ctxdata->mutex); | ||||||
|  | 	if (!sess) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	shm = get_msg_arg(ctx, arg->num_params, &msg_arg, &msg_parg); | ||||||
|  | 	if (IS_ERR(shm)) | ||||||
|  | 		return PTR_ERR(shm); | ||||||
|  | 	msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; | ||||||
|  | 	msg_arg->func = arg->func; | ||||||
|  | 	msg_arg->session = arg->session; | ||||||
|  | 	msg_arg->cancel_id = arg->cancel_id; | ||||||
|  | 
 | ||||||
|  | 	rc = optee_to_msg_param(msg_arg->params, arg->num_params, param); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	if (optee_do_call_with_arg(ctx, msg_parg)) { | ||||||
|  | 		msg_arg->ret = TEEC_ERROR_COMMUNICATION; | ||||||
|  | 		msg_arg->ret_origin = TEEC_ORIGIN_COMMS; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (optee_from_msg_param(param, arg->num_params, msg_arg->params)) { | ||||||
|  | 		msg_arg->ret = TEEC_ERROR_COMMUNICATION; | ||||||
|  | 		msg_arg->ret_origin = TEEC_ORIGIN_COMMS; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	arg->ret = msg_arg->ret; | ||||||
|  | 	arg->ret_origin = msg_arg->ret_origin; | ||||||
|  | out: | ||||||
|  | 	tee_shm_free(shm); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) | ||||||
|  | { | ||||||
|  | 	struct optee_context_data *ctxdata = ctx->data; | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	struct optee_msg_arg *msg_arg; | ||||||
|  | 	phys_addr_t msg_parg; | ||||||
|  | 	struct optee_session *sess; | ||||||
|  | 
 | ||||||
|  | 	/* Check that the session is valid */ | ||||||
|  | 	mutex_lock(&ctxdata->mutex); | ||||||
|  | 	sess = find_session(ctxdata, session); | ||||||
|  | 	mutex_unlock(&ctxdata->mutex); | ||||||
|  | 	if (!sess) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); | ||||||
|  | 	if (IS_ERR(shm)) | ||||||
|  | 		return PTR_ERR(shm); | ||||||
|  | 
 | ||||||
|  | 	msg_arg->cmd = OPTEE_MSG_CMD_CANCEL; | ||||||
|  | 	msg_arg->session = session; | ||||||
|  | 	msg_arg->cancel_id = cancel_id; | ||||||
|  | 	optee_do_call_with_arg(ctx, msg_parg); | ||||||
|  | 
 | ||||||
|  | 	tee_shm_free(shm); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_enable_shm_cache() - Enables caching of some shared memory allocation | ||||||
|  |  *			      in OP-TEE | ||||||
|  |  * @optee:	main service struct | ||||||
|  |  */ | ||||||
|  | void optee_enable_shm_cache(struct optee *optee) | ||||||
|  | { | ||||||
|  | 	struct optee_call_waiter w; | ||||||
|  | 
 | ||||||
|  | 	/* We need to retry until secure world isn't busy. */ | ||||||
|  | 	optee_cq_wait_init(&optee->call_queue, &w); | ||||||
|  | 	while (true) { | ||||||
|  | 		struct arm_smccc_res res; | ||||||
|  | 
 | ||||||
|  | 		optee->invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				 0, &res); | ||||||
|  | 		if (res.a0 == OPTEE_SMC_RETURN_OK) | ||||||
|  | 			break; | ||||||
|  | 		optee_cq_wait_for_completion(&optee->call_queue, &w); | ||||||
|  | 	} | ||||||
|  | 	optee_cq_wait_final(&optee->call_queue, &w); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_disable_shm_cache() - Disables caching of some shared memory allocation | ||||||
|  |  *			      in OP-TEE | ||||||
|  |  * @optee:	main service struct | ||||||
|  |  */ | ||||||
|  | void optee_disable_shm_cache(struct optee *optee) | ||||||
|  | { | ||||||
|  | 	struct optee_call_waiter w; | ||||||
|  | 
 | ||||||
|  | 	/* We need to retry until secure world isn't busy. */ | ||||||
|  | 	optee_cq_wait_init(&optee->call_queue, &w); | ||||||
|  | 	while (true) { | ||||||
|  | 		union { | ||||||
|  | 			struct arm_smccc_res smccc; | ||||||
|  | 			struct optee_smc_disable_shm_cache_result result; | ||||||
|  | 		} res; | ||||||
|  | 
 | ||||||
|  | 		optee->invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				 0, &res.smccc); | ||||||
|  | 		if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) | ||||||
|  | 			break; /* All shm's freed */ | ||||||
|  | 		if (res.result.status == OPTEE_SMC_RETURN_OK) { | ||||||
|  | 			struct tee_shm *shm; | ||||||
|  | 
 | ||||||
|  | 			shm = reg_pair_to_ptr(res.result.shm_upper32, | ||||||
|  | 					      res.result.shm_lower32); | ||||||
|  | 			tee_shm_free(shm); | ||||||
|  | 		} else { | ||||||
|  | 			optee_cq_wait_for_completion(&optee->call_queue, &w); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	optee_cq_wait_final(&optee->call_queue, &w); | ||||||
|  | } | ||||||
							
								
								
									
										622
									
								
								drivers/tee/optee/core.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										622
									
								
								drivers/tee/optee/core.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,622 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||||
|  | 
 | ||||||
|  | #include <linux/arm-smccc.h> | ||||||
|  | #include <linux/errno.h> | ||||||
|  | #include <linux/io.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/of.h> | ||||||
|  | #include <linux/of_platform.h> | ||||||
|  | #include <linux/platform_device.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/string.h> | ||||||
|  | #include <linux/tee_drv.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/uaccess.h> | ||||||
|  | #include "optee_private.h" | ||||||
|  | #include "optee_smc.h" | ||||||
|  | 
 | ||||||
|  | #define DRIVER_NAME "optee" | ||||||
|  | 
 | ||||||
|  | #define OPTEE_SHM_NUM_PRIV_PAGES	1 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_from_msg_param() - convert from OPTEE_MSG parameters to | ||||||
|  |  *			    struct tee_param | ||||||
|  |  * @params:	subsystem internal parameter representation | ||||||
|  |  * @num_params:	number of elements in the parameter arrays | ||||||
|  |  * @msg_params:	OPTEE_MSG parameters | ||||||
|  |  * Returns 0 on success or <0 on failure | ||||||
|  |  */ | ||||||
|  | int optee_from_msg_param(struct tee_param *params, size_t num_params, | ||||||
|  | 			 const struct optee_msg_param *msg_params) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 	size_t n; | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	phys_addr_t pa; | ||||||
|  | 
 | ||||||
|  | 	for (n = 0; n < num_params; n++) { | ||||||
|  | 		struct tee_param *p = params + n; | ||||||
|  | 		const struct optee_msg_param *mp = msg_params + n; | ||||||
|  | 		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; | ||||||
|  | 
 | ||||||
|  | 		switch (attr) { | ||||||
|  | 		case OPTEE_MSG_ATTR_TYPE_NONE: | ||||||
|  | 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; | ||||||
|  | 			memset(&p->u, 0, sizeof(p->u)); | ||||||
|  | 			break; | ||||||
|  | 		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: | ||||||
|  | 		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: | ||||||
|  | 		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: | ||||||
|  | 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + | ||||||
|  | 				  attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; | ||||||
|  | 			p->u.value.a = mp->u.value.a; | ||||||
|  | 			p->u.value.b = mp->u.value.b; | ||||||
|  | 			p->u.value.c = mp->u.value.c; | ||||||
|  | 			break; | ||||||
|  | 		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: | ||||||
|  | 		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: | ||||||
|  | 		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: | ||||||
|  | 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + | ||||||
|  | 				  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; | ||||||
|  | 			p->u.memref.size = mp->u.tmem.size; | ||||||
|  | 			shm = (struct tee_shm *)(unsigned long) | ||||||
|  | 				mp->u.tmem.shm_ref; | ||||||
|  | 			if (!shm) { | ||||||
|  | 				p->u.memref.shm_offs = 0; | ||||||
|  | 				p->u.memref.shm = NULL; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 			rc = tee_shm_get_pa(shm, 0, &pa); | ||||||
|  | 			if (rc) | ||||||
|  | 				return rc; | ||||||
|  | 			p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; | ||||||
|  | 			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 - 1; | ||||||
|  | 
 | ||||||
|  | 				rc = tee_shm_get_pa(shm, o, NULL); | ||||||
|  | 				if (rc) | ||||||
|  | 					return rc; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			return -EINVAL; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters | ||||||
|  |  * @msg_params:	OPTEE_MSG parameters | ||||||
|  |  * @num_params:	number of elements in the parameter arrays | ||||||
|  |  * @params:	subsystem itnernal parameter representation | ||||||
|  |  * Returns 0 on success or <0 on failure | ||||||
|  |  */ | ||||||
|  | int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, | ||||||
|  | 		       const struct tee_param *params) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 	size_t n; | ||||||
|  | 	phys_addr_t pa; | ||||||
|  | 
 | ||||||
|  | 	for (n = 0; n < num_params; n++) { | ||||||
|  | 		const struct tee_param *p = params + n; | ||||||
|  | 		struct optee_msg_param *mp = msg_params + n; | ||||||
|  | 
 | ||||||
|  | 		switch (p->attr) { | ||||||
|  | 		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: | ||||||
|  | 			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; | ||||||
|  | 			memset(&mp->u, 0, sizeof(mp->u)); | ||||||
|  | 			break; | ||||||
|  | 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: | ||||||
|  | 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: | ||||||
|  | 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: | ||||||
|  | 			mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - | ||||||
|  | 				   TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; | ||||||
|  | 			mp->u.value.a = p->u.value.a; | ||||||
|  | 			mp->u.value.b = p->u.value.b; | ||||||
|  | 			mp->u.value.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: | ||||||
|  | 			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 (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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void optee_get_version(struct tee_device *teedev, | ||||||
|  | 			      struct tee_ioctl_version_data *vers) | ||||||
|  | { | ||||||
|  | 	struct tee_ioctl_version_data v = { | ||||||
|  | 		.impl_id = TEE_IMPL_ID_OPTEE, | ||||||
|  | 		.impl_caps = TEE_OPTEE_CAP_TZ, | ||||||
|  | 		.gen_caps = TEE_GEN_CAP_GP, | ||||||
|  | 	}; | ||||||
|  | 	*vers = v; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int optee_open(struct tee_context *ctx) | ||||||
|  | { | ||||||
|  | 	struct optee_context_data *ctxdata; | ||||||
|  | 	struct tee_device *teedev = ctx->teedev; | ||||||
|  | 	struct optee *optee = tee_get_drvdata(teedev); | ||||||
|  | 
 | ||||||
|  | 	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); | ||||||
|  | 	if (!ctxdata) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	if (teedev == optee->supp_teedev) { | ||||||
|  | 		bool busy = true; | ||||||
|  | 
 | ||||||
|  | 		mutex_lock(&optee->supp.ctx_mutex); | ||||||
|  | 		if (!optee->supp.ctx) { | ||||||
|  | 			busy = false; | ||||||
|  | 			optee->supp.ctx = ctx; | ||||||
|  | 		} | ||||||
|  | 		mutex_unlock(&optee->supp.ctx_mutex); | ||||||
|  | 		if (busy) { | ||||||
|  | 			kfree(ctxdata); | ||||||
|  | 			return -EBUSY; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mutex_init(&ctxdata->mutex); | ||||||
|  | 	INIT_LIST_HEAD(&ctxdata->sess_list); | ||||||
|  | 
 | ||||||
|  | 	ctx->data = ctxdata; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void optee_release(struct tee_context *ctx) | ||||||
|  | { | ||||||
|  | 	struct optee_context_data *ctxdata = ctx->data; | ||||||
|  | 	struct tee_device *teedev = ctx->teedev; | ||||||
|  | 	struct optee *optee = tee_get_drvdata(teedev); | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	struct optee_msg_arg *arg = NULL; | ||||||
|  | 	phys_addr_t parg; | ||||||
|  | 	struct optee_session *sess; | ||||||
|  | 	struct optee_session *sess_tmp; | ||||||
|  | 
 | ||||||
|  | 	if (!ctxdata) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED); | ||||||
|  | 	if (!IS_ERR(shm)) { | ||||||
|  | 		arg = tee_shm_get_va(shm, 0); | ||||||
|  | 		/*
 | ||||||
|  | 		 * If va2pa fails for some reason, we can't call | ||||||
|  | 		 * optee_close_session(), only free the memory. Secure OS | ||||||
|  | 		 * will leak sessions and finally refuse more sessions, but | ||||||
|  | 		 * we will at least let normal world reclaim its memory. | ||||||
|  | 		 */ | ||||||
|  | 		if (!IS_ERR(arg)) | ||||||
|  | 			tee_shm_va2pa(shm, arg, &parg); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, | ||||||
|  | 				 list_node) { | ||||||
|  | 		list_del(&sess->list_node); | ||||||
|  | 		if (!IS_ERR_OR_NULL(arg)) { | ||||||
|  | 			memset(arg, 0, sizeof(*arg)); | ||||||
|  | 			arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; | ||||||
|  | 			arg->session = sess->session_id; | ||||||
|  | 			optee_do_call_with_arg(ctx, parg); | ||||||
|  | 		} | ||||||
|  | 		kfree(sess); | ||||||
|  | 	} | ||||||
|  | 	kfree(ctxdata); | ||||||
|  | 
 | ||||||
|  | 	if (!IS_ERR(shm)) | ||||||
|  | 		tee_shm_free(shm); | ||||||
|  | 
 | ||||||
|  | 	ctx->data = NULL; | ||||||
|  | 
 | ||||||
|  | 	if (teedev == optee->supp_teedev) { | ||||||
|  | 		mutex_lock(&optee->supp.ctx_mutex); | ||||||
|  | 		optee->supp.ctx = NULL; | ||||||
|  | 		mutex_unlock(&optee->supp.ctx_mutex); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct tee_driver_ops optee_ops = { | ||||||
|  | 	.get_version = optee_get_version, | ||||||
|  | 	.open = optee_open, | ||||||
|  | 	.release = optee_release, | ||||||
|  | 	.open_session = optee_open_session, | ||||||
|  | 	.close_session = optee_close_session, | ||||||
|  | 	.invoke_func = optee_invoke_func, | ||||||
|  | 	.cancel_req = optee_cancel_req, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct tee_desc optee_desc = { | ||||||
|  | 	.name = DRIVER_NAME "-clnt", | ||||||
|  | 	.ops = &optee_ops, | ||||||
|  | 	.owner = THIS_MODULE, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct tee_driver_ops optee_supp_ops = { | ||||||
|  | 	.get_version = optee_get_version, | ||||||
|  | 	.open = optee_open, | ||||||
|  | 	.release = optee_release, | ||||||
|  | 	.supp_recv = optee_supp_recv, | ||||||
|  | 	.supp_send = optee_supp_send, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct tee_desc optee_supp_desc = { | ||||||
|  | 	.name = DRIVER_NAME "-supp", | ||||||
|  | 	.ops = &optee_supp_ops, | ||||||
|  | 	.owner = THIS_MODULE, | ||||||
|  | 	.flags = TEE_DESC_PRIVILEGED, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) | ||||||
|  | { | ||||||
|  | 	struct arm_smccc_res res; | ||||||
|  | 
 | ||||||
|  | 	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); | ||||||
|  | 
 | ||||||
|  | 	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && | ||||||
|  | 	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) | ||||||
|  | 		return true; | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) | ||||||
|  | { | ||||||
|  | 	union { | ||||||
|  | 		struct arm_smccc_res smccc; | ||||||
|  | 		struct optee_smc_calls_revision_result result; | ||||||
|  | 	} res; | ||||||
|  | 
 | ||||||
|  | 	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); | ||||||
|  | 
 | ||||||
|  | 	if (res.result.major == OPTEE_MSG_REVISION_MAJOR && | ||||||
|  | 	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) | ||||||
|  | 		return true; | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, | ||||||
|  | 					    u32 *sec_caps) | ||||||
|  | { | ||||||
|  | 	union { | ||||||
|  | 		struct arm_smccc_res smccc; | ||||||
|  | 		struct optee_smc_exchange_capabilities_result result; | ||||||
|  | 	} res; | ||||||
|  | 	u32 a1 = 0; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * TODO This isn't enough to tell if it's UP system (from kernel | ||||||
|  | 	 * point of view) or not, is_smp() returns the the information | ||||||
|  | 	 * needed, but can't be called directly from here. | ||||||
|  | 	 */ | ||||||
|  | 	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) | ||||||
|  | 		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; | ||||||
|  | 
 | ||||||
|  | 	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		  &res.smccc); | ||||||
|  | 
 | ||||||
|  | 	if (res.result.status != OPTEE_SMC_RETURN_OK) | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
|  | 	*sec_caps = res.result.capabilities; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct tee_shm_pool * | ||||||
|  | optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) | ||||||
|  | { | ||||||
|  | 	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; | ||||||
|  | 
 | ||||||
|  | 	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); | ||||||
|  | 	if (res.result.status != OPTEE_SMC_RETURN_OK) { | ||||||
|  | 		pr_info("shm service not available\n"); | ||||||
|  | 		return ERR_PTR(-ENOENT); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (res.result.settings != OPTEE_SMC_SHM_CACHED) { | ||||||
|  | 		pr_err("only normal cached shared memory supported\n"); | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	begin = roundup(res.result.start, PAGE_SIZE); | ||||||
|  | 	end = rounddown(res.result.start + res.result.size, PAGE_SIZE); | ||||||
|  | 	paddr = begin; | ||||||
|  | 	size = end - begin; | ||||||
|  | 
 | ||||||
|  | 	if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { | ||||||
|  | 		pr_err("too small shared memory area\n"); | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	va = memremap(paddr, size, MEMREMAP_WB); | ||||||
|  | 	if (!va) { | ||||||
|  | 		pr_err("shared memory ioremap failed\n"); | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 	} | ||||||
|  | 	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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	*memremaped_shm = va; | ||||||
|  | out: | ||||||
|  | 	return pool; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Simple wrapper functions to be able to use a function pointer */ | ||||||
|  | static void optee_smccc_smc(unsigned long a0, unsigned long a1, | ||||||
|  | 			    unsigned long a2, unsigned long a3, | ||||||
|  | 			    unsigned long a4, unsigned long a5, | ||||||
|  | 			    unsigned long a6, unsigned long a7, | ||||||
|  | 			    struct arm_smccc_res *res) | ||||||
|  | { | ||||||
|  | 	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void optee_smccc_hvc(unsigned long a0, unsigned long a1, | ||||||
|  | 			    unsigned long a2, unsigned long a3, | ||||||
|  | 			    unsigned long a4, unsigned long a5, | ||||||
|  | 			    unsigned long a6, unsigned long a7, | ||||||
|  | 			    struct arm_smccc_res *res) | ||||||
|  | { | ||||||
|  | 	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static optee_invoke_fn *get_invoke_func(struct device_node *np) | ||||||
|  | { | ||||||
|  | 	const char *method; | ||||||
|  | 
 | ||||||
|  | 	pr_info("probing for conduit method from DT.\n"); | ||||||
|  | 
 | ||||||
|  | 	if (of_property_read_string(np, "method", &method)) { | ||||||
|  | 		pr_warn("missing \"method\" property\n"); | ||||||
|  | 		return ERR_PTR(-ENXIO); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!strcmp("hvc", method)) | ||||||
|  | 		return optee_smccc_hvc; | ||||||
|  | 	else if (!strcmp("smc", method)) | ||||||
|  | 		return optee_smccc_smc; | ||||||
|  | 
 | ||||||
|  | 	pr_warn("invalid \"method\" property: %s\n", method); | ||||||
|  | 	return ERR_PTR(-EINVAL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct optee *optee_probe(struct device_node *np) | ||||||
|  | { | ||||||
|  | 	optee_invoke_fn *invoke_fn; | ||||||
|  | 	struct tee_shm_pool *pool; | ||||||
|  | 	struct optee *optee = NULL; | ||||||
|  | 	void *memremaped_shm = NULL; | ||||||
|  | 	struct tee_device *teedev; | ||||||
|  | 	u32 sec_caps; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	invoke_fn = get_invoke_func(np); | ||||||
|  | 	if (IS_ERR(invoke_fn)) | ||||||
|  | 		return (void *)invoke_fn; | ||||||
|  | 
 | ||||||
|  | 	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { | ||||||
|  | 		pr_warn("api uid mismatch\n"); | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!optee_msg_api_revision_is_compatible(invoke_fn)) { | ||||||
|  | 		pr_warn("api revision mismatch\n"); | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { | ||||||
|  | 		pr_warn("capabilities mismatch\n"); | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * We have no other option for shared memory, if secure world | ||||||
|  | 	 * doesn't have any reserved memory we can use we can't continue. | ||||||
|  | 	 */ | ||||||
|  | 	if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 
 | ||||||
|  | 	pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); | ||||||
|  | 	if (IS_ERR(pool)) | ||||||
|  | 		return (void *)pool; | ||||||
|  | 
 | ||||||
|  | 	optee = kzalloc(sizeof(*optee), GFP_KERNEL); | ||||||
|  | 	if (!optee) { | ||||||
|  | 		rc = -ENOMEM; | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	optee->invoke_fn = invoke_fn; | ||||||
|  | 
 | ||||||
|  | 	teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); | ||||||
|  | 	if (IS_ERR(teedev)) { | ||||||
|  | 		rc = PTR_ERR(teedev); | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 	optee->teedev = teedev; | ||||||
|  | 
 | ||||||
|  | 	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); | ||||||
|  | 	if (IS_ERR(teedev)) { | ||||||
|  | 		rc = PTR_ERR(teedev); | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 	optee->supp_teedev = teedev; | ||||||
|  | 
 | ||||||
|  | 	rc = tee_device_register(optee->teedev); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	rc = tee_device_register(optee->supp_teedev); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	mutex_init(&optee->call_queue.mutex); | ||||||
|  | 	INIT_LIST_HEAD(&optee->call_queue.waiters); | ||||||
|  | 	optee_wait_queue_init(&optee->wait_queue); | ||||||
|  | 	optee_supp_init(&optee->supp); | ||||||
|  | 	optee->memremaped_shm = memremaped_shm; | ||||||
|  | 	optee->pool = pool; | ||||||
|  | 
 | ||||||
|  | 	optee_enable_shm_cache(optee); | ||||||
|  | 
 | ||||||
|  | 	pr_info("initialized driver\n"); | ||||||
|  | 	return optee; | ||||||
|  | err: | ||||||
|  | 	if (optee) { | ||||||
|  | 		/*
 | ||||||
|  | 		 * tee_device_unregister() is safe to call even if the | ||||||
|  | 		 * devices hasn't been registered with | ||||||
|  | 		 * tee_device_register() yet. | ||||||
|  | 		 */ | ||||||
|  | 		tee_device_unregister(optee->supp_teedev); | ||||||
|  | 		tee_device_unregister(optee->teedev); | ||||||
|  | 		kfree(optee); | ||||||
|  | 	} | ||||||
|  | 	if (pool) | ||||||
|  | 		tee_shm_pool_free(pool); | ||||||
|  | 	if (memremaped_shm) | ||||||
|  | 		memunmap(memremaped_shm); | ||||||
|  | 	return ERR_PTR(rc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void optee_remove(struct optee *optee) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * Ask OP-TEE to free all cached shared memory objects to decrease | ||||||
|  | 	 * reference counters and also avoid wild pointers in secure world | ||||||
|  | 	 * into the old shared memory range. | ||||||
|  | 	 */ | ||||||
|  | 	optee_disable_shm_cache(optee); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * The two devices has to be unregistered before we can free the | ||||||
|  | 	 * other resources. | ||||||
|  | 	 */ | ||||||
|  | 	tee_device_unregister(optee->supp_teedev); | ||||||
|  | 	tee_device_unregister(optee->teedev); | ||||||
|  | 
 | ||||||
|  | 	tee_shm_pool_free(optee->pool); | ||||||
|  | 	if (optee->memremaped_shm) | ||||||
|  | 		memunmap(optee->memremaped_shm); | ||||||
|  | 	optee_wait_queue_exit(&optee->wait_queue); | ||||||
|  | 	optee_supp_uninit(&optee->supp); | ||||||
|  | 	mutex_destroy(&optee->call_queue.mutex); | ||||||
|  | 
 | ||||||
|  | 	kfree(optee); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct of_device_id optee_match[] = { | ||||||
|  | 	{ .compatible = "linaro,optee-tz" }, | ||||||
|  | 	{}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct optee *optee_svc; | ||||||
|  | 
 | ||||||
|  | static int __init optee_driver_init(void) | ||||||
|  | { | ||||||
|  | 	struct device_node *fw_np; | ||||||
|  | 	struct device_node *np; | ||||||
|  | 	struct optee *optee; | ||||||
|  | 
 | ||||||
|  | 	/* Node is supposed to be below /firmware */ | ||||||
|  | 	fw_np = of_find_node_by_name(NULL, "firmware"); | ||||||
|  | 	if (!fw_np) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	np = of_find_matching_node(fw_np, optee_match); | ||||||
|  | 	of_node_put(fw_np); | ||||||
|  | 	if (!np) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	optee = optee_probe(np); | ||||||
|  | 	of_node_put(np); | ||||||
|  | 
 | ||||||
|  | 	if (IS_ERR(optee)) | ||||||
|  | 		return PTR_ERR(optee); | ||||||
|  | 
 | ||||||
|  | 	optee_svc = optee; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | module_init(optee_driver_init); | ||||||
|  | 
 | ||||||
|  | static void __exit optee_driver_exit(void) | ||||||
|  | { | ||||||
|  | 	struct optee *optee = optee_svc; | ||||||
|  | 
 | ||||||
|  | 	optee_svc = NULL; | ||||||
|  | 	if (optee) | ||||||
|  | 		optee_remove(optee); | ||||||
|  | } | ||||||
|  | module_exit(optee_driver_exit); | ||||||
|  | 
 | ||||||
|  | MODULE_AUTHOR("Linaro"); | ||||||
|  | MODULE_DESCRIPTION("OP-TEE driver"); | ||||||
|  | MODULE_SUPPORTED_DEVICE(""); | ||||||
|  | MODULE_VERSION("1.0"); | ||||||
|  | MODULE_LICENSE("GPL v2"); | ||||||
							
								
								
									
										418
									
								
								drivers/tee/optee/optee_msg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								drivers/tee/optee/optee_msg.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,418 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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 _OPTEE_MSG_H | ||||||
|  | #define _OPTEE_MSG_H | ||||||
|  | 
 | ||||||
|  | #include <linux/bitops.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This file defines the OP-TEE message protocol used to communicate | ||||||
|  |  * with an instance of OP-TEE running in secure world. | ||||||
|  |  * | ||||||
|  |  * This file is divided into three sections. | ||||||
|  |  * 1. Formatting of messages. | ||||||
|  |  * 2. Requests from normal world | ||||||
|  |  * 3. Requests from secure world, Remote Procedure Call (RPC), handled by | ||||||
|  |  *    tee-supplicant. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*****************************************************************************
 | ||||||
|  |  * Part 1 - formatting of messages | ||||||
|  |  *****************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_NONE		0x0 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_VALUE_INPUT		0x1 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT	0x2 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_VALUE_INOUT		0x3 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT		0x5 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT		0x6 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT		0x7 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT		0x9 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT		0xa | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT		0xb | ||||||
|  | 
 | ||||||
|  | #define OPTEE_MSG_ATTR_TYPE_MASK		GENMASK(7, 0) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Meta parameter to be absorbed by the Secure OS and not passed | ||||||
|  |  * to the Trusted Application. | ||||||
|  |  * | ||||||
|  |  * Currently only used with OPTEE_MSG_CMD_OPEN_SESSION. | ||||||
|  |  */ | ||||||
|  | #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. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_ATTR_FRAGMENT			BIT(9) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Memory attributes for caching passed with temp memrefs. The actual value | ||||||
|  |  * used is defined outside the message protocol with the exception of | ||||||
|  |  * OPTEE_MSG_ATTR_CACHE_PREDEFINED which means the attributes already | ||||||
|  |  * defined for the memory range should be used. If optee_smc.h is used as | ||||||
|  |  * bearer of this protocol OPTEE_SMC_SHM_* is used for values. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_ATTR_CACHE_SHIFT		16 | ||||||
|  | #define OPTEE_MSG_ATTR_CACHE_MASK		GENMASK(2, 0) | ||||||
|  | #define OPTEE_MSG_ATTR_CACHE_PREDEFINED		0 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Same values as TEE_LOGIN_* from TEE Internal API | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_LOGIN_PUBLIC			0x00000000 | ||||||
|  | #define OPTEE_MSG_LOGIN_USER			0x00000001 | ||||||
|  | #define OPTEE_MSG_LOGIN_GROUP			0x00000002 | ||||||
|  | #define OPTEE_MSG_LOGIN_APPLICATION		0x00000004 | ||||||
|  | #define OPTEE_MSG_LOGIN_APPLICATION_USER	0x00000005 | ||||||
|  | #define OPTEE_MSG_LOGIN_APPLICATION_GROUP	0x00000006 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct optee_msg_param_tmem - temporary memory reference parameter | ||||||
|  |  * @buf_ptr:	Address of the buffer | ||||||
|  |  * @size:	Size of the buffer | ||||||
|  |  * @shm_ref:	Temporary shared memory reference, pointer to a struct tee_shm | ||||||
|  |  * | ||||||
|  |  * Secure and normal world communicates pointers as physical address | ||||||
|  |  * instead of the virtual address. This is because secure and normal world | ||||||
|  |  * have completely independent memory mapping. Normal world can even have a | ||||||
|  |  * hypervisor which need to translate the guest physical address (AKA IPA | ||||||
|  |  * in ARM documentation) to a real physical address before passing the | ||||||
|  |  * structure to secure world. | ||||||
|  |  */ | ||||||
|  | struct optee_msg_param_tmem { | ||||||
|  | 	u64 buf_ptr; | ||||||
|  | 	u64 size; | ||||||
|  | 	u64 shm_ref; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct optee_msg_param_rmem - registered memory reference parameter | ||||||
|  |  * @offs:	Offset into shared memory reference | ||||||
|  |  * @size:	Size of the buffer | ||||||
|  |  * @shm_ref:	Shared memory reference, pointer to a struct tee_shm | ||||||
|  |  */ | ||||||
|  | struct optee_msg_param_rmem { | ||||||
|  | 	u64 offs; | ||||||
|  | 	u64 size; | ||||||
|  | 	u64 shm_ref; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct optee_msg_param_value - opaque value parameter | ||||||
|  |  * | ||||||
|  |  * Value parameters are passed unchecked between normal and secure world. | ||||||
|  |  */ | ||||||
|  | struct optee_msg_param_value { | ||||||
|  | 	u64 a; | ||||||
|  | 	u64 b; | ||||||
|  | 	u64 c; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct optee_msg_param - parameter used together with struct optee_msg_arg | ||||||
|  |  * @attr:	attributes | ||||||
|  |  * @tmem:	parameter by temporary memory reference | ||||||
|  |  * @rmem:	parameter by registered memory reference | ||||||
|  |  * @value:	parameter by opaque 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_NONE indicates that none of the members are used. | ||||||
|  |  */ | ||||||
|  | struct optee_msg_param { | ||||||
|  | 	u64 attr; | ||||||
|  | 	union { | ||||||
|  | 		struct optee_msg_param_tmem tmem; | ||||||
|  | 		struct optee_msg_param_rmem rmem; | ||||||
|  | 		struct optee_msg_param_value value; | ||||||
|  | 	} u; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct optee_msg_arg - call argument | ||||||
|  |  * @cmd: Command, one of OPTEE_MSG_CMD_* or OPTEE_MSG_RPC_CMD_* | ||||||
|  |  * @func: Trusted Application function, specific to the Trusted Application, | ||||||
|  |  *	     used if cmd == OPTEE_MSG_CMD_INVOKE_COMMAND | ||||||
|  |  * @session: In parameter for all OPTEE_MSG_CMD_* except | ||||||
|  |  *	     OPTEE_MSG_CMD_OPEN_SESSION where it's an output parameter instead | ||||||
|  |  * @cancel_id: Cancellation id, a unique value to identify this request | ||||||
|  |  * @ret: return value | ||||||
|  |  * @ret_origin: origin of the return value | ||||||
|  |  * @num_params: number of parameters supplied to the OS Command | ||||||
|  |  * @params: the parameters supplied to the OS Command | ||||||
|  |  * | ||||||
|  |  * All normal calls to Trusted OS uses this struct. If cmd requires further | ||||||
|  |  * information than what these field holds it can be passed as a parameter | ||||||
|  |  * tagged as meta (setting the OPTEE_MSG_ATTR_META bit in corresponding | ||||||
|  |  * attrs field). All parameters tagged as meta has to come first. | ||||||
|  |  * | ||||||
|  |  * Temp memref parameters can be fragmented if supported by the Trusted OS | ||||||
|  |  * (when optee_smc.h is bearer of this protocol this is indicated with | ||||||
|  |  * OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM). If a logical memref parameter is | ||||||
|  |  * fragmented then has all but the last fragment the | ||||||
|  |  * OPTEE_MSG_ATTR_FRAGMENT bit set in attrs. Even if a memref is fragmented | ||||||
|  |  * it will still be presented as a single logical memref to the Trusted | ||||||
|  |  * Application. | ||||||
|  |  */ | ||||||
|  | struct optee_msg_arg { | ||||||
|  | 	u32 cmd; | ||||||
|  | 	u32 func; | ||||||
|  | 	u32 session; | ||||||
|  | 	u32 cancel_id; | ||||||
|  | 	u32 pad; | ||||||
|  | 	u32 ret; | ||||||
|  | 	u32 ret_origin; | ||||||
|  | 	u32 num_params; | ||||||
|  | 
 | ||||||
|  | 	/* num_params tells the actual number of element in params */ | ||||||
|  | 	struct optee_msg_param params[0]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * OPTEE_MSG_GET_ARG_SIZE - return size of struct optee_msg_arg | ||||||
|  |  * | ||||||
|  |  * @num_params: Number of parameters embedded in the struct optee_msg_arg | ||||||
|  |  * | ||||||
|  |  * Returns the size of the struct optee_msg_arg together with the number | ||||||
|  |  * of embedded parameters. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_GET_ARG_SIZE(num_params) \ | ||||||
|  | 	(sizeof(struct optee_msg_arg) + \ | ||||||
|  | 	 sizeof(struct optee_msg_param) * (num_params)) | ||||||
|  | 
 | ||||||
|  | /*****************************************************************************
 | ||||||
|  |  * Part 2 - requests from normal world | ||||||
|  |  *****************************************************************************/ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Return the following UID if using API specified in this file without | ||||||
|  |  * further extensions: | ||||||
|  |  * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b. | ||||||
|  |  * Represented in 4 32-bit words in OPTEE_MSG_UID_0, OPTEE_MSG_UID_1, | ||||||
|  |  * OPTEE_MSG_UID_2, OPTEE_MSG_UID_3. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_UID_0			0x384fb3e0 | ||||||
|  | #define OPTEE_MSG_UID_1			0xe7f811e3 | ||||||
|  | #define OPTEE_MSG_UID_2			0xaf630002 | ||||||
|  | #define OPTEE_MSG_UID_3			0xa5d5c51b | ||||||
|  | #define OPTEE_MSG_FUNCID_CALLS_UID	0xFF01 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Returns 2.0 if using API specified in this file without further | ||||||
|  |  * extensions. Represented in 2 32-bit words in OPTEE_MSG_REVISION_MAJOR | ||||||
|  |  * and OPTEE_MSG_REVISION_MINOR | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_REVISION_MAJOR	2 | ||||||
|  | #define OPTEE_MSG_REVISION_MINOR	0 | ||||||
|  | #define OPTEE_MSG_FUNCID_CALLS_REVISION	0xFF03 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get UUID of Trusted OS. | ||||||
|  |  * | ||||||
|  |  * Used by non-secure world to figure out which Trusted OS is installed. | ||||||
|  |  * Note that returned UUID is the UUID of the Trusted OS, not of the API. | ||||||
|  |  * | ||||||
|  |  * Returns UUID in 4 32-bit words in the same way as | ||||||
|  |  * OPTEE_MSG_FUNCID_CALLS_UID described above. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_OS_OPTEE_UUID_0	0x486178e0 | ||||||
|  | #define OPTEE_MSG_OS_OPTEE_UUID_1	0xe7f811e3 | ||||||
|  | #define OPTEE_MSG_OS_OPTEE_UUID_2	0xbc5e0002 | ||||||
|  | #define OPTEE_MSG_OS_OPTEE_UUID_3	0xa5d5c51b | ||||||
|  | #define OPTEE_MSG_FUNCID_GET_OS_UUID	0x0000 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get revision of Trusted OS. | ||||||
|  |  * | ||||||
|  |  * Used by non-secure world to figure out which version of the Trusted OS | ||||||
|  |  * is installed. Note that the returned revision is the revision of the | ||||||
|  |  * Trusted OS, not of the API. | ||||||
|  |  * | ||||||
|  |  * Returns revision in 2 32-bit words in the same way as | ||||||
|  |  * OPTEE_MSG_CALLS_REVISION described above. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_FUNCID_GET_OS_REVISION	0x0001 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Do a secure call with struct optee_msg_arg as argument | ||||||
|  |  * The OPTEE_MSG_CMD_* below defines what goes in struct optee_msg_arg::cmd | ||||||
|  |  * | ||||||
|  |  * OPTEE_MSG_CMD_OPEN_SESSION opens a session to a Trusted Application. | ||||||
|  |  * The first two parameters are tagged as meta, holding two value | ||||||
|  |  * parameters to pass the following information: | ||||||
|  |  * param[0].u.value.a-b uuid of Trusted Application | ||||||
|  |  * param[1].u.value.a-b uuid of Client | ||||||
|  |  * param[1].u.value.c Login class of client OPTEE_MSG_LOGIN_* | ||||||
|  |  * | ||||||
|  |  * OPTEE_MSG_CMD_INVOKE_COMMAND invokes a command a previously opened | ||||||
|  |  * session to a Trusted Application.  struct optee_msg_arg::func is Trusted | ||||||
|  |  * Application function, specific to the Trusted Application. | ||||||
|  |  * | ||||||
|  |  * OPTEE_MSG_CMD_CLOSE_SESSION closes a previously opened session to | ||||||
|  |  * Trusted Application. | ||||||
|  |  * | ||||||
|  |  * OPTEE_MSG_CMD_CANCEL cancels a currently invoked command. | ||||||
|  |  * | ||||||
|  |  * OPTEE_MSG_CMD_REGISTER_SHM registers a shared memory reference. The | ||||||
|  |  * information is passed as: | ||||||
|  |  * [in] param[0].attr			OPTEE_MSG_ATTR_TYPE_TMEM_INPUT | ||||||
|  |  *					[| OPTEE_MSG_ATTR_FRAGMENT] | ||||||
|  |  * [in] param[0].u.tmem.buf_ptr		physical address (of first fragment) | ||||||
|  |  * [in] param[0].u.tmem.size		size (of first fragment) | ||||||
|  |  * [in] param[0].u.tmem.shm_ref		holds shared memory reference | ||||||
|  |  * ... | ||||||
|  |  * The shared memory can optionally be fragmented, temp memrefs can follow | ||||||
|  |  * each other with all but the last with the OPTEE_MSG_ATTR_FRAGMENT bit set. | ||||||
|  |  * | ||||||
|  |  * OPTEE_MSG_CMD_UNREGISTER_SHM unregisteres a previously registered shared | ||||||
|  |  * memory reference. The information is passed as: | ||||||
|  |  * [in] param[0].attr			OPTEE_MSG_ATTR_TYPE_RMEM_INPUT | ||||||
|  |  * [in] param[0].u.rmem.shm_ref		holds shared memory reference | ||||||
|  |  * [in] param[0].u.rmem.offs		0 | ||||||
|  |  * [in] param[0].u.rmem.size		0 | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_CMD_OPEN_SESSION	0 | ||||||
|  | #define OPTEE_MSG_CMD_INVOKE_COMMAND	1 | ||||||
|  | #define OPTEE_MSG_CMD_CLOSE_SESSION	2 | ||||||
|  | #define OPTEE_MSG_CMD_CANCEL		3 | ||||||
|  | #define OPTEE_MSG_CMD_REGISTER_SHM	4 | ||||||
|  | #define OPTEE_MSG_CMD_UNREGISTER_SHM	5 | ||||||
|  | #define OPTEE_MSG_FUNCID_CALL_WITH_ARG	0x0004 | ||||||
|  | 
 | ||||||
|  | /*****************************************************************************
 | ||||||
|  |  * Part 3 - Requests from secure world, RPC | ||||||
|  |  *****************************************************************************/ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * All RPC is done with a struct optee_msg_arg as bearer of information, | ||||||
|  |  * struct optee_msg_arg::arg holds values defined by OPTEE_MSG_RPC_CMD_* below | ||||||
|  |  * | ||||||
|  |  * RPC communication with tee-supplicant is reversed compared to normal | ||||||
|  |  * client communication desribed above. The supplicant receives requests | ||||||
|  |  * and sends responses. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Load a TA into memory, defined in tee-supplicant | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_LOAD_TA	0 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Reserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_RPMB		1 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * File system access, defined in tee-supplicant | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_FS		2 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get time | ||||||
|  |  * | ||||||
|  |  * Returns number of seconds and nano seconds since the Epoch, | ||||||
|  |  * 1970-01-01 00:00:00 +0000 (UTC). | ||||||
|  |  * | ||||||
|  |  * [out] param[0].u.value.a	Number of seconds | ||||||
|  |  * [out] param[0].u.value.b	Number of nano seconds. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_GET_TIME	3 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Wait queue primitive, helper for secure world to implement a wait queue. | ||||||
|  |  * | ||||||
|  |  * If secure world need to wait for a secure world mutex it issues a sleep | ||||||
|  |  * request instead of spinning in secure world. Conversely is a wakeup | ||||||
|  |  * request issued when a secure world mutex with a thread waiting thread is | ||||||
|  |  * unlocked. | ||||||
|  |  * | ||||||
|  |  * Waiting on a key | ||||||
|  |  * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP | ||||||
|  |  * [in] param[0].u.value.b wait key | ||||||
|  |  * | ||||||
|  |  * Waking up a key | ||||||
|  |  * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP | ||||||
|  |  * [in] param[0].u.value.b wakeup key | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_WAIT_QUEUE	4 | ||||||
|  | #define OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP	0 | ||||||
|  | #define OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP	1 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Suspend execution | ||||||
|  |  * | ||||||
|  |  * [in] param[0].value	.a number of milliseconds to suspend | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_SUSPEND	5 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Allocate a piece of shared memory | ||||||
|  |  * | ||||||
|  |  * Shared memory can optionally be fragmented, to support that additional | ||||||
|  |  * spare param entries are allocated to make room for eventual fragments. | ||||||
|  |  * The spare param entries has .attr = OPTEE_MSG_ATTR_TYPE_NONE when | ||||||
|  |  * unused. All returned temp memrefs except the last should have the | ||||||
|  |  * OPTEE_MSG_ATTR_FRAGMENT bit set in the attr field. | ||||||
|  |  * | ||||||
|  |  * [in]  param[0].u.value.a		type of memory one of | ||||||
|  |  *					OPTEE_MSG_RPC_SHM_TYPE_* below | ||||||
|  |  * [in]  param[0].u.value.b		requested size | ||||||
|  |  * [in]  param[0].u.value.c		required alignment | ||||||
|  |  * | ||||||
|  |  * [out] param[0].u.tmem.buf_ptr	physical address (of first fragment) | ||||||
|  |  * [out] param[0].u.tmem.size		size (of first fragment) | ||||||
|  |  * [out] param[0].u.tmem.shm_ref	shared memory reference | ||||||
|  |  * ... | ||||||
|  |  * [out] param[n].u.tmem.buf_ptr	physical address | ||||||
|  |  * [out] param[n].u.tmem.size		size | ||||||
|  |  * [out] param[n].u.tmem.shm_ref	shared memory reference (same value | ||||||
|  |  *					as in param[n-1].u.tmem.shm_ref) | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_SHM_ALLOC	6 | ||||||
|  | /* Memory that can be shared with a non-secure user space application */ | ||||||
|  | #define OPTEE_MSG_RPC_SHM_TYPE_APPL	0 | ||||||
|  | /* Memory only shared with non-secure kernel */ | ||||||
|  | #define OPTEE_MSG_RPC_SHM_TYPE_KERNEL	1 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC | ||||||
|  |  * | ||||||
|  |  * [in]  param[0].u.value.a		type of memory one of | ||||||
|  |  *					OPTEE_MSG_RPC_SHM_TYPE_* above | ||||||
|  |  * [in]  param[0].u.value.b		value of shared memory reference | ||||||
|  |  *					returned in param[0].u.tmem.shm_ref | ||||||
|  |  *					above | ||||||
|  |  */ | ||||||
|  | #define OPTEE_MSG_RPC_CMD_SHM_FREE	7 | ||||||
|  | 
 | ||||||
|  | #endif /* _OPTEE_MSG_H */ | ||||||
							
								
								
									
										183
									
								
								drivers/tee/optee/optee_private.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								drivers/tee/optee/optee_private.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,183 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2015, Linaro Limited | ||||||
|  |  * | ||||||
|  |  * This software is licensed under the terms of the GNU General Public | ||||||
|  |  * License version 2, as published by the Free Software Foundation, and | ||||||
|  |  * may be copied, distributed, and modified under those terms. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef OPTEE_PRIVATE_H | ||||||
|  | #define OPTEE_PRIVATE_H | ||||||
|  | 
 | ||||||
|  | #include <linux/arm-smccc.h> | ||||||
|  | #include <linux/semaphore.h> | ||||||
|  | #include <linux/tee_drv.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include "optee_msg.h" | ||||||
|  | 
 | ||||||
|  | #define OPTEE_MAX_ARG_SIZE	1024 | ||||||
|  | 
 | ||||||
|  | /* Some Global Platform error codes used in this driver */ | ||||||
|  | #define TEEC_SUCCESS			0x00000000 | ||||||
|  | #define TEEC_ERROR_BAD_PARAMETERS	0xFFFF0006 | ||||||
|  | #define TEEC_ERROR_COMMUNICATION	0xFFFF000E | ||||||
|  | #define TEEC_ERROR_OUT_OF_MEMORY	0xFFFF000C | ||||||
|  | 
 | ||||||
|  | #define TEEC_ORIGIN_COMMS		0x00000002 | ||||||
|  | 
 | ||||||
|  | typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, | ||||||
|  | 				unsigned long, unsigned long, unsigned long, | ||||||
|  | 				unsigned long, unsigned long, | ||||||
|  | 				struct arm_smccc_res *); | ||||||
|  | 
 | ||||||
|  | struct optee_call_queue { | ||||||
|  | 	/* Serializes access to this struct */ | ||||||
|  | 	struct mutex mutex; | ||||||
|  | 	struct list_head waiters; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct optee_wait_queue { | ||||||
|  | 	/* Serializes access to this struct */ | ||||||
|  | 	struct mutex mu; | ||||||
|  | 	struct list_head db; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct optee_supp - supplicant synchronization struct | ||||||
|  |  * @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 | ||||||
|  |  */ | ||||||
|  | struct optee_supp { | ||||||
|  | 	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; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct optee - main service struct | ||||||
|  |  * @supp_teedev:	supplicant device | ||||||
|  |  * @teedev:		client device | ||||||
|  |  * @invoke_fn:		function to issue smc or hvc | ||||||
|  |  * @call_queue:		queue of threads waiting to call @invoke_fn | ||||||
|  |  * @wait_queue:		queue of threads from secure world waiting for a | ||||||
|  |  *			secure world sync object | ||||||
|  |  * @supp:		supplicant synchronization struct for RPC to supplicant | ||||||
|  |  * @pool:		shared memory pool | ||||||
|  |  * @memremaped_shm	virtual address of memory in shared memory pool | ||||||
|  |  */ | ||||||
|  | struct optee { | ||||||
|  | 	struct tee_device *supp_teedev; | ||||||
|  | 	struct tee_device *teedev; | ||||||
|  | 	optee_invoke_fn *invoke_fn; | ||||||
|  | 	struct optee_call_queue call_queue; | ||||||
|  | 	struct optee_wait_queue wait_queue; | ||||||
|  | 	struct optee_supp supp; | ||||||
|  | 	struct tee_shm_pool *pool; | ||||||
|  | 	void *memremaped_shm; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct optee_session { | ||||||
|  | 	struct list_head list_node; | ||||||
|  | 	u32 session_id; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct optee_context_data { | ||||||
|  | 	/* Serializes access to this struct */ | ||||||
|  | 	struct mutex mutex; | ||||||
|  | 	struct list_head sess_list; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct optee_rpc_param { | ||||||
|  | 	u32	a0; | ||||||
|  | 	u32	a1; | ||||||
|  | 	u32	a2; | ||||||
|  | 	u32	a3; | ||||||
|  | 	u32	a4; | ||||||
|  | 	u32	a5; | ||||||
|  | 	u32	a6; | ||||||
|  | 	u32	a7; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param); | ||||||
|  | 
 | ||||||
|  | void optee_wait_queue_init(struct optee_wait_queue *wq); | ||||||
|  | void optee_wait_queue_exit(struct optee_wait_queue *wq); | ||||||
|  | 
 | ||||||
|  | u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, | ||||||
|  | 			struct tee_param *param); | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | 
 | ||||||
|  | int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, | ||||||
|  | 		    struct tee_param *param); | ||||||
|  | int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, | ||||||
|  | 		    struct tee_param *param); | ||||||
|  | 
 | ||||||
|  | u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); | ||||||
|  | int optee_open_session(struct tee_context *ctx, | ||||||
|  | 		       struct tee_ioctl_open_session_arg *arg, | ||||||
|  | 		       struct tee_param *param); | ||||||
|  | int optee_close_session(struct tee_context *ctx, u32 session); | ||||||
|  | int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, | ||||||
|  | 		      struct tee_param *param); | ||||||
|  | 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_from_msg_param(struct tee_param *params, size_t num_params, | ||||||
|  | 			 const struct optee_msg_param *msg_params); | ||||||
|  | int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, | ||||||
|  | 		       const struct tee_param *params); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Small helpers | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static inline void *reg_pair_to_ptr(u32 reg0, u32 reg1) | ||||||
|  | { | ||||||
|  | 	return (void *)(unsigned long)(((u64)reg0 << 32) | reg1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) | ||||||
|  | { | ||||||
|  | 	*reg0 = val >> 32; | ||||||
|  | 	*reg1 = val; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /*OPTEE_PRIVATE_H*/ | ||||||
							
								
								
									
										450
									
								
								drivers/tee/optee/optee_smc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										450
									
								
								drivers/tee/optee/optee_smc.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,450 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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 OPTEE_SMC_H | ||||||
|  | #define OPTEE_SMC_H | ||||||
|  | 
 | ||||||
|  | #include <linux/arm-smccc.h> | ||||||
|  | #include <linux/bitops.h> | ||||||
|  | 
 | ||||||
|  | #define OPTEE_SMC_STD_CALL_VAL(func_num) \ | ||||||
|  | 	ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \ | ||||||
|  | 			   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num)) | ||||||
|  | #define OPTEE_SMC_FAST_CALL_VAL(func_num) \ | ||||||
|  | 	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ | ||||||
|  | 			   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num)) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Function specified by SMC Calling convention. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_CALLS_COUNT	0xFF00 | ||||||
|  | #define OPTEE_SMC_CALLS_COUNT \ | ||||||
|  | 	ARM_SMCCC_CALL_VAL(OPTEE_SMC_FAST_CALL, SMCCC_SMC_32, \ | ||||||
|  | 			   SMCCC_OWNER_TRUSTED_OS_END, \ | ||||||
|  | 			   OPTEE_SMC_FUNCID_CALLS_COUNT) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Normal cached memory (write-back), shareable for SMP systems and not | ||||||
|  |  * shareable for UP systems. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_SHM_CACHED		1 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * a0..a7 is used as register names in the descriptions below, on arm32 | ||||||
|  |  * that translates to r0..r7 and on arm64 to w0..w7. In both cases it's | ||||||
|  |  * 32-bit registers. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Function specified by SMC Calling convention | ||||||
|  |  * | ||||||
|  |  * Return one of the following UIDs if using API specified in this file | ||||||
|  |  * without further extentions: | ||||||
|  |  * 65cb6b93-af0c-4617-8ed6-644a8d1140f8 | ||||||
|  |  * see also OPTEE_SMC_UID_* in optee_msg.h | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_CALLS_UID OPTEE_MSG_FUNCID_CALLS_UID | ||||||
|  | #define OPTEE_SMC_CALLS_UID \ | ||||||
|  | 	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ | ||||||
|  | 			   ARM_SMCCC_OWNER_TRUSTED_OS_END, \ | ||||||
|  | 			   OPTEE_SMC_FUNCID_CALLS_UID) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Function specified by SMC Calling convention | ||||||
|  |  * | ||||||
|  |  * Returns 2.0 if using API specified in this file without further extentions. | ||||||
|  |  * see also OPTEE_MSG_REVISION_* in optee_msg.h | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_CALLS_REVISION OPTEE_MSG_FUNCID_CALLS_REVISION | ||||||
|  | #define OPTEE_SMC_CALLS_REVISION \ | ||||||
|  | 	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ | ||||||
|  | 			   ARM_SMCCC_OWNER_TRUSTED_OS_END, \ | ||||||
|  | 			   OPTEE_SMC_FUNCID_CALLS_REVISION) | ||||||
|  | 
 | ||||||
|  | struct optee_smc_calls_revision_result { | ||||||
|  | 	unsigned long major; | ||||||
|  | 	unsigned long minor; | ||||||
|  | 	unsigned long reserved0; | ||||||
|  | 	unsigned long reserved1; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get UUID of Trusted OS. | ||||||
|  |  * | ||||||
|  |  * Used by non-secure world to figure out which Trusted OS is installed. | ||||||
|  |  * Note that returned UUID is the UUID of the Trusted OS, not of the API. | ||||||
|  |  * | ||||||
|  |  * Returns UUID in a0-4 in the same way as OPTEE_SMC_CALLS_UID | ||||||
|  |  * described above. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEE_MSG_FUNCID_GET_OS_UUID | ||||||
|  | #define OPTEE_SMC_CALL_GET_OS_UUID \ | ||||||
|  | 	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get revision of Trusted OS. | ||||||
|  |  * | ||||||
|  |  * Used by non-secure world to figure out which version of the Trusted OS | ||||||
|  |  * is installed. Note that the returned revision is the revision of the | ||||||
|  |  * Trusted OS, not of the API. | ||||||
|  |  * | ||||||
|  |  * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION | ||||||
|  |  * described above. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEE_MSG_FUNCID_GET_OS_REVISION | ||||||
|  | #define OPTEE_SMC_CALL_GET_OS_REVISION \ | ||||||
|  | 	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Call with struct optee_msg_arg as argument | ||||||
|  |  * | ||||||
|  |  * Call register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC*CALL_WITH_ARG | ||||||
|  |  * a1	Upper 32bit of a 64bit physical pointer to a struct optee_msg_arg | ||||||
|  |  * a2	Lower 32bit of a 64bit physical pointer to a struct optee_msg_arg | ||||||
|  |  * a3	Cache settings, not used if physical pointer is in a predefined shared | ||||||
|  |  *	memory area else per OPTEE_SMC_SHM_* | ||||||
|  |  * a4-6	Not used | ||||||
|  |  * a7	Hypervisor Client ID register | ||||||
|  |  * | ||||||
|  |  * Normal return register usage: | ||||||
|  |  * a0	Return value, OPTEE_SMC_RETURN_* | ||||||
|  |  * a1-3	Not used | ||||||
|  |  * a4-7	Preserved | ||||||
|  |  * | ||||||
|  |  * OPTEE_SMC_RETURN_ETHREAD_LIMIT return register usage: | ||||||
|  |  * a0	Return value, OPTEE_SMC_RETURN_ETHREAD_LIMIT | ||||||
|  |  * a1-3	Preserved | ||||||
|  |  * a4-7	Preserved | ||||||
|  |  * | ||||||
|  |  * RPC return register usage: | ||||||
|  |  * a0	Return value, OPTEE_SMC_RETURN_IS_RPC(val) | ||||||
|  |  * a1-2	RPC parameters | ||||||
|  |  * a3-7	Resume information, must be preserved | ||||||
|  |  * | ||||||
|  |  * Possible return values: | ||||||
|  |  * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION	Trusted OS does not recognize this | ||||||
|  |  *					function. | ||||||
|  |  * OPTEE_SMC_RETURN_OK			Call completed, result updated in | ||||||
|  |  *					the previously supplied struct | ||||||
|  |  *					optee_msg_arg. | ||||||
|  |  * OPTEE_SMC_RETURN_ETHREAD_LIMIT	Number of Trusted OS threads exceeded, | ||||||
|  |  *					try again later. | ||||||
|  |  * OPTEE_SMC_RETURN_EBADADDR		Bad physcial pointer to struct | ||||||
|  |  *					optee_msg_arg. | ||||||
|  |  * OPTEE_SMC_RETURN_EBADCMD		Bad/unknown cmd in struct optee_msg_arg | ||||||
|  |  * OPTEE_SMC_RETURN_IS_RPC()		Call suspended by RPC call to normal | ||||||
|  |  *					world. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG | ||||||
|  | #define OPTEE_SMC_CALL_WITH_ARG \ | ||||||
|  | 	OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get Shared Memory Config | ||||||
|  |  * | ||||||
|  |  * Returns the Secure/Non-secure shared memory config. | ||||||
|  |  * | ||||||
|  |  * Call register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_GET_SHM_CONFIG | ||||||
|  |  * a1-6	Not used | ||||||
|  |  * a7	Hypervisor Client ID register | ||||||
|  |  * | ||||||
|  |  * Have config return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_OK | ||||||
|  |  * a1	Physical address of start of SHM | ||||||
|  |  * a2	Size of of SHM | ||||||
|  |  * a3	Cache settings of memory, as defined by the | ||||||
|  |  *	OPTEE_SMC_SHM_* values above | ||||||
|  |  * a4-7	Preserved | ||||||
|  |  * | ||||||
|  |  * Not available register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_ENOTAVAIL | ||||||
|  |  * a1-3 Not used | ||||||
|  |  * a4-7	Preserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_GET_SHM_CONFIG	7 | ||||||
|  | #define OPTEE_SMC_GET_SHM_CONFIG \ | ||||||
|  | 	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG) | ||||||
|  | 
 | ||||||
|  | struct optee_smc_get_shm_config_result { | ||||||
|  | 	unsigned long status; | ||||||
|  | 	unsigned long start; | ||||||
|  | 	unsigned long size; | ||||||
|  | 	unsigned long settings; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Exchanges capabilities between normal world and secure world | ||||||
|  |  * | ||||||
|  |  * Call register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_EXCHANGE_CAPABILITIES | ||||||
|  |  * a1	bitfield of normal world capabilities OPTEE_SMC_NSEC_CAP_* | ||||||
|  |  * a2-6	Not used | ||||||
|  |  * a7	Hypervisor Client ID register | ||||||
|  |  * | ||||||
|  |  * Normal return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_OK | ||||||
|  |  * a1	bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* | ||||||
|  |  * a2-7	Preserved | ||||||
|  |  * | ||||||
|  |  * Error return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world | ||||||
|  |  * a1	bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* | ||||||
|  |  * a2-7 Preserved | ||||||
|  |  */ | ||||||
|  | /* Normal world works as a uniprocessor system */ | ||||||
|  | #define OPTEE_SMC_NSEC_CAP_UNIPROCESSOR		BIT(0) | ||||||
|  | /* Secure world has reserved shared memory for normal world to use */ | ||||||
|  | #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) | ||||||
|  | #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES	9 | ||||||
|  | #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ | ||||||
|  | 	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) | ||||||
|  | 
 | ||||||
|  | struct optee_smc_exchange_capabilities_result { | ||||||
|  | 	unsigned long status; | ||||||
|  | 	unsigned long capabilities; | ||||||
|  | 	unsigned long reserved0; | ||||||
|  | 	unsigned long reserved1; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Disable and empties cache of shared memory objects | ||||||
|  |  * | ||||||
|  |  * Secure world can cache frequently used shared memory objects, for | ||||||
|  |  * example objects used as RPC arguments. When secure world is idle this | ||||||
|  |  * function returns one shared memory reference to free. To disable the | ||||||
|  |  * cache and free all cached objects this function has to be called until | ||||||
|  |  * it returns OPTEE_SMC_RETURN_ENOTAVAIL. | ||||||
|  |  * | ||||||
|  |  * Call register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_DISABLE_SHM_CACHE | ||||||
|  |  * a1-6	Not used | ||||||
|  |  * a7	Hypervisor Client ID register | ||||||
|  |  * | ||||||
|  |  * Normal return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_OK | ||||||
|  |  * a1	Upper 32bit of a 64bit Shared memory cookie | ||||||
|  |  * a2	Lower 32bit of a 64bit Shared memory cookie | ||||||
|  |  * a3-7	Preserved | ||||||
|  |  * | ||||||
|  |  * Cache empty return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_ENOTAVAIL | ||||||
|  |  * a1-7	Preserved | ||||||
|  |  * | ||||||
|  |  * Not idle return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_EBUSY | ||||||
|  |  * a1-7	Preserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE	10 | ||||||
|  | #define OPTEE_SMC_DISABLE_SHM_CACHE \ | ||||||
|  | 	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE) | ||||||
|  | 
 | ||||||
|  | struct optee_smc_disable_shm_cache_result { | ||||||
|  | 	unsigned long status; | ||||||
|  | 	unsigned long shm_upper32; | ||||||
|  | 	unsigned long shm_lower32; | ||||||
|  | 	unsigned long reserved0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Enable cache of shared memory objects | ||||||
|  |  * | ||||||
|  |  * Secure world can cache frequently used shared memory objects, for | ||||||
|  |  * example objects used as RPC arguments. When secure world is idle this | ||||||
|  |  * function returns OPTEE_SMC_RETURN_OK and the cache is enabled. If | ||||||
|  |  * secure world isn't idle OPTEE_SMC_RETURN_EBUSY is returned. | ||||||
|  |  * | ||||||
|  |  * Call register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_ENABLE_SHM_CACHE | ||||||
|  |  * a1-6	Not used | ||||||
|  |  * a7	Hypervisor Client ID register | ||||||
|  |  * | ||||||
|  |  * Normal return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_OK | ||||||
|  |  * a1-7	Preserved | ||||||
|  |  * | ||||||
|  |  * Not idle return register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_EBUSY | ||||||
|  |  * a1-7	Preserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE	11 | ||||||
|  | #define OPTEE_SMC_ENABLE_SHM_CACHE \ | ||||||
|  | 	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Resume from RPC (for example after processing an IRQ) | ||||||
|  |  * | ||||||
|  |  * Call register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC | ||||||
|  |  * a1-3	Value of a1-3 when OPTEE_SMC_CALL_WITH_ARG returned | ||||||
|  |  *	OPTEE_SMC_RETURN_RPC in a0 | ||||||
|  |  * | ||||||
|  |  * Return register usage is the same as for OPTEE_SMC_*CALL_WITH_ARG above. | ||||||
|  |  * | ||||||
|  |  * Possible return values | ||||||
|  |  * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION	Trusted OS does not recognize this | ||||||
|  |  *					function. | ||||||
|  |  * OPTEE_SMC_RETURN_OK			Original call completed, result | ||||||
|  |  *					updated in the previously supplied. | ||||||
|  |  *					struct optee_msg_arg | ||||||
|  |  * OPTEE_SMC_RETURN_RPC			Call suspended by RPC call to normal | ||||||
|  |  *					world. | ||||||
|  |  * OPTEE_SMC_RETURN_ERESUME		Resume failed, the opaque resume | ||||||
|  |  *					information was corrupt. | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_FUNCID_RETURN_FROM_RPC	3 | ||||||
|  | #define OPTEE_SMC_CALL_RETURN_FROM_RPC \ | ||||||
|  | 	OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC) | ||||||
|  | 
 | ||||||
|  | #define OPTEE_SMC_RETURN_RPC_PREFIX_MASK	0xFFFF0000 | ||||||
|  | #define OPTEE_SMC_RETURN_RPC_PREFIX		0xFFFF0000 | ||||||
|  | #define OPTEE_SMC_RETURN_RPC_FUNC_MASK		0x0000FFFF | ||||||
|  | 
 | ||||||
|  | #define OPTEE_SMC_RETURN_GET_RPC_FUNC(ret) \ | ||||||
|  | 	((ret) & OPTEE_SMC_RETURN_RPC_FUNC_MASK) | ||||||
|  | 
 | ||||||
|  | #define OPTEE_SMC_RPC_VAL(func)		((func) | OPTEE_SMC_RETURN_RPC_PREFIX) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Allocate memory for RPC parameter passing. The memory is used to hold a | ||||||
|  |  * struct optee_msg_arg. | ||||||
|  |  * | ||||||
|  |  * "Call" register usage: | ||||||
|  |  * a0	This value, OPTEE_SMC_RETURN_RPC_ALLOC | ||||||
|  |  * a1	Size in bytes of required argument memory | ||||||
|  |  * a2	Not used | ||||||
|  |  * a3	Resume information, must be preserved | ||||||
|  |  * a4-5	Not used | ||||||
|  |  * a6-7	Resume information, must be preserved | ||||||
|  |  * | ||||||
|  |  * "Return" register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. | ||||||
|  |  * a1	Upper 32bits of 64bit physical pointer to allocated | ||||||
|  |  *	memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't | ||||||
|  |  *	be allocated. | ||||||
|  |  * a2	Lower 32bits of 64bit physical pointer to allocated | ||||||
|  |  *	memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't | ||||||
|  |  *	be allocated | ||||||
|  |  * a3	Preserved | ||||||
|  |  * a4	Upper 32bits of 64bit Shared memory cookie used when freeing | ||||||
|  |  *	the memory or doing an RPC | ||||||
|  |  * a5	Lower 32bits of 64bit Shared memory cookie used when freeing | ||||||
|  |  *	the memory or doing an RPC | ||||||
|  |  * a6-7	Preserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_RPC_FUNC_ALLOC	0 | ||||||
|  | #define OPTEE_SMC_RETURN_RPC_ALLOC \ | ||||||
|  | 	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC | ||||||
|  |  * | ||||||
|  |  * "Call" register usage: | ||||||
|  |  * a0	This value, OPTEE_SMC_RETURN_RPC_FREE | ||||||
|  |  * a1	Upper 32bits of 64bit shared memory cookie belonging to this | ||||||
|  |  *	argument memory | ||||||
|  |  * a2	Lower 32bits of 64bit shared memory cookie belonging to this | ||||||
|  |  *	argument memory | ||||||
|  |  * a3-7	Resume information, must be preserved | ||||||
|  |  * | ||||||
|  |  * "Return" register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. | ||||||
|  |  * a1-2	Not used | ||||||
|  |  * a3-7	Preserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_RPC_FUNC_FREE		2 | ||||||
|  | #define OPTEE_SMC_RETURN_RPC_FREE \ | ||||||
|  | 	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Deliver an IRQ in normal world. | ||||||
|  |  * | ||||||
|  |  * "Call" register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_RPC_IRQ | ||||||
|  |  * a1-7	Resume information, must be preserved | ||||||
|  |  * | ||||||
|  |  * "Return" register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. | ||||||
|  |  * a1-7	Preserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_RPC_FUNC_IRQ		4 | ||||||
|  | #define OPTEE_SMC_RETURN_RPC_IRQ \ | ||||||
|  | 	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_IRQ) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Do an RPC request. The supplied struct optee_msg_arg tells which | ||||||
|  |  * request to do and the parameters for the request. The following fields | ||||||
|  |  * are used (the rest are unused): | ||||||
|  |  * - cmd		the Request ID | ||||||
|  |  * - ret		return value of the request, filled in by normal world | ||||||
|  |  * - num_params		number of parameters for the request | ||||||
|  |  * - params		the parameters | ||||||
|  |  * - param_attrs	attributes of the parameters | ||||||
|  |  * | ||||||
|  |  * "Call" register usage: | ||||||
|  |  * a0	OPTEE_SMC_RETURN_RPC_CMD | ||||||
|  |  * a1	Upper 32bit of a 64bit Shared memory cookie holding a | ||||||
|  |  *	struct optee_msg_arg, must be preserved, only the data should | ||||||
|  |  *	be updated | ||||||
|  |  * a2	Lower 32bit of a 64bit Shared memory cookie holding a | ||||||
|  |  *	struct optee_msg_arg, must be preserved, only the data should | ||||||
|  |  *	be updated | ||||||
|  |  * a3-7	Resume information, must be preserved | ||||||
|  |  * | ||||||
|  |  * "Return" register usage: | ||||||
|  |  * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. | ||||||
|  |  * a1-2	Not used | ||||||
|  |  * a3-7	Preserved | ||||||
|  |  */ | ||||||
|  | #define OPTEE_SMC_RPC_FUNC_CMD		5 | ||||||
|  | #define OPTEE_SMC_RETURN_RPC_CMD \ | ||||||
|  | 	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD) | ||||||
|  | 
 | ||||||
|  | /* Returned in a0 */ | ||||||
|  | #define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF | ||||||
|  | 
 | ||||||
|  | /* Returned in a0 only from Trusted OS functions */ | ||||||
|  | #define OPTEE_SMC_RETURN_OK		0x0 | ||||||
|  | #define OPTEE_SMC_RETURN_ETHREAD_LIMIT	0x1 | ||||||
|  | #define OPTEE_SMC_RETURN_EBUSY		0x2 | ||||||
|  | #define OPTEE_SMC_RETURN_ERESUME	0x3 | ||||||
|  | #define OPTEE_SMC_RETURN_EBADADDR	0x4 | ||||||
|  | #define OPTEE_SMC_RETURN_EBADCMD	0x5 | ||||||
|  | #define OPTEE_SMC_RETURN_ENOMEM		0x6 | ||||||
|  | #define OPTEE_SMC_RETURN_ENOTAVAIL	0x7 | ||||||
|  | #define OPTEE_SMC_RETURN_IS_RPC(ret)	__optee_smc_return_is_rpc((ret)) | ||||||
|  | 
 | ||||||
|  | static inline bool __optee_smc_return_is_rpc(u32 ret) | ||||||
|  | { | ||||||
|  | 	return ret != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION && | ||||||
|  | 	       (ret & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) == | ||||||
|  | 			OPTEE_SMC_RETURN_RPC_PREFIX; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /* OPTEE_SMC_H */ | ||||||
							
								
								
									
										396
									
								
								drivers/tee/optee/rpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								drivers/tee/optee/rpc.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,396 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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) KBUILD_MODNAME ": " fmt | ||||||
|  | 
 | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/device.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/tee_drv.h> | ||||||
|  | #include "optee_private.h" | ||||||
|  | #include "optee_smc.h" | ||||||
|  | 
 | ||||||
|  | struct wq_entry { | ||||||
|  | 	struct list_head link; | ||||||
|  | 	struct completion c; | ||||||
|  | 	u32 key; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void optee_wait_queue_init(struct optee_wait_queue *priv) | ||||||
|  | { | ||||||
|  | 	mutex_init(&priv->mu); | ||||||
|  | 	INIT_LIST_HEAD(&priv->db); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void optee_wait_queue_exit(struct optee_wait_queue *priv) | ||||||
|  | { | ||||||
|  | 	mutex_destroy(&priv->mu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) | ||||||
|  | { | ||||||
|  | 	struct timespec64 ts; | ||||||
|  | 
 | ||||||
|  | 	if (arg->num_params != 1) | ||||||
|  | 		goto bad; | ||||||
|  | 	if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != | ||||||
|  | 			OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT) | ||||||
|  | 		goto bad; | ||||||
|  | 
 | ||||||
|  | 	getnstimeofday64(&ts); | ||||||
|  | 	arg->params[0].u.value.a = ts.tv_sec; | ||||||
|  | 	arg->params[0].u.value.b = ts.tv_nsec; | ||||||
|  | 
 | ||||||
|  | 	arg->ret = TEEC_SUCCESS; | ||||||
|  | 	return; | ||||||
|  | bad: | ||||||
|  | 	arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key) | ||||||
|  | { | ||||||
|  | 	struct wq_entry *w; | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&wq->mu); | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(w, &wq->db, link) | ||||||
|  | 		if (w->key == key) | ||||||
|  | 			goto out; | ||||||
|  | 
 | ||||||
|  | 	w = kmalloc(sizeof(*w), GFP_KERNEL); | ||||||
|  | 	if (w) { | ||||||
|  | 		init_completion(&w->c); | ||||||
|  | 		w->key = key; | ||||||
|  | 		list_add_tail(&w->link, &wq->db); | ||||||
|  | 	} | ||||||
|  | out: | ||||||
|  | 	mutex_unlock(&wq->mu); | ||||||
|  | 	return w; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void wq_sleep(struct optee_wait_queue *wq, u32 key) | ||||||
|  | { | ||||||
|  | 	struct wq_entry *w = wq_entry_get(wq, key); | ||||||
|  | 
 | ||||||
|  | 	if (w) { | ||||||
|  | 		wait_for_completion(&w->c); | ||||||
|  | 		mutex_lock(&wq->mu); | ||||||
|  | 		list_del(&w->link); | ||||||
|  | 		mutex_unlock(&wq->mu); | ||||||
|  | 		kfree(w); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void wq_wakeup(struct optee_wait_queue *wq, u32 key) | ||||||
|  | { | ||||||
|  | 	struct wq_entry *w = wq_entry_get(wq, key); | ||||||
|  | 
 | ||||||
|  | 	if (w) | ||||||
|  | 		complete(&w->c); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_rpc_func_cmd_wq(struct optee *optee, | ||||||
|  | 				   struct optee_msg_arg *arg) | ||||||
|  | { | ||||||
|  | 	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; | ||||||
|  | 
 | ||||||
|  | 	switch (arg->params[0].u.value.a) { | ||||||
|  | 	case OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP: | ||||||
|  | 		wq_sleep(&optee->wait_queue, arg->params[0].u.value.b); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP: | ||||||
|  | 		wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		goto bad; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	arg->ret = TEEC_SUCCESS; | ||||||
|  | 	return; | ||||||
|  | bad: | ||||||
|  | 	arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) | ||||||
|  | { | ||||||
|  | 	u32 msec_to_wait; | ||||||
|  | 
 | ||||||
|  | 	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; | ||||||
|  | 
 | ||||||
|  | 	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); | ||||||
|  | 
 | ||||||
|  | 	arg->ret = TEEC_SUCCESS; | ||||||
|  | 	return; | ||||||
|  | bad: | ||||||
|  | 	arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_rpc_supp_cmd(struct tee_context *ctx, | ||||||
|  | 				struct optee_msg_arg *arg) | ||||||
|  | { | ||||||
|  | 	struct tee_param *params; | ||||||
|  | 
 | ||||||
|  | 	arg->ret_origin = TEEC_ORIGIN_COMMS; | ||||||
|  | 
 | ||||||
|  | 	params = kmalloc_array(arg->num_params, sizeof(struct tee_param), | ||||||
|  | 			       GFP_KERNEL); | ||||||
|  | 	if (!params) { | ||||||
|  | 		arg->ret = TEEC_ERROR_OUT_OF_MEMORY; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (optee_from_msg_param(params, arg->num_params, arg->params)) { | ||||||
|  | 		arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params); | ||||||
|  | 
 | ||||||
|  | 	if (optee_to_msg_param(arg->params, arg->num_params, params)) | ||||||
|  | 		arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | out: | ||||||
|  | 	kfree(params); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) | ||||||
|  | { | ||||||
|  | 	u32 ret; | ||||||
|  | 	struct tee_param param; | ||||||
|  | 	struct optee *optee = tee_get_drvdata(ctx->teedev); | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 
 | ||||||
|  | 	param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; | ||||||
|  | 	param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL; | ||||||
|  | 	param.u.value.b = sz; | ||||||
|  | 	param.u.value.c = 0; | ||||||
|  | 
 | ||||||
|  | 	ret = optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_ALLOC, 1, ¶m); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&optee->supp.ctx_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); | ||||||
|  | 	return shm; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, | ||||||
|  | 					  struct optee_msg_arg *arg) | ||||||
|  | { | ||||||
|  | 	phys_addr_t pa; | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	size_t sz; | ||||||
|  | 	size_t n; | ||||||
|  | 
 | ||||||
|  | 	arg->ret_origin = TEEC_ORIGIN_COMMS; | ||||||
|  | 
 | ||||||
|  | 	if (!arg->num_params || | ||||||
|  | 	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { | ||||||
|  | 		arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (n = 1; n < arg->num_params; n++) { | ||||||
|  | 		if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) { | ||||||
|  | 			arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sz = arg->params[0].u.value.b; | ||||||
|  | 	switch (arg->params[0].u.value.a) { | ||||||
|  | 	case OPTEE_MSG_RPC_SHM_TYPE_APPL: | ||||||
|  | 		shm = cmd_alloc_suppl(ctx, sz); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: | ||||||
|  | 		shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (IS_ERR(shm)) { | ||||||
|  | 		arg->ret = TEEC_ERROR_OUT_OF_MEMORY; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (tee_shm_get_pa(shm, 0, &pa)) { | ||||||
|  | 		arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | 		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; | ||||||
|  | 	arg->ret = TEEC_SUCCESS; | ||||||
|  | 	return; | ||||||
|  | bad: | ||||||
|  | 	tee_shm_free(shm); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) | ||||||
|  | { | ||||||
|  | 	struct tee_param param; | ||||||
|  | 
 | ||||||
|  | 	param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; | ||||||
|  | 	param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL; | ||||||
|  | 	param.u.value.b = tee_shm_get_id(shm); | ||||||
|  | 	param.u.value.c = 0; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure | ||||||
|  | 	 * world has released its reference. | ||||||
|  | 	 * | ||||||
|  | 	 * It's better to do this before sending the request to supplicant | ||||||
|  | 	 * as we'd like to let the process doing the initial allocation to | ||||||
|  | 	 * do release the last reference too in order to avoid stacking | ||||||
|  | 	 * many pending fput() on the client process. This could otherwise | ||||||
|  | 	 * happen if secure world does many allocate and free in a single | ||||||
|  | 	 * invoke. | ||||||
|  | 	 */ | ||||||
|  | 	tee_shm_put(shm); | ||||||
|  | 
 | ||||||
|  | 	optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_FREE, 1, ¶m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, | ||||||
|  | 					 struct optee_msg_arg *arg) | ||||||
|  | { | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 
 | ||||||
|  | 	arg->ret_origin = TEEC_ORIGIN_COMMS; | ||||||
|  | 
 | ||||||
|  | 	if (arg->num_params != 1 || | ||||||
|  | 	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { | ||||||
|  | 		arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; | ||||||
|  | 	switch (arg->params[0].u.value.a) { | ||||||
|  | 	case OPTEE_MSG_RPC_SHM_TYPE_APPL: | ||||||
|  | 		cmd_free_suppl(ctx, shm); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: | ||||||
|  | 		tee_shm_free(shm); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		arg->ret = TEEC_ERROR_BAD_PARAMETERS; | ||||||
|  | 	} | ||||||
|  | 	arg->ret = TEEC_SUCCESS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, | ||||||
|  | 				struct tee_shm *shm) | ||||||
|  | { | ||||||
|  | 	struct optee_msg_arg *arg; | ||||||
|  | 
 | ||||||
|  | 	arg = tee_shm_get_va(shm, 0); | ||||||
|  | 	if (IS_ERR(arg)) { | ||||||
|  | 		pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch (arg->cmd) { | ||||||
|  | 	case OPTEE_MSG_RPC_CMD_GET_TIME: | ||||||
|  | 		handle_rpc_func_cmd_get_time(arg); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_MSG_RPC_CMD_WAIT_QUEUE: | ||||||
|  | 		handle_rpc_func_cmd_wq(optee, arg); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_MSG_RPC_CMD_SUSPEND: | ||||||
|  | 		handle_rpc_func_cmd_wait(arg); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_MSG_RPC_CMD_SHM_ALLOC: | ||||||
|  | 		handle_rpc_func_cmd_shm_alloc(ctx, arg); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_MSG_RPC_CMD_SHM_FREE: | ||||||
|  | 		handle_rpc_func_cmd_shm_free(ctx, arg); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		handle_rpc_supp_cmd(ctx, arg); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_handle_rpc() - handle RPC from secure world | ||||||
|  |  * @ctx:	context doing the RPC | ||||||
|  |  * @param:	value of registers for the RPC | ||||||
|  |  * | ||||||
|  |  * Result of RPC is written back into @param. | ||||||
|  |  */ | ||||||
|  | void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param) | ||||||
|  | { | ||||||
|  | 	struct tee_device *teedev = ctx->teedev; | ||||||
|  | 	struct optee *optee = tee_get_drvdata(teedev); | ||||||
|  | 	struct tee_shm *shm; | ||||||
|  | 	phys_addr_t pa; | ||||||
|  | 
 | ||||||
|  | 	switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { | ||||||
|  | 	case OPTEE_SMC_RPC_FUNC_ALLOC: | ||||||
|  | 		shm = tee_shm_alloc(ctx, param->a1, TEE_SHM_MAPPED); | ||||||
|  | 		if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { | ||||||
|  | 			reg_pair_from_64(¶m->a1, ¶m->a2, pa); | ||||||
|  | 			reg_pair_from_64(¶m->a4, ¶m->a5, | ||||||
|  | 					 (unsigned long)shm); | ||||||
|  | 		} else { | ||||||
|  | 			param->a1 = 0; | ||||||
|  | 			param->a2 = 0; | ||||||
|  | 			param->a4 = 0; | ||||||
|  | 			param->a5 = 0; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_SMC_RPC_FUNC_FREE: | ||||||
|  | 		shm = reg_pair_to_ptr(param->a1, param->a2); | ||||||
|  | 		tee_shm_free(shm); | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_SMC_RPC_FUNC_IRQ: | ||||||
|  | 		/*
 | ||||||
|  | 		 * 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 | ||||||
|  | 		 * vector. | ||||||
|  | 		 */ | ||||||
|  | 		break; | ||||||
|  | 	case OPTEE_SMC_RPC_FUNC_CMD: | ||||||
|  | 		shm = reg_pair_to_ptr(param->a1, param->a2); | ||||||
|  | 		handle_rpc_func_cmd(ctx, optee, shm); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		pr_warn("Unknown RPC func 0x%x\n", | ||||||
|  | 			(u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; | ||||||
|  | } | ||||||
							
								
								
									
										273
									
								
								drivers/tee/optee/supp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								drivers/tee/optee/supp.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,273 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2015, Linaro Limited | ||||||
|  |  * | ||||||
|  |  * This software is licensed under the terms of the GNU General Public | ||||||
|  |  * License version 2, as published by the Free Software Foundation, and | ||||||
|  |  * may be copied, distributed, and modified under those terms. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | #include <linux/device.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/uaccess.h> | ||||||
|  | #include "optee_private.h" | ||||||
|  | 
 | ||||||
|  | void optee_supp_init(struct optee_supp *supp) | ||||||
|  | { | ||||||
|  | 	memset(supp, 0, sizeof(*supp)); | ||||||
|  | 	mutex_init(&supp->ctx_mutex); | ||||||
|  | 	mutex_init(&supp->thrd_mutex); | ||||||
|  | 	mutex_init(&supp->supp_mutex); | ||||||
|  | 	init_completion(&supp->data_to_supp); | ||||||
|  | 	init_completion(&supp->data_from_supp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void optee_supp_uninit(struct optee_supp *supp) | ||||||
|  | { | ||||||
|  | 	mutex_destroy(&supp->ctx_mutex); | ||||||
|  | 	mutex_destroy(&supp->thrd_mutex); | ||||||
|  | 	mutex_destroy(&supp->supp_mutex); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_supp_thrd_req() - request service from supplicant | ||||||
|  |  * @ctx:	context doing the request | ||||||
|  |  * @func:	function requested | ||||||
|  |  * @num_params:	number of elements in @param array | ||||||
|  |  * @param:	parameters for function | ||||||
|  |  * | ||||||
|  |  * Returns result of operation to be passed to secure world | ||||||
|  |  */ | ||||||
|  | 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; | ||||||
|  | 	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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * 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. | ||||||
|  | 	 */ | ||||||
|  | 
 | ||||||
|  | 	supp->func = func; | ||||||
|  | 	supp->num_params = num_params; | ||||||
|  | 	supp->param = param; | ||||||
|  | 	supp->req_posted = true; | ||||||
|  | 
 | ||||||
|  | 	/* Let supplicant get the data */ | ||||||
|  | 	complete(&supp->data_to_supp); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Wait for supplicant to process and return result, once we've | ||||||
|  | 	 * returned from wait_for_completion(data_from_supp) we have | ||||||
|  | 	 * exclusive access again. | ||||||
|  | 	 */ | ||||||
|  | 	while (wait_for_completion_interruptible(&supp->data_from_supp)) { | ||||||
|  | 		mutex_lock(&supp->ctx_mutex); | ||||||
|  | 		interruptable = !supp->ctx; | ||||||
|  | 		if (interruptable) { | ||||||
|  | 			/*
 | ||||||
|  | 			 * There's no supplicant available and since the | ||||||
|  | 			 * supp->ctx_mutex currently is held none can | ||||||
|  | 			 * become available until the mutex released | ||||||
|  | 			 * again. | ||||||
|  | 			 * | ||||||
|  | 			 * Interrupting an RPC to supplicant is only | ||||||
|  | 			 * allowed as a way of slightly improving the user | ||||||
|  | 			 * experience in case the supplicant hasn't been | ||||||
|  | 			 * started yet. During normal operation the supplicant | ||||||
|  | 			 * 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); | ||||||
|  | 		} | ||||||
|  | 		mutex_unlock(&supp->ctx_mutex); | ||||||
|  | 		if (interruptable) | ||||||
|  | 			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); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_supp_recv() - receive request for supplicant | ||||||
|  |  * @ctx:	context receiving the request | ||||||
|  |  * @func:	requested function in supplicant | ||||||
|  |  * @num_params:	number of elements allocated in @param, updated with number | ||||||
|  |  *		used elements | ||||||
|  |  * @param:	space for parameters for @func | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success or <0 on failure | ||||||
|  |  */ | ||||||
|  | int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, | ||||||
|  | 		    struct tee_param *param) | ||||||
|  | { | ||||||
|  | 	struct tee_device *teedev = ctx->teedev; | ||||||
|  | 	struct optee *optee = tee_get_drvdata(teedev); | ||||||
|  | 	struct optee_supp *supp = &optee->supp; | ||||||
|  | 	int 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); | ||||||
|  | 
 | ||||||
|  | 	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 (supp->req_posted) { | ||||||
|  | 			supp->ret = TEEC_ERROR_COMMUNICATION; | ||||||
|  | 			supp->supp_next_send = false; | ||||||
|  | 			complete(&supp->data_from_supp); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * 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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* We have exlusive access to the data */ | ||||||
|  | 
 | ||||||
|  | 	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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	*func = supp->func; | ||||||
|  | 	*num_params = supp->num_params; | ||||||
|  | 	memcpy(param, supp->param, | ||||||
|  | 	       sizeof(struct tee_param) * supp->num_params); | ||||||
|  | 
 | ||||||
|  | 	/* Allow optee_supp_send() below to do its work */ | ||||||
|  | 	supp->supp_next_send = true; | ||||||
|  | 
 | ||||||
|  | 	rc = 0; | ||||||
|  | out: | ||||||
|  | 	mutex_unlock(&supp->supp_mutex); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * optee_supp_send() - send result of request from supplicant | ||||||
|  |  * @ctx:	context sending result | ||||||
|  |  * @ret:	return value of request | ||||||
|  |  * @num_params:	number of parameters returned | ||||||
|  |  * @param:	returned parameters | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success or <0 on failure. | ||||||
|  |  */ | ||||||
|  | int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, | ||||||
|  | 		    struct tee_param *param) | ||||||
|  | { | ||||||
|  | 	struct tee_device *teedev = ctx->teedev; | ||||||
|  | 	struct optee *optee = tee_get_drvdata(teedev); | ||||||
|  | 	struct optee_supp *supp = &optee->supp; | ||||||
|  | 	size_t n; | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * We still have exclusive access to the data since that's how we | ||||||
|  | 	 * left it when returning from optee_supp_read(). | ||||||
|  | 	 */ | ||||||
|  | 
 | ||||||
|  | 	/* See comment on mutex in optee_supp_read() above */ | ||||||
|  | 	mutex_lock(&supp->supp_mutex); | ||||||
|  | 
 | ||||||
|  | 	if (!supp->supp_next_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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Update out and in/out parameters */ | ||||||
|  | 	for (n = 0; n < num_params; n++) { | ||||||
|  | 		struct tee_param *p = supp->param + n; | ||||||
|  | 
 | ||||||
|  | 		switch (p->attr) { | ||||||
|  | 		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; | ||||||
|  | 			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; | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	supp->ret = ret; | ||||||
|  | 
 | ||||||
|  | 	/* Allow optee_supp_recv() above to do its work */ | ||||||
|  | 	supp->supp_next_send = false; | ||||||
|  | 
 | ||||||
|  | 	/* Let the requesting thread continue */ | ||||||
|  | 	complete(&supp->data_from_supp); | ||||||
|  | out: | ||||||
|  | 	mutex_unlock(&supp->supp_mutex); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jens Wiklander
						Jens Wiklander