162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015, Linaro Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/device.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/uaccess.h>
862306a36Sopenharmony_ci#include "optee_private.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistruct optee_supp_req {
1162306a36Sopenharmony_ci	struct list_head link;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	bool in_queue;
1462306a36Sopenharmony_ci	u32 func;
1562306a36Sopenharmony_ci	u32 ret;
1662306a36Sopenharmony_ci	size_t num_params;
1762306a36Sopenharmony_ci	struct tee_param *param;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	struct completion c;
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_civoid optee_supp_init(struct optee_supp *supp)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	memset(supp, 0, sizeof(*supp));
2562306a36Sopenharmony_ci	mutex_init(&supp->mutex);
2662306a36Sopenharmony_ci	init_completion(&supp->reqs_c);
2762306a36Sopenharmony_ci	idr_init(&supp->idr);
2862306a36Sopenharmony_ci	INIT_LIST_HEAD(&supp->reqs);
2962306a36Sopenharmony_ci	supp->req_id = -1;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_civoid optee_supp_uninit(struct optee_supp *supp)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	mutex_destroy(&supp->mutex);
3562306a36Sopenharmony_ci	idr_destroy(&supp->idr);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid optee_supp_release(struct optee_supp *supp)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	int id;
4162306a36Sopenharmony_ci	struct optee_supp_req *req;
4262306a36Sopenharmony_ci	struct optee_supp_req *req_tmp;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	mutex_lock(&supp->mutex);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* Abort all request retrieved by supplicant */
4762306a36Sopenharmony_ci	idr_for_each_entry(&supp->idr, req, id) {
4862306a36Sopenharmony_ci		idr_remove(&supp->idr, id);
4962306a36Sopenharmony_ci		req->ret = TEEC_ERROR_COMMUNICATION;
5062306a36Sopenharmony_ci		complete(&req->c);
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* Abort all queued requests */
5462306a36Sopenharmony_ci	list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) {
5562306a36Sopenharmony_ci		list_del(&req->link);
5662306a36Sopenharmony_ci		req->in_queue = false;
5762306a36Sopenharmony_ci		req->ret = TEEC_ERROR_COMMUNICATION;
5862306a36Sopenharmony_ci		complete(&req->c);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	supp->ctx = NULL;
6262306a36Sopenharmony_ci	supp->req_id = -1;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mutex_unlock(&supp->mutex);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/**
6862306a36Sopenharmony_ci * optee_supp_thrd_req() - request service from supplicant
6962306a36Sopenharmony_ci * @ctx:	context doing the request
7062306a36Sopenharmony_ci * @func:	function requested
7162306a36Sopenharmony_ci * @num_params:	number of elements in @param array
7262306a36Sopenharmony_ci * @param:	parameters for function
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * Returns result of operation to be passed to secure world
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ciu32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
7762306a36Sopenharmony_ci			struct tee_param *param)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(ctx->teedev);
8162306a36Sopenharmony_ci	struct optee_supp *supp = &optee->supp;
8262306a36Sopenharmony_ci	struct optee_supp_req *req;
8362306a36Sopenharmony_ci	bool interruptable;
8462306a36Sopenharmony_ci	u32 ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/*
8762306a36Sopenharmony_ci	 * Return in case there is no supplicant available and
8862306a36Sopenharmony_ci	 * non-blocking request.
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	if (!supp->ctx && ctx->supp_nowait)
9162306a36Sopenharmony_ci		return TEEC_ERROR_COMMUNICATION;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
9462306a36Sopenharmony_ci	if (!req)
9562306a36Sopenharmony_ci		return TEEC_ERROR_OUT_OF_MEMORY;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	init_completion(&req->c);
9862306a36Sopenharmony_ci	req->func = func;
9962306a36Sopenharmony_ci	req->num_params = num_params;
10062306a36Sopenharmony_ci	req->param = param;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Insert the request in the request list */
10362306a36Sopenharmony_ci	mutex_lock(&supp->mutex);
10462306a36Sopenharmony_ci	list_add_tail(&req->link, &supp->reqs);
10562306a36Sopenharmony_ci	req->in_queue = true;
10662306a36Sopenharmony_ci	mutex_unlock(&supp->mutex);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Tell an eventual waiter there's a new request */
10962306a36Sopenharmony_ci	complete(&supp->reqs_c);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/*
11262306a36Sopenharmony_ci	 * Wait for supplicant to process and return result, once we've
11362306a36Sopenharmony_ci	 * returned from wait_for_completion(&req->c) successfully we have
11462306a36Sopenharmony_ci	 * exclusive access again.
11562306a36Sopenharmony_ci	 */
11662306a36Sopenharmony_ci	while (wait_for_completion_interruptible(&req->c)) {
11762306a36Sopenharmony_ci		mutex_lock(&supp->mutex);
11862306a36Sopenharmony_ci		interruptable = !supp->ctx;
11962306a36Sopenharmony_ci		if (interruptable) {
12062306a36Sopenharmony_ci			/*
12162306a36Sopenharmony_ci			 * There's no supplicant available and since the
12262306a36Sopenharmony_ci			 * supp->mutex currently is held none can
12362306a36Sopenharmony_ci			 * become available until the mutex released
12462306a36Sopenharmony_ci			 * again.
12562306a36Sopenharmony_ci			 *
12662306a36Sopenharmony_ci			 * Interrupting an RPC to supplicant is only
12762306a36Sopenharmony_ci			 * allowed as a way of slightly improving the user
12862306a36Sopenharmony_ci			 * experience in case the supplicant hasn't been
12962306a36Sopenharmony_ci			 * started yet. During normal operation the supplicant
13062306a36Sopenharmony_ci			 * will serve all requests in a timely manner and
13162306a36Sopenharmony_ci			 * interrupting then wouldn't make sense.
13262306a36Sopenharmony_ci			 */
13362306a36Sopenharmony_ci			if (req->in_queue) {
13462306a36Sopenharmony_ci				list_del(&req->link);
13562306a36Sopenharmony_ci				req->in_queue = false;
13662306a36Sopenharmony_ci			}
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci		mutex_unlock(&supp->mutex);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		if (interruptable) {
14162306a36Sopenharmony_ci			req->ret = TEEC_ERROR_COMMUNICATION;
14262306a36Sopenharmony_ci			break;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	ret = req->ret;
14762306a36Sopenharmony_ci	kfree(req);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return ret;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic struct optee_supp_req  *supp_pop_entry(struct optee_supp *supp,
15362306a36Sopenharmony_ci					      int num_params, int *id)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct optee_supp_req *req;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (supp->req_id != -1) {
15862306a36Sopenharmony_ci		/*
15962306a36Sopenharmony_ci		 * Supplicant should not mix synchronous and asnynchronous
16062306a36Sopenharmony_ci		 * requests.
16162306a36Sopenharmony_ci		 */
16262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (list_empty(&supp->reqs))
16662306a36Sopenharmony_ci		return NULL;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	req = list_first_entry(&supp->reqs, struct optee_supp_req, link);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (num_params < req->num_params) {
17162306a36Sopenharmony_ci		/* Not enough room for parameters */
17262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	*id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL);
17662306a36Sopenharmony_ci	if (*id < 0)
17762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	list_del(&req->link);
18062306a36Sopenharmony_ci	req->in_queue = false;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return req;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int supp_check_recv_params(size_t num_params, struct tee_param *params,
18662306a36Sopenharmony_ci				  size_t *num_meta)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	size_t n;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!num_params)
19162306a36Sopenharmony_ci		return -EINVAL;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	 * If there's memrefs we need to decrease those as they where
19562306a36Sopenharmony_ci	 * increased earlier and we'll even refuse to accept any below.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	for (n = 0; n < num_params; n++)
19862306a36Sopenharmony_ci		if (tee_param_is_memref(params + n) && params[n].u.memref.shm)
19962306a36Sopenharmony_ci			tee_shm_put(params[n].u.memref.shm);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * We only expect parameters as TEE_IOCTL_PARAM_ATTR_TYPE_NONE with
20362306a36Sopenharmony_ci	 * or without the TEE_IOCTL_PARAM_ATTR_META bit set.
20462306a36Sopenharmony_ci	 */
20562306a36Sopenharmony_ci	for (n = 0; n < num_params; n++)
20662306a36Sopenharmony_ci		if (params[n].attr &&
20762306a36Sopenharmony_ci		    params[n].attr != TEE_IOCTL_PARAM_ATTR_META)
20862306a36Sopenharmony_ci			return -EINVAL;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* At most we'll need one meta parameter so no need to check for more */
21162306a36Sopenharmony_ci	if (params->attr == TEE_IOCTL_PARAM_ATTR_META)
21262306a36Sopenharmony_ci		*num_meta = 1;
21362306a36Sopenharmony_ci	else
21462306a36Sopenharmony_ci		*num_meta = 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/**
22062306a36Sopenharmony_ci * optee_supp_recv() - receive request for supplicant
22162306a36Sopenharmony_ci * @ctx:	context receiving the request
22262306a36Sopenharmony_ci * @func:	requested function in supplicant
22362306a36Sopenharmony_ci * @num_params:	number of elements allocated in @param, updated with number
22462306a36Sopenharmony_ci *		used elements
22562306a36Sopenharmony_ci * @param:	space for parameters for @func
22662306a36Sopenharmony_ci *
22762306a36Sopenharmony_ci * Returns 0 on success or <0 on failure
22862306a36Sopenharmony_ci */
22962306a36Sopenharmony_ciint optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
23062306a36Sopenharmony_ci		    struct tee_param *param)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct tee_device *teedev = ctx->teedev;
23362306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(teedev);
23462306a36Sopenharmony_ci	struct optee_supp *supp = &optee->supp;
23562306a36Sopenharmony_ci	struct optee_supp_req *req = NULL;
23662306a36Sopenharmony_ci	int id;
23762306a36Sopenharmony_ci	size_t num_meta;
23862306a36Sopenharmony_ci	int rc;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	rc = supp_check_recv_params(*num_params, param, &num_meta);
24162306a36Sopenharmony_ci	if (rc)
24262306a36Sopenharmony_ci		return rc;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	while (true) {
24562306a36Sopenharmony_ci		mutex_lock(&supp->mutex);
24662306a36Sopenharmony_ci		req = supp_pop_entry(supp, *num_params - num_meta, &id);
24762306a36Sopenharmony_ci		mutex_unlock(&supp->mutex);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		if (req) {
25062306a36Sopenharmony_ci			if (IS_ERR(req))
25162306a36Sopenharmony_ci				return PTR_ERR(req);
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		/*
25662306a36Sopenharmony_ci		 * If we didn't get a request we'll block in
25762306a36Sopenharmony_ci		 * wait_for_completion() to avoid needless spinning.
25862306a36Sopenharmony_ci		 *
25962306a36Sopenharmony_ci		 * This is where supplicant will be hanging most of
26062306a36Sopenharmony_ci		 * the time, let's make this interruptable so we
26162306a36Sopenharmony_ci		 * can easily restart supplicant if needed.
26262306a36Sopenharmony_ci		 */
26362306a36Sopenharmony_ci		if (wait_for_completion_interruptible(&supp->reqs_c))
26462306a36Sopenharmony_ci			return -ERESTARTSYS;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (num_meta) {
26862306a36Sopenharmony_ci		/*
26962306a36Sopenharmony_ci		 * tee-supplicant support meta parameters -> requsts can be
27062306a36Sopenharmony_ci		 * processed asynchronously.
27162306a36Sopenharmony_ci		 */
27262306a36Sopenharmony_ci		param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
27362306a36Sopenharmony_ci			      TEE_IOCTL_PARAM_ATTR_META;
27462306a36Sopenharmony_ci		param->u.value.a = id;
27562306a36Sopenharmony_ci		param->u.value.b = 0;
27662306a36Sopenharmony_ci		param->u.value.c = 0;
27762306a36Sopenharmony_ci	} else {
27862306a36Sopenharmony_ci		mutex_lock(&supp->mutex);
27962306a36Sopenharmony_ci		supp->req_id = id;
28062306a36Sopenharmony_ci		mutex_unlock(&supp->mutex);
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	*func = req->func;
28462306a36Sopenharmony_ci	*num_params = req->num_params + num_meta;
28562306a36Sopenharmony_ci	memcpy(param + num_meta, req->param,
28662306a36Sopenharmony_ci	       sizeof(struct tee_param) * req->num_params);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic struct optee_supp_req *supp_pop_req(struct optee_supp *supp,
29262306a36Sopenharmony_ci					   size_t num_params,
29362306a36Sopenharmony_ci					   struct tee_param *param,
29462306a36Sopenharmony_ci					   size_t *num_meta)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct optee_supp_req *req;
29762306a36Sopenharmony_ci	int id;
29862306a36Sopenharmony_ci	size_t nm;
29962306a36Sopenharmony_ci	const u32 attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
30062306a36Sopenharmony_ci			 TEE_IOCTL_PARAM_ATTR_META;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (!num_params)
30362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (supp->req_id == -1) {
30662306a36Sopenharmony_ci		if (param->attr != attr)
30762306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
30862306a36Sopenharmony_ci		id = param->u.value.a;
30962306a36Sopenharmony_ci		nm = 1;
31062306a36Sopenharmony_ci	} else {
31162306a36Sopenharmony_ci		id = supp->req_id;
31262306a36Sopenharmony_ci		nm = 0;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	req = idr_find(&supp->idr, id);
31662306a36Sopenharmony_ci	if (!req)
31762306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if ((num_params - nm) != req->num_params)
32062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	idr_remove(&supp->idr, id);
32362306a36Sopenharmony_ci	supp->req_id = -1;
32462306a36Sopenharmony_ci	*num_meta = nm;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return req;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/**
33062306a36Sopenharmony_ci * optee_supp_send() - send result of request from supplicant
33162306a36Sopenharmony_ci * @ctx:	context sending result
33262306a36Sopenharmony_ci * @ret:	return value of request
33362306a36Sopenharmony_ci * @num_params:	number of parameters returned
33462306a36Sopenharmony_ci * @param:	returned parameters
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * Returns 0 on success or <0 on failure.
33762306a36Sopenharmony_ci */
33862306a36Sopenharmony_ciint optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
33962306a36Sopenharmony_ci		    struct tee_param *param)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct tee_device *teedev = ctx->teedev;
34262306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(teedev);
34362306a36Sopenharmony_ci	struct optee_supp *supp = &optee->supp;
34462306a36Sopenharmony_ci	struct optee_supp_req *req;
34562306a36Sopenharmony_ci	size_t n;
34662306a36Sopenharmony_ci	size_t num_meta;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	mutex_lock(&supp->mutex);
34962306a36Sopenharmony_ci	req = supp_pop_req(supp, num_params, param, &num_meta);
35062306a36Sopenharmony_ci	mutex_unlock(&supp->mutex);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (IS_ERR(req)) {
35362306a36Sopenharmony_ci		/* Something is wrong, let supplicant restart. */
35462306a36Sopenharmony_ci		return PTR_ERR(req);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* Update out and in/out parameters */
35862306a36Sopenharmony_ci	for (n = 0; n < req->num_params; n++) {
35962306a36Sopenharmony_ci		struct tee_param *p = req->param + n;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
36262306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
36362306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
36462306a36Sopenharmony_ci			p->u.value.a = param[n + num_meta].u.value.a;
36562306a36Sopenharmony_ci			p->u.value.b = param[n + num_meta].u.value.b;
36662306a36Sopenharmony_ci			p->u.value.c = param[n + num_meta].u.value.c;
36762306a36Sopenharmony_ci			break;
36862306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
36962306a36Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
37062306a36Sopenharmony_ci			p->u.memref.size = param[n + num_meta].u.memref.size;
37162306a36Sopenharmony_ci			break;
37262306a36Sopenharmony_ci		default:
37362306a36Sopenharmony_ci			break;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci	req->ret = ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* Let the requesting thread continue */
37962306a36Sopenharmony_ci	complete(&req->c);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
383