| /* | 
 |  * 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; | 
 | } |