Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

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