162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015-2021, Linaro Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/i2c.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/tee_drv.h>
1262306a36Sopenharmony_ci#include "optee_private.h"
1362306a36Sopenharmony_ci#include "optee_rpc_cmd.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct timespec64 ts;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	if (arg->num_params != 1)
2062306a36Sopenharmony_ci		goto bad;
2162306a36Sopenharmony_ci	if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) !=
2262306a36Sopenharmony_ci			OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT)
2362306a36Sopenharmony_ci		goto bad;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	ktime_get_real_ts64(&ts);
2662306a36Sopenharmony_ci	arg->params[0].u.value.a = ts.tv_sec;
2762306a36Sopenharmony_ci	arg->params[0].u.value.b = ts.tv_nsec;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	arg->ret = TEEC_SUCCESS;
3062306a36Sopenharmony_ci	return;
3162306a36Sopenharmony_cibad:
3262306a36Sopenharmony_ci	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_I2C)
3662306a36Sopenharmony_cistatic void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx,
3762306a36Sopenharmony_ci					     struct optee_msg_arg *arg)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(ctx->teedev);
4062306a36Sopenharmony_ci	struct tee_param *params;
4162306a36Sopenharmony_ci	struct i2c_adapter *adapter;
4262306a36Sopenharmony_ci	struct i2c_msg msg = { };
4362306a36Sopenharmony_ci	size_t i;
4462306a36Sopenharmony_ci	int ret = -EOPNOTSUPP;
4562306a36Sopenharmony_ci	u8 attr[] = {
4662306a36Sopenharmony_ci		TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
4762306a36Sopenharmony_ci		TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
4862306a36Sopenharmony_ci		TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
4962306a36Sopenharmony_ci		TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT,
5062306a36Sopenharmony_ci	};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (arg->num_params != ARRAY_SIZE(attr)) {
5362306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	params = kmalloc_array(arg->num_params, sizeof(struct tee_param),
5862306a36Sopenharmony_ci			       GFP_KERNEL);
5962306a36Sopenharmony_ci	if (!params) {
6062306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
6162306a36Sopenharmony_ci		return;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (optee->ops->from_msg_param(optee, params, arg->num_params,
6562306a36Sopenharmony_ci				       arg->params))
6662306a36Sopenharmony_ci		goto bad;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	for (i = 0; i < arg->num_params; i++) {
6962306a36Sopenharmony_ci		if (params[i].attr != attr[i])
7062306a36Sopenharmony_ci			goto bad;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	adapter = i2c_get_adapter(params[0].u.value.b);
7462306a36Sopenharmony_ci	if (!adapter)
7562306a36Sopenharmony_ci		goto bad;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (params[1].u.value.a & OPTEE_RPC_I2C_FLAGS_TEN_BIT) {
7862306a36Sopenharmony_ci		if (!i2c_check_functionality(adapter,
7962306a36Sopenharmony_ci					     I2C_FUNC_10BIT_ADDR)) {
8062306a36Sopenharmony_ci			i2c_put_adapter(adapter);
8162306a36Sopenharmony_ci			goto bad;
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		msg.flags = I2C_M_TEN;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	msg.addr = params[0].u.value.c;
8862306a36Sopenharmony_ci	msg.buf  = params[2].u.memref.shm->kaddr;
8962306a36Sopenharmony_ci	msg.len  = params[2].u.memref.size;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	switch (params[0].u.value.a) {
9262306a36Sopenharmony_ci	case OPTEE_RPC_I2C_TRANSFER_RD:
9362306a36Sopenharmony_ci		msg.flags |= I2C_M_RD;
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci	case OPTEE_RPC_I2C_TRANSFER_WR:
9662306a36Sopenharmony_ci		break;
9762306a36Sopenharmony_ci	default:
9862306a36Sopenharmony_ci		i2c_put_adapter(adapter);
9962306a36Sopenharmony_ci		goto bad;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	ret = i2c_transfer(adapter, &msg, 1);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (ret < 0) {
10562306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_COMMUNICATION;
10662306a36Sopenharmony_ci	} else {
10762306a36Sopenharmony_ci		params[3].u.value.a = msg.len;
10862306a36Sopenharmony_ci		if (optee->ops->to_msg_param(optee, arg->params,
10962306a36Sopenharmony_ci					     arg->num_params, params))
11062306a36Sopenharmony_ci			arg->ret = TEEC_ERROR_BAD_PARAMETERS;
11162306a36Sopenharmony_ci		else
11262306a36Sopenharmony_ci			arg->ret = TEEC_SUCCESS;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	i2c_put_adapter(adapter);
11662306a36Sopenharmony_ci	kfree(params);
11762306a36Sopenharmony_ci	return;
11862306a36Sopenharmony_cibad:
11962306a36Sopenharmony_ci	kfree(params);
12062306a36Sopenharmony_ci	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci#else
12362306a36Sopenharmony_cistatic void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx,
12462306a36Sopenharmony_ci					     struct optee_msg_arg *arg)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	arg->ret = TEEC_ERROR_NOT_SUPPORTED;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci#endif
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void handle_rpc_func_cmd_wq(struct optee *optee,
13162306a36Sopenharmony_ci				   struct optee_msg_arg *arg)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	if (arg->num_params != 1)
13462306a36Sopenharmony_ci		goto bad;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) !=
13762306a36Sopenharmony_ci			OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
13862306a36Sopenharmony_ci		goto bad;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	switch (arg->params[0].u.value.a) {
14162306a36Sopenharmony_ci	case OPTEE_RPC_NOTIFICATION_WAIT:
14262306a36Sopenharmony_ci		if (optee_notif_wait(optee, arg->params[0].u.value.b))
14362306a36Sopenharmony_ci			goto bad;
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	case OPTEE_RPC_NOTIFICATION_SEND:
14662306a36Sopenharmony_ci		if (optee_notif_send(optee, arg->params[0].u.value.b))
14762306a36Sopenharmony_ci			goto bad;
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	default:
15062306a36Sopenharmony_ci		goto bad;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	arg->ret = TEEC_SUCCESS;
15462306a36Sopenharmony_ci	return;
15562306a36Sopenharmony_cibad:
15662306a36Sopenharmony_ci	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	u32 msec_to_wait;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (arg->num_params != 1)
16462306a36Sopenharmony_ci		goto bad;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) !=
16762306a36Sopenharmony_ci			OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
16862306a36Sopenharmony_ci		goto bad;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	msec_to_wait = arg->params[0].u.value.a;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Go to interruptible sleep */
17362306a36Sopenharmony_ci	msleep_interruptible(msec_to_wait);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	arg->ret = TEEC_SUCCESS;
17662306a36Sopenharmony_ci	return;
17762306a36Sopenharmony_cibad:
17862306a36Sopenharmony_ci	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void handle_rpc_supp_cmd(struct tee_context *ctx, struct optee *optee,
18262306a36Sopenharmony_ci				struct optee_msg_arg *arg)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct tee_param *params;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	arg->ret_origin = TEEC_ORIGIN_COMMS;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	params = kmalloc_array(arg->num_params, sizeof(struct tee_param),
18962306a36Sopenharmony_ci			       GFP_KERNEL);
19062306a36Sopenharmony_ci	if (!params) {
19162306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
19262306a36Sopenharmony_ci		return;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (optee->ops->from_msg_param(optee, params, arg->num_params,
19662306a36Sopenharmony_ci				       arg->params)) {
19762306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
19862306a36Sopenharmony_ci		goto out;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (optee->ops->to_msg_param(optee, arg->params, arg->num_params,
20462306a36Sopenharmony_ci				     params))
20562306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
20662306a36Sopenharmony_ciout:
20762306a36Sopenharmony_ci	kfree(params);
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistruct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	u32 ret;
21362306a36Sopenharmony_ci	struct tee_param param;
21462306a36Sopenharmony_ci	struct optee *optee = tee_get_drvdata(ctx->teedev);
21562306a36Sopenharmony_ci	struct tee_shm *shm;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
21862306a36Sopenharmony_ci	param.u.value.a = OPTEE_RPC_SHM_TYPE_APPL;
21962306a36Sopenharmony_ci	param.u.value.b = sz;
22062306a36Sopenharmony_ci	param.u.value.c = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	ret = optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_ALLOC, 1, &param);
22362306a36Sopenharmony_ci	if (ret)
22462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	mutex_lock(&optee->supp.mutex);
22762306a36Sopenharmony_ci	/* Increases count as secure world doesn't have a reference */
22862306a36Sopenharmony_ci	shm = tee_shm_get_from_id(optee->supp.ctx, param.u.value.c);
22962306a36Sopenharmony_ci	mutex_unlock(&optee->supp.mutex);
23062306a36Sopenharmony_ci	return shm;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_civoid optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct tee_param param;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
23862306a36Sopenharmony_ci	param.u.value.a = OPTEE_RPC_SHM_TYPE_APPL;
23962306a36Sopenharmony_ci	param.u.value.b = tee_shm_get_id(shm);
24062306a36Sopenharmony_ci	param.u.value.c = 0;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/*
24362306a36Sopenharmony_ci	 * Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure
24462306a36Sopenharmony_ci	 * world has released its reference.
24562306a36Sopenharmony_ci	 *
24662306a36Sopenharmony_ci	 * It's better to do this before sending the request to supplicant
24762306a36Sopenharmony_ci	 * as we'd like to let the process doing the initial allocation to
24862306a36Sopenharmony_ci	 * do release the last reference too in order to avoid stacking
24962306a36Sopenharmony_ci	 * many pending fput() on the client process. This could otherwise
25062306a36Sopenharmony_ci	 * happen if secure world does many allocate and free in a single
25162306a36Sopenharmony_ci	 * invoke.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	tee_shm_put(shm);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, &param);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_civoid optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
25962306a36Sopenharmony_ci		   struct optee_msg_arg *arg)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	switch (arg->cmd) {
26262306a36Sopenharmony_ci	case OPTEE_RPC_CMD_GET_TIME:
26362306a36Sopenharmony_ci		handle_rpc_func_cmd_get_time(arg);
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	case OPTEE_RPC_CMD_NOTIFICATION:
26662306a36Sopenharmony_ci		handle_rpc_func_cmd_wq(optee, arg);
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci	case OPTEE_RPC_CMD_SUSPEND:
26962306a36Sopenharmony_ci		handle_rpc_func_cmd_wait(arg);
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	case OPTEE_RPC_CMD_I2C_TRANSFER:
27262306a36Sopenharmony_ci		handle_rpc_func_cmd_i2c_transfer(ctx, arg);
27362306a36Sopenharmony_ci		break;
27462306a36Sopenharmony_ci	default:
27562306a36Sopenharmony_ci		handle_rpc_supp_cmd(ctx, optee, arg);
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci
280