162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2019 Advanced Micro Devices, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/device.h>
762306a36Sopenharmony_ci#include <linux/tee.h>
862306a36Sopenharmony_ci#include <linux/tee_drv.h>
962306a36Sopenharmony_ci#include <linux/psp-tee.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/psp.h>
1262306a36Sopenharmony_ci#include "amdtee_if.h"
1362306a36Sopenharmony_ci#include "amdtee_private.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic int tee_params_to_amd_params(struct tee_param *tee, u32 count,
1662306a36Sopenharmony_ci				    struct tee_operation *amd)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	int i, ret = 0;
1962306a36Sopenharmony_ci	u32 type;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	if (!count)
2262306a36Sopenharmony_ci		return 0;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (!tee || !amd || count > TEE_MAX_PARAMS)
2562306a36Sopenharmony_ci		return -EINVAL;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	amd->param_types = 0;
2862306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
2962306a36Sopenharmony_ci		/* AMD TEE does not support meta parameter */
3062306a36Sopenharmony_ci		if (tee[i].attr > TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT)
3162306a36Sopenharmony_ci			return -EINVAL;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		amd->param_types |= ((tee[i].attr & 0xF) << i * 4);
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
3762306a36Sopenharmony_ci		type = TEE_PARAM_TYPE_GET(amd->param_types, i);
3862306a36Sopenharmony_ci		pr_debug("%s: type[%d] = 0x%x\n", __func__, i, type);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		if (type == TEE_OP_PARAM_TYPE_INVALID)
4162306a36Sopenharmony_ci			return -EINVAL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci		if (type == TEE_OP_PARAM_TYPE_NONE)
4462306a36Sopenharmony_ci			continue;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci		/* It is assumed that all values are within 2^32-1 */
4762306a36Sopenharmony_ci		if (type > TEE_OP_PARAM_TYPE_VALUE_INOUT) {
4862306a36Sopenharmony_ci			u32 buf_id = get_buffer_id(tee[i].u.memref.shm);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci			amd->params[i].mref.buf_id = buf_id;
5162306a36Sopenharmony_ci			amd->params[i].mref.offset = tee[i].u.memref.shm_offs;
5262306a36Sopenharmony_ci			amd->params[i].mref.size = tee[i].u.memref.size;
5362306a36Sopenharmony_ci			pr_debug("%s: bufid[%d] = 0x%x, offset[%d] = 0x%x, size[%d] = 0x%x\n",
5462306a36Sopenharmony_ci				 __func__,
5562306a36Sopenharmony_ci				 i, amd->params[i].mref.buf_id,
5662306a36Sopenharmony_ci				 i, amd->params[i].mref.offset,
5762306a36Sopenharmony_ci				 i, amd->params[i].mref.size);
5862306a36Sopenharmony_ci		} else {
5962306a36Sopenharmony_ci			if (tee[i].u.value.c)
6062306a36Sopenharmony_ci				pr_warn("%s: Discarding value c", __func__);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci			amd->params[i].val.a = tee[i].u.value.a;
6362306a36Sopenharmony_ci			amd->params[i].val.b = tee[i].u.value.b;
6462306a36Sopenharmony_ci			pr_debug("%s: a[%d] = 0x%x, b[%d] = 0x%x\n", __func__,
6562306a36Sopenharmony_ci				 i, amd->params[i].val.a,
6662306a36Sopenharmony_ci				 i, amd->params[i].val.b);
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	return ret;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int amd_params_to_tee_params(struct tee_param *tee, u32 count,
7362306a36Sopenharmony_ci				    struct tee_operation *amd)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	int i, ret = 0;
7662306a36Sopenharmony_ci	u32 type;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!count)
7962306a36Sopenharmony_ci		return 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (!tee || !amd || count > TEE_MAX_PARAMS)
8262306a36Sopenharmony_ci		return -EINVAL;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Assumes amd->param_types is valid */
8562306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
8662306a36Sopenharmony_ci		type = TEE_PARAM_TYPE_GET(amd->param_types, i);
8762306a36Sopenharmony_ci		pr_debug("%s: type[%d] = 0x%x\n", __func__, i, type);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		if (type == TEE_OP_PARAM_TYPE_INVALID ||
9062306a36Sopenharmony_ci		    type > TEE_OP_PARAM_TYPE_MEMREF_INOUT)
9162306a36Sopenharmony_ci			return -EINVAL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		if (type == TEE_OP_PARAM_TYPE_NONE ||
9462306a36Sopenharmony_ci		    type == TEE_OP_PARAM_TYPE_VALUE_INPUT ||
9562306a36Sopenharmony_ci		    type == TEE_OP_PARAM_TYPE_MEMREF_INPUT)
9662306a36Sopenharmony_ci			continue;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		/*
9962306a36Sopenharmony_ci		 * It is assumed that buf_id remains unchanged for
10062306a36Sopenharmony_ci		 * both open_session and invoke_cmd call
10162306a36Sopenharmony_ci		 */
10262306a36Sopenharmony_ci		if (type > TEE_OP_PARAM_TYPE_MEMREF_INPUT) {
10362306a36Sopenharmony_ci			tee[i].u.memref.shm_offs = amd->params[i].mref.offset;
10462306a36Sopenharmony_ci			tee[i].u.memref.size = amd->params[i].mref.size;
10562306a36Sopenharmony_ci			pr_debug("%s: bufid[%d] = 0x%x, offset[%d] = 0x%x, size[%d] = 0x%x\n",
10662306a36Sopenharmony_ci				 __func__,
10762306a36Sopenharmony_ci				 i, amd->params[i].mref.buf_id,
10862306a36Sopenharmony_ci				 i, amd->params[i].mref.offset,
10962306a36Sopenharmony_ci				 i, amd->params[i].mref.size);
11062306a36Sopenharmony_ci		} else {
11162306a36Sopenharmony_ci			/* field 'c' not supported by AMD TEE */
11262306a36Sopenharmony_ci			tee[i].u.value.a = amd->params[i].val.a;
11362306a36Sopenharmony_ci			tee[i].u.value.b = amd->params[i].val.b;
11462306a36Sopenharmony_ci			tee[i].u.value.c = 0;
11562306a36Sopenharmony_ci			pr_debug("%s: a[%d] = 0x%x, b[%d] = 0x%x\n",
11662306a36Sopenharmony_ci				 __func__,
11762306a36Sopenharmony_ci				 i, amd->params[i].val.a,
11862306a36Sopenharmony_ci				 i, amd->params[i].val.b);
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	return ret;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic DEFINE_MUTEX(ta_refcount_mutex);
12562306a36Sopenharmony_cistatic LIST_HEAD(ta_list);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic u32 get_ta_refcount(u32 ta_handle)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct amdtee_ta_data *ta_data;
13062306a36Sopenharmony_ci	u32 count = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Caller must hold a mutex */
13362306a36Sopenharmony_ci	list_for_each_entry(ta_data, &ta_list, list_node)
13462306a36Sopenharmony_ci		if (ta_data->ta_handle == ta_handle)
13562306a36Sopenharmony_ci			return ++ta_data->refcount;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ta_data = kzalloc(sizeof(*ta_data), GFP_KERNEL);
13862306a36Sopenharmony_ci	if (ta_data) {
13962306a36Sopenharmony_ci		ta_data->ta_handle = ta_handle;
14062306a36Sopenharmony_ci		ta_data->refcount = 1;
14162306a36Sopenharmony_ci		count = ta_data->refcount;
14262306a36Sopenharmony_ci		list_add(&ta_data->list_node, &ta_list);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return count;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic u32 put_ta_refcount(u32 ta_handle)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct amdtee_ta_data *ta_data;
15162306a36Sopenharmony_ci	u32 count = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* Caller must hold a mutex */
15462306a36Sopenharmony_ci	list_for_each_entry(ta_data, &ta_list, list_node)
15562306a36Sopenharmony_ci		if (ta_data->ta_handle == ta_handle) {
15662306a36Sopenharmony_ci			count = --ta_data->refcount;
15762306a36Sopenharmony_ci			if (count == 0) {
15862306a36Sopenharmony_ci				list_del(&ta_data->list_node);
15962306a36Sopenharmony_ci				kfree(ta_data);
16062306a36Sopenharmony_ci				break;
16162306a36Sopenharmony_ci			}
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return count;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ciint handle_unload_ta(u32 ta_handle)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct tee_cmd_unload_ta cmd = {0};
17062306a36Sopenharmony_ci	u32 status, count;
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!ta_handle)
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	mutex_lock(&ta_refcount_mutex);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	count = put_ta_refcount(ta_handle);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (count) {
18162306a36Sopenharmony_ci		pr_debug("unload ta: not unloading %u count %u\n",
18262306a36Sopenharmony_ci			 ta_handle, count);
18362306a36Sopenharmony_ci		ret = -EBUSY;
18462306a36Sopenharmony_ci		goto unlock;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	cmd.ta_handle = ta_handle;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	ret = psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA, (void *)&cmd,
19062306a36Sopenharmony_ci				  sizeof(cmd), &status);
19162306a36Sopenharmony_ci	if (!ret && status != 0) {
19262306a36Sopenharmony_ci		pr_err("unload ta: status = 0x%x\n", status);
19362306a36Sopenharmony_ci		ret = -EBUSY;
19462306a36Sopenharmony_ci	} else {
19562306a36Sopenharmony_ci		pr_debug("unloaded ta handle %u\n", ta_handle);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciunlock:
19962306a36Sopenharmony_ci	mutex_unlock(&ta_refcount_mutex);
20062306a36Sopenharmony_ci	return ret;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ciint handle_close_session(u32 ta_handle, u32 info)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct tee_cmd_close_session cmd = {0};
20662306a36Sopenharmony_ci	u32 status;
20762306a36Sopenharmony_ci	int ret;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (ta_handle == 0)
21062306a36Sopenharmony_ci		return -EINVAL;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	cmd.ta_handle = ta_handle;
21362306a36Sopenharmony_ci	cmd.session_info = info;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ret = psp_tee_process_cmd(TEE_CMD_ID_CLOSE_SESSION, (void *)&cmd,
21662306a36Sopenharmony_ci				  sizeof(cmd), &status);
21762306a36Sopenharmony_ci	if (!ret && status != 0) {
21862306a36Sopenharmony_ci		pr_err("close session: status = 0x%x\n", status);
21962306a36Sopenharmony_ci		ret = -EBUSY;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return ret;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_civoid handle_unmap_shmem(u32 buf_id)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct tee_cmd_unmap_shared_mem cmd = {0};
22862306a36Sopenharmony_ci	u32 status;
22962306a36Sopenharmony_ci	int ret;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	cmd.buf_id = buf_id;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ret = psp_tee_process_cmd(TEE_CMD_ID_UNMAP_SHARED_MEM, (void *)&cmd,
23462306a36Sopenharmony_ci				  sizeof(cmd), &status);
23562306a36Sopenharmony_ci	if (!ret)
23662306a36Sopenharmony_ci		pr_debug("unmap shared memory: buf_id %u status = 0x%x\n",
23762306a36Sopenharmony_ci			 buf_id, status);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciint handle_invoke_cmd(struct tee_ioctl_invoke_arg *arg, u32 sinfo,
24162306a36Sopenharmony_ci		      struct tee_param *p)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct tee_cmd_invoke_cmd cmd = {0};
24462306a36Sopenharmony_ci	int ret;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (!arg || (!p && arg->num_params))
24762306a36Sopenharmony_ci		return -EINVAL;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	arg->ret_origin = TEEC_ORIGIN_COMMS;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (arg->session == 0) {
25262306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
25362306a36Sopenharmony_ci		return -EINVAL;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ret = tee_params_to_amd_params(p, arg->num_params, &cmd.op);
25762306a36Sopenharmony_ci	if (ret) {
25862306a36Sopenharmony_ci		pr_err("invalid Params. Abort invoke command\n");
25962306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
26062306a36Sopenharmony_ci		return ret;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	cmd.ta_handle = get_ta_handle(arg->session);
26462306a36Sopenharmony_ci	cmd.cmd_id = arg->func;
26562306a36Sopenharmony_ci	cmd.session_info = sinfo;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ret = psp_tee_process_cmd(TEE_CMD_ID_INVOKE_CMD, (void *)&cmd,
26862306a36Sopenharmony_ci				  sizeof(cmd), &arg->ret);
26962306a36Sopenharmony_ci	if (ret) {
27062306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_COMMUNICATION;
27162306a36Sopenharmony_ci	} else {
27262306a36Sopenharmony_ci		ret = amd_params_to_tee_params(p, arg->num_params, &cmd.op);
27362306a36Sopenharmony_ci		if (unlikely(ret)) {
27462306a36Sopenharmony_ci			pr_err("invoke command: failed to copy output\n");
27562306a36Sopenharmony_ci			arg->ret = TEEC_ERROR_GENERIC;
27662306a36Sopenharmony_ci			return ret;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci		arg->ret_origin = cmd.return_origin;
27962306a36Sopenharmony_ci		pr_debug("invoke command: RO = 0x%x ret = 0x%x\n",
28062306a36Sopenharmony_ci			 arg->ret_origin, arg->ret);
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return ret;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ciint handle_map_shmem(u32 count, struct shmem_desc *start, u32 *buf_id)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct tee_cmd_map_shared_mem *cmd;
28962306a36Sopenharmony_ci	phys_addr_t paddr;
29062306a36Sopenharmony_ci	int ret, i;
29162306a36Sopenharmony_ci	u32 status;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (!count || !start || !buf_id)
29462306a36Sopenharmony_ci		return -EINVAL;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
29762306a36Sopenharmony_ci	if (!cmd)
29862306a36Sopenharmony_ci		return -ENOMEM;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* Size must be page aligned */
30162306a36Sopenharmony_ci	for (i = 0; i < count ; i++) {
30262306a36Sopenharmony_ci		if (!start[i].kaddr || (start[i].size & (PAGE_SIZE - 1))) {
30362306a36Sopenharmony_ci			ret = -EINVAL;
30462306a36Sopenharmony_ci			goto free_cmd;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if ((u64)start[i].kaddr & (PAGE_SIZE - 1)) {
30862306a36Sopenharmony_ci			pr_err("map shared memory: page unaligned. addr 0x%llx",
30962306a36Sopenharmony_ci			       (u64)start[i].kaddr);
31062306a36Sopenharmony_ci			ret = -EINVAL;
31162306a36Sopenharmony_ci			goto free_cmd;
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	cmd->sg_list.count = count;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* Create buffer list */
31862306a36Sopenharmony_ci	for (i = 0; i < count ; i++) {
31962306a36Sopenharmony_ci		paddr = __psp_pa(start[i].kaddr);
32062306a36Sopenharmony_ci		cmd->sg_list.buf[i].hi_addr = upper_32_bits(paddr);
32162306a36Sopenharmony_ci		cmd->sg_list.buf[i].low_addr = lower_32_bits(paddr);
32262306a36Sopenharmony_ci		cmd->sg_list.buf[i].size = start[i].size;
32362306a36Sopenharmony_ci		cmd->sg_list.size += cmd->sg_list.buf[i].size;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		pr_debug("buf[%d]:hi addr = 0x%x\n", i,
32662306a36Sopenharmony_ci			 cmd->sg_list.buf[i].hi_addr);
32762306a36Sopenharmony_ci		pr_debug("buf[%d]:low addr = 0x%x\n", i,
32862306a36Sopenharmony_ci			 cmd->sg_list.buf[i].low_addr);
32962306a36Sopenharmony_ci		pr_debug("buf[%d]:size = 0x%x\n", i, cmd->sg_list.buf[i].size);
33062306a36Sopenharmony_ci		pr_debug("list size = 0x%x\n", cmd->sg_list.size);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	*buf_id = 0;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	ret = psp_tee_process_cmd(TEE_CMD_ID_MAP_SHARED_MEM, (void *)cmd,
33662306a36Sopenharmony_ci				  sizeof(*cmd), &status);
33762306a36Sopenharmony_ci	if (!ret && !status) {
33862306a36Sopenharmony_ci		*buf_id = cmd->buf_id;
33962306a36Sopenharmony_ci		pr_debug("mapped buffer ID = 0x%x\n", *buf_id);
34062306a36Sopenharmony_ci	} else {
34162306a36Sopenharmony_ci		pr_err("map shared memory: status = 0x%x\n", status);
34262306a36Sopenharmony_ci		ret = -ENOMEM;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cifree_cmd:
34662306a36Sopenharmony_ci	kfree(cmd);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return ret;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ciint handle_open_session(struct tee_ioctl_open_session_arg *arg, u32 *info,
35262306a36Sopenharmony_ci			struct tee_param *p)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct tee_cmd_open_session cmd = {0};
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (!arg || !info || (!p && arg->num_params))
35862306a36Sopenharmony_ci		return -EINVAL;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	arg->ret_origin = TEEC_ORIGIN_COMMS;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (arg->session == 0) {
36362306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_GENERIC;
36462306a36Sopenharmony_ci		return -EINVAL;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ret = tee_params_to_amd_params(p, arg->num_params, &cmd.op);
36862306a36Sopenharmony_ci	if (ret) {
36962306a36Sopenharmony_ci		pr_err("invalid Params. Abort open session\n");
37062306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
37162306a36Sopenharmony_ci		return ret;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	cmd.ta_handle = get_ta_handle(arg->session);
37562306a36Sopenharmony_ci	*info = 0;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	ret = psp_tee_process_cmd(TEE_CMD_ID_OPEN_SESSION, (void *)&cmd,
37862306a36Sopenharmony_ci				  sizeof(cmd), &arg->ret);
37962306a36Sopenharmony_ci	if (ret) {
38062306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_COMMUNICATION;
38162306a36Sopenharmony_ci	} else {
38262306a36Sopenharmony_ci		ret = amd_params_to_tee_params(p, arg->num_params, &cmd.op);
38362306a36Sopenharmony_ci		if (unlikely(ret)) {
38462306a36Sopenharmony_ci			pr_err("open session: failed to copy output\n");
38562306a36Sopenharmony_ci			arg->ret = TEEC_ERROR_GENERIC;
38662306a36Sopenharmony_ci			return ret;
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci		arg->ret_origin = cmd.return_origin;
38962306a36Sopenharmony_ci		*info = cmd.session_info;
39062306a36Sopenharmony_ci		pr_debug("open session: session info = 0x%x\n", *info);
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	pr_debug("open session: ret = 0x%x RO = 0x%x\n", arg->ret,
39462306a36Sopenharmony_ci		 arg->ret_origin);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return ret;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ciint handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct tee_cmd_unload_ta unload_cmd = {};
40262306a36Sopenharmony_ci	struct tee_cmd_load_ta load_cmd = {};
40362306a36Sopenharmony_ci	phys_addr_t blob;
40462306a36Sopenharmony_ci	int ret;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (size == 0 || !data || !arg)
40762306a36Sopenharmony_ci		return -EINVAL;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	blob = __psp_pa(data);
41062306a36Sopenharmony_ci	if (blob & (PAGE_SIZE - 1)) {
41162306a36Sopenharmony_ci		pr_err("load TA: page unaligned. blob 0x%llx", blob);
41262306a36Sopenharmony_ci		return -EINVAL;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	load_cmd.hi_addr = upper_32_bits(blob);
41662306a36Sopenharmony_ci	load_cmd.low_addr = lower_32_bits(blob);
41762306a36Sopenharmony_ci	load_cmd.size = size;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	mutex_lock(&ta_refcount_mutex);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	ret = psp_tee_process_cmd(TEE_CMD_ID_LOAD_TA, (void *)&load_cmd,
42262306a36Sopenharmony_ci				  sizeof(load_cmd), &arg->ret);
42362306a36Sopenharmony_ci	if (ret) {
42462306a36Sopenharmony_ci		arg->ret_origin = TEEC_ORIGIN_COMMS;
42562306a36Sopenharmony_ci		arg->ret = TEEC_ERROR_COMMUNICATION;
42662306a36Sopenharmony_ci	} else {
42762306a36Sopenharmony_ci		arg->ret_origin = load_cmd.return_origin;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		if (arg->ret == TEEC_SUCCESS) {
43062306a36Sopenharmony_ci			ret = get_ta_refcount(load_cmd.ta_handle);
43162306a36Sopenharmony_ci			if (!ret) {
43262306a36Sopenharmony_ci				arg->ret_origin = TEEC_ORIGIN_COMMS;
43362306a36Sopenharmony_ci				arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci				/* Unload the TA on error */
43662306a36Sopenharmony_ci				unload_cmd.ta_handle = load_cmd.ta_handle;
43762306a36Sopenharmony_ci				psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA,
43862306a36Sopenharmony_ci						    (void *)&unload_cmd,
43962306a36Sopenharmony_ci						    sizeof(unload_cmd), &ret);
44062306a36Sopenharmony_ci			} else {
44162306a36Sopenharmony_ci				set_session_id(load_cmd.ta_handle, 0, &arg->session);
44262306a36Sopenharmony_ci			}
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci	mutex_unlock(&ta_refcount_mutex);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	pr_debug("load TA: TA handle = 0x%x, RO = 0x%x, ret = 0x%x\n",
44862306a36Sopenharmony_ci		 load_cmd.ta_handle, arg->ret_origin, arg->ret);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
452