18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2015, Linaro Limited
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h>
98c2ecf20Sopenharmony_ci#include <linux/crash_dump.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/tee_drv.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
218c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
228c2ecf20Sopenharmony_ci#include "optee_private.h"
238c2ecf20Sopenharmony_ci#include "optee_smc.h"
248c2ecf20Sopenharmony_ci#include "shm_pool.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define DRIVER_NAME "optee"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define OPTEE_SHM_NUM_PRIV_PAGES	CONFIG_OPTEE_SHM_NUM_PRIV_PAGES
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/**
318c2ecf20Sopenharmony_ci * optee_from_msg_param() - convert from OPTEE_MSG parameters to
328c2ecf20Sopenharmony_ci *			    struct tee_param
338c2ecf20Sopenharmony_ci * @params:	subsystem internal parameter representation
348c2ecf20Sopenharmony_ci * @num_params:	number of elements in the parameter arrays
358c2ecf20Sopenharmony_ci * @msg_params:	OPTEE_MSG parameters
368c2ecf20Sopenharmony_ci * Returns 0 on success or <0 on failure
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ciint optee_from_msg_param(struct tee_param *params, size_t num_params,
398c2ecf20Sopenharmony_ci			 const struct optee_msg_param *msg_params)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	int rc;
428c2ecf20Sopenharmony_ci	size_t n;
438c2ecf20Sopenharmony_ci	struct tee_shm *shm;
448c2ecf20Sopenharmony_ci	phys_addr_t pa;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	for (n = 0; n < num_params; n++) {
478c2ecf20Sopenharmony_ci		struct tee_param *p = params + n;
488c2ecf20Sopenharmony_ci		const struct optee_msg_param *mp = msg_params + n;
498c2ecf20Sopenharmony_ci		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		switch (attr) {
528c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_NONE:
538c2ecf20Sopenharmony_ci			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
548c2ecf20Sopenharmony_ci			memset(&p->u, 0, sizeof(p->u));
558c2ecf20Sopenharmony_ci			break;
568c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
578c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
588c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
598c2ecf20Sopenharmony_ci			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
608c2ecf20Sopenharmony_ci				  attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
618c2ecf20Sopenharmony_ci			p->u.value.a = mp->u.value.a;
628c2ecf20Sopenharmony_ci			p->u.value.b = mp->u.value.b;
638c2ecf20Sopenharmony_ci			p->u.value.c = mp->u.value.c;
648c2ecf20Sopenharmony_ci			break;
658c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
668c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
678c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
688c2ecf20Sopenharmony_ci			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
698c2ecf20Sopenharmony_ci				  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
708c2ecf20Sopenharmony_ci			p->u.memref.size = mp->u.tmem.size;
718c2ecf20Sopenharmony_ci			shm = (struct tee_shm *)(unsigned long)
728c2ecf20Sopenharmony_ci				mp->u.tmem.shm_ref;
738c2ecf20Sopenharmony_ci			if (!shm) {
748c2ecf20Sopenharmony_ci				p->u.memref.shm_offs = 0;
758c2ecf20Sopenharmony_ci				p->u.memref.shm = NULL;
768c2ecf20Sopenharmony_ci				break;
778c2ecf20Sopenharmony_ci			}
788c2ecf20Sopenharmony_ci			rc = tee_shm_get_pa(shm, 0, &pa);
798c2ecf20Sopenharmony_ci			if (rc)
808c2ecf20Sopenharmony_ci				return rc;
818c2ecf20Sopenharmony_ci			p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
828c2ecf20Sopenharmony_ci			p->u.memref.shm = shm;
838c2ecf20Sopenharmony_ci			break;
848c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
858c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
868c2ecf20Sopenharmony_ci		case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
878c2ecf20Sopenharmony_ci			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
888c2ecf20Sopenharmony_ci				  attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
898c2ecf20Sopenharmony_ci			p->u.memref.size = mp->u.rmem.size;
908c2ecf20Sopenharmony_ci			shm = (struct tee_shm *)(unsigned long)
918c2ecf20Sopenharmony_ci				mp->u.rmem.shm_ref;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci			if (!shm) {
948c2ecf20Sopenharmony_ci				p->u.memref.shm_offs = 0;
958c2ecf20Sopenharmony_ci				p->u.memref.shm = NULL;
968c2ecf20Sopenharmony_ci				break;
978c2ecf20Sopenharmony_ci			}
988c2ecf20Sopenharmony_ci			p->u.memref.shm_offs = mp->u.rmem.offs;
998c2ecf20Sopenharmony_ci			p->u.memref.shm = shm;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci			break;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		default:
1048c2ecf20Sopenharmony_ci			return -EINVAL;
1058c2ecf20Sopenharmony_ci		}
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int to_msg_param_tmp_mem(struct optee_msg_param *mp,
1118c2ecf20Sopenharmony_ci				const struct tee_param *p)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	int rc;
1148c2ecf20Sopenharmony_ci	phys_addr_t pa;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
1178c2ecf20Sopenharmony_ci		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
1208c2ecf20Sopenharmony_ci	mp->u.tmem.size = p->u.memref.size;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (!p->u.memref.shm) {
1238c2ecf20Sopenharmony_ci		mp->u.tmem.buf_ptr = 0;
1248c2ecf20Sopenharmony_ci		return 0;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
1288c2ecf20Sopenharmony_ci	if (rc)
1298c2ecf20Sopenharmony_ci		return rc;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	mp->u.tmem.buf_ptr = pa;
1328c2ecf20Sopenharmony_ci	mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
1338c2ecf20Sopenharmony_ci		    OPTEE_MSG_ATTR_CACHE_SHIFT;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int to_msg_param_reg_mem(struct optee_msg_param *mp,
1398c2ecf20Sopenharmony_ci				const struct tee_param *p)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
1428c2ecf20Sopenharmony_ci		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
1458c2ecf20Sopenharmony_ci	mp->u.rmem.size = p->u.memref.size;
1468c2ecf20Sopenharmony_ci	mp->u.rmem.offs = p->u.memref.shm_offs;
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/**
1518c2ecf20Sopenharmony_ci * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
1528c2ecf20Sopenharmony_ci * @msg_params:	OPTEE_MSG parameters
1538c2ecf20Sopenharmony_ci * @num_params:	number of elements in the parameter arrays
1548c2ecf20Sopenharmony_ci * @params:	subsystem itnernal parameter representation
1558c2ecf20Sopenharmony_ci * Returns 0 on success or <0 on failure
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_ciint optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
1588c2ecf20Sopenharmony_ci		       const struct tee_param *params)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int rc;
1618c2ecf20Sopenharmony_ci	size_t n;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	for (n = 0; n < num_params; n++) {
1648c2ecf20Sopenharmony_ci		const struct tee_param *p = params + n;
1658c2ecf20Sopenharmony_ci		struct optee_msg_param *mp = msg_params + n;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		switch (p->attr) {
1688c2ecf20Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
1698c2ecf20Sopenharmony_ci			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
1708c2ecf20Sopenharmony_ci			memset(&mp->u, 0, sizeof(mp->u));
1718c2ecf20Sopenharmony_ci			break;
1728c2ecf20Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
1738c2ecf20Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
1748c2ecf20Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
1758c2ecf20Sopenharmony_ci			mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
1768c2ecf20Sopenharmony_ci				   TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
1778c2ecf20Sopenharmony_ci			mp->u.value.a = p->u.value.a;
1788c2ecf20Sopenharmony_ci			mp->u.value.b = p->u.value.b;
1798c2ecf20Sopenharmony_ci			mp->u.value.c = p->u.value.c;
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
1828c2ecf20Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
1838c2ecf20Sopenharmony_ci		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
1848c2ecf20Sopenharmony_ci			if (tee_shm_is_registered(p->u.memref.shm))
1858c2ecf20Sopenharmony_ci				rc = to_msg_param_reg_mem(mp, p);
1868c2ecf20Sopenharmony_ci			else
1878c2ecf20Sopenharmony_ci				rc = to_msg_param_tmp_mem(mp, p);
1888c2ecf20Sopenharmony_ci			if (rc)
1898c2ecf20Sopenharmony_ci				return rc;
1908c2ecf20Sopenharmony_ci			break;
1918c2ecf20Sopenharmony_ci		default:
1928c2ecf20Sopenharmony_ci			return -EINVAL;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	return 0;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void optee_get_version(struct tee_device *teedev,
1998c2ecf20Sopenharmony_ci			      struct tee_ioctl_version_data *vers)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct tee_ioctl_version_data v = {
2028c2ecf20Sopenharmony_ci		.impl_id = TEE_IMPL_ID_OPTEE,
2038c2ecf20Sopenharmony_ci		.impl_caps = TEE_OPTEE_CAP_TZ,
2048c2ecf20Sopenharmony_ci		.gen_caps = TEE_GEN_CAP_GP,
2058c2ecf20Sopenharmony_ci	};
2068c2ecf20Sopenharmony_ci	struct optee *optee = tee_get_drvdata(teedev);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
2098c2ecf20Sopenharmony_ci		v.gen_caps |= TEE_GEN_CAP_REG_MEM;
2108c2ecf20Sopenharmony_ci	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
2118c2ecf20Sopenharmony_ci		v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL;
2128c2ecf20Sopenharmony_ci	*vers = v;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic void optee_bus_scan(struct work_struct *work)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int optee_open(struct tee_context *ctx)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct optee_context_data *ctxdata;
2238c2ecf20Sopenharmony_ci	struct tee_device *teedev = ctx->teedev;
2248c2ecf20Sopenharmony_ci	struct optee *optee = tee_get_drvdata(teedev);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
2278c2ecf20Sopenharmony_ci	if (!ctxdata)
2288c2ecf20Sopenharmony_ci		return -ENOMEM;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (teedev == optee->supp_teedev) {
2318c2ecf20Sopenharmony_ci		bool busy = true;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		mutex_lock(&optee->supp.mutex);
2348c2ecf20Sopenharmony_ci		if (!optee->supp.ctx) {
2358c2ecf20Sopenharmony_ci			busy = false;
2368c2ecf20Sopenharmony_ci			optee->supp.ctx = ctx;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci		mutex_unlock(&optee->supp.mutex);
2398c2ecf20Sopenharmony_ci		if (busy) {
2408c2ecf20Sopenharmony_ci			kfree(ctxdata);
2418c2ecf20Sopenharmony_ci			return -EBUSY;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		if (!optee->scan_bus_done) {
2458c2ecf20Sopenharmony_ci			INIT_WORK(&optee->scan_bus_work, optee_bus_scan);
2468c2ecf20Sopenharmony_ci			optee->scan_bus_wq = create_workqueue("optee_bus_scan");
2478c2ecf20Sopenharmony_ci			if (!optee->scan_bus_wq) {
2488c2ecf20Sopenharmony_ci				kfree(ctxdata);
2498c2ecf20Sopenharmony_ci				return -ECHILD;
2508c2ecf20Sopenharmony_ci			}
2518c2ecf20Sopenharmony_ci			queue_work(optee->scan_bus_wq, &optee->scan_bus_work);
2528c2ecf20Sopenharmony_ci			optee->scan_bus_done = true;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	mutex_init(&ctxdata->mutex);
2568c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctxdata->sess_list);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
2598c2ecf20Sopenharmony_ci		ctx->cap_memref_null  = true;
2608c2ecf20Sopenharmony_ci	else
2618c2ecf20Sopenharmony_ci		ctx->cap_memref_null = false;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	ctx->data = ctxdata;
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void optee_release(struct tee_context *ctx)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct optee_context_data *ctxdata = ctx->data;
2708c2ecf20Sopenharmony_ci	struct tee_device *teedev = ctx->teedev;
2718c2ecf20Sopenharmony_ci	struct optee *optee = tee_get_drvdata(teedev);
2728c2ecf20Sopenharmony_ci	struct tee_shm *shm;
2738c2ecf20Sopenharmony_ci	struct optee_msg_arg *arg = NULL;
2748c2ecf20Sopenharmony_ci	phys_addr_t parg;
2758c2ecf20Sopenharmony_ci	struct optee_session *sess;
2768c2ecf20Sopenharmony_ci	struct optee_session *sess_tmp;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (!ctxdata)
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg),
2828c2ecf20Sopenharmony_ci			    TEE_SHM_MAPPED | TEE_SHM_PRIV);
2838c2ecf20Sopenharmony_ci	if (!IS_ERR(shm)) {
2848c2ecf20Sopenharmony_ci		arg = tee_shm_get_va(shm, 0);
2858c2ecf20Sopenharmony_ci		/*
2868c2ecf20Sopenharmony_ci		 * If va2pa fails for some reason, we can't call into
2878c2ecf20Sopenharmony_ci		 * secure world, only free the memory. Secure OS will leak
2888c2ecf20Sopenharmony_ci		 * sessions and finally refuse more sessions, but we will
2898c2ecf20Sopenharmony_ci		 * at least let normal world reclaim its memory.
2908c2ecf20Sopenharmony_ci		 */
2918c2ecf20Sopenharmony_ci		if (!IS_ERR(arg))
2928c2ecf20Sopenharmony_ci			if (tee_shm_va2pa(shm, arg, &parg))
2938c2ecf20Sopenharmony_ci				arg = NULL; /* prevent usage of parg below */
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
2978c2ecf20Sopenharmony_ci				 list_node) {
2988c2ecf20Sopenharmony_ci		list_del(&sess->list_node);
2998c2ecf20Sopenharmony_ci		if (!IS_ERR_OR_NULL(arg)) {
3008c2ecf20Sopenharmony_ci			memset(arg, 0, sizeof(*arg));
3018c2ecf20Sopenharmony_ci			arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
3028c2ecf20Sopenharmony_ci			arg->session = sess->session_id;
3038c2ecf20Sopenharmony_ci			optee_do_call_with_arg(ctx, parg);
3048c2ecf20Sopenharmony_ci		}
3058c2ecf20Sopenharmony_ci		kfree(sess);
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	kfree(ctxdata);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!IS_ERR(shm))
3108c2ecf20Sopenharmony_ci		tee_shm_free(shm);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ctx->data = NULL;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (teedev == optee->supp_teedev) {
3158c2ecf20Sopenharmony_ci		if (optee->scan_bus_wq) {
3168c2ecf20Sopenharmony_ci			destroy_workqueue(optee->scan_bus_wq);
3178c2ecf20Sopenharmony_ci			optee->scan_bus_wq = NULL;
3188c2ecf20Sopenharmony_ci		}
3198c2ecf20Sopenharmony_ci		optee_supp_release(&optee->supp);
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic const struct tee_driver_ops optee_ops = {
3248c2ecf20Sopenharmony_ci	.get_version = optee_get_version,
3258c2ecf20Sopenharmony_ci	.open = optee_open,
3268c2ecf20Sopenharmony_ci	.release = optee_release,
3278c2ecf20Sopenharmony_ci	.open_session = optee_open_session,
3288c2ecf20Sopenharmony_ci	.close_session = optee_close_session,
3298c2ecf20Sopenharmony_ci	.invoke_func = optee_invoke_func,
3308c2ecf20Sopenharmony_ci	.cancel_req = optee_cancel_req,
3318c2ecf20Sopenharmony_ci	.shm_register = optee_shm_register,
3328c2ecf20Sopenharmony_ci	.shm_unregister = optee_shm_unregister,
3338c2ecf20Sopenharmony_ci};
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic const struct tee_desc optee_desc = {
3368c2ecf20Sopenharmony_ci	.name = DRIVER_NAME "-clnt",
3378c2ecf20Sopenharmony_ci	.ops = &optee_ops,
3388c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3398c2ecf20Sopenharmony_ci};
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic const struct tee_driver_ops optee_supp_ops = {
3428c2ecf20Sopenharmony_ci	.get_version = optee_get_version,
3438c2ecf20Sopenharmony_ci	.open = optee_open,
3448c2ecf20Sopenharmony_ci	.release = optee_release,
3458c2ecf20Sopenharmony_ci	.supp_recv = optee_supp_recv,
3468c2ecf20Sopenharmony_ci	.supp_send = optee_supp_send,
3478c2ecf20Sopenharmony_ci	.shm_register = optee_shm_register_supp,
3488c2ecf20Sopenharmony_ci	.shm_unregister = optee_shm_unregister_supp,
3498c2ecf20Sopenharmony_ci};
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic const struct tee_desc optee_supp_desc = {
3528c2ecf20Sopenharmony_ci	.name = DRIVER_NAME "-supp",
3538c2ecf20Sopenharmony_ci	.ops = &optee_supp_ops,
3548c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3558c2ecf20Sopenharmony_ci	.flags = TEE_DESC_PRIVILEGED,
3568c2ecf20Sopenharmony_ci};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct arm_smccc_res res;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
3658c2ecf20Sopenharmony_ci	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
3668c2ecf20Sopenharmony_ci		return true;
3678c2ecf20Sopenharmony_ci	return false;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	union {
3738c2ecf20Sopenharmony_ci		struct arm_smccc_res smccc;
3748c2ecf20Sopenharmony_ci		struct optee_smc_call_get_os_revision_result result;
3758c2ecf20Sopenharmony_ci	} res = {
3768c2ecf20Sopenharmony_ci		.result = {
3778c2ecf20Sopenharmony_ci			.build_id = 0
3788c2ecf20Sopenharmony_ci		}
3798c2ecf20Sopenharmony_ci	};
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
3828c2ecf20Sopenharmony_ci		  &res.smccc);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (res.result.build_id)
3858c2ecf20Sopenharmony_ci		pr_info("revision %lu.%lu (%08lx)", res.result.major,
3868c2ecf20Sopenharmony_ci			res.result.minor, res.result.build_id);
3878c2ecf20Sopenharmony_ci	else
3888c2ecf20Sopenharmony_ci		pr_info("revision %lu.%lu", res.result.major, res.result.minor);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	union {
3948c2ecf20Sopenharmony_ci		struct arm_smccc_res smccc;
3958c2ecf20Sopenharmony_ci		struct optee_smc_calls_revision_result result;
3968c2ecf20Sopenharmony_ci	} res;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
4018c2ecf20Sopenharmony_ci	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
4028c2ecf20Sopenharmony_ci		return true;
4038c2ecf20Sopenharmony_ci	return false;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
4078c2ecf20Sopenharmony_ci					    u32 *sec_caps)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	union {
4108c2ecf20Sopenharmony_ci		struct arm_smccc_res smccc;
4118c2ecf20Sopenharmony_ci		struct optee_smc_exchange_capabilities_result result;
4128c2ecf20Sopenharmony_ci	} res;
4138c2ecf20Sopenharmony_ci	u32 a1 = 0;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/*
4168c2ecf20Sopenharmony_ci	 * TODO This isn't enough to tell if it's UP system (from kernel
4178c2ecf20Sopenharmony_ci	 * point of view) or not, is_smp() returns the the information
4188c2ecf20Sopenharmony_ci	 * needed, but can't be called directly from here.
4198c2ecf20Sopenharmony_ci	 */
4208c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1)
4218c2ecf20Sopenharmony_ci		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0,
4248c2ecf20Sopenharmony_ci		  &res.smccc);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (res.result.status != OPTEE_SMC_RETURN_OK)
4278c2ecf20Sopenharmony_ci		return false;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	*sec_caps = res.result.capabilities;
4308c2ecf20Sopenharmony_ci	return true;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic struct tee_shm_pool *optee_config_dyn_shm(void)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct tee_shm_pool_mgr *priv_mgr;
4368c2ecf20Sopenharmony_ci	struct tee_shm_pool_mgr *dmabuf_mgr;
4378c2ecf20Sopenharmony_ci	void *rc;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	rc = optee_shm_pool_alloc_pages();
4408c2ecf20Sopenharmony_ci	if (IS_ERR(rc))
4418c2ecf20Sopenharmony_ci		return rc;
4428c2ecf20Sopenharmony_ci	priv_mgr = rc;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	rc = optee_shm_pool_alloc_pages();
4458c2ecf20Sopenharmony_ci	if (IS_ERR(rc)) {
4468c2ecf20Sopenharmony_ci		tee_shm_pool_mgr_destroy(priv_mgr);
4478c2ecf20Sopenharmony_ci		return rc;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci	dmabuf_mgr = rc;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
4528c2ecf20Sopenharmony_ci	if (IS_ERR(rc)) {
4538c2ecf20Sopenharmony_ci		tee_shm_pool_mgr_destroy(priv_mgr);
4548c2ecf20Sopenharmony_ci		tee_shm_pool_mgr_destroy(dmabuf_mgr);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return rc;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic struct tee_shm_pool *
4618c2ecf20Sopenharmony_cioptee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	union {
4648c2ecf20Sopenharmony_ci		struct arm_smccc_res smccc;
4658c2ecf20Sopenharmony_ci		struct optee_smc_get_shm_config_result result;
4668c2ecf20Sopenharmony_ci	} res;
4678c2ecf20Sopenharmony_ci	unsigned long vaddr;
4688c2ecf20Sopenharmony_ci	phys_addr_t paddr;
4698c2ecf20Sopenharmony_ci	size_t size;
4708c2ecf20Sopenharmony_ci	phys_addr_t begin;
4718c2ecf20Sopenharmony_ci	phys_addr_t end;
4728c2ecf20Sopenharmony_ci	void *va;
4738c2ecf20Sopenharmony_ci	struct tee_shm_pool_mgr *priv_mgr;
4748c2ecf20Sopenharmony_ci	struct tee_shm_pool_mgr *dmabuf_mgr;
4758c2ecf20Sopenharmony_ci	void *rc;
4768c2ecf20Sopenharmony_ci	const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
4798c2ecf20Sopenharmony_ci	if (res.result.status != OPTEE_SMC_RETURN_OK) {
4808c2ecf20Sopenharmony_ci		pr_err("static shm service not available\n");
4818c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
4858c2ecf20Sopenharmony_ci		pr_err("only normal cached shared memory supported\n");
4868c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	begin = roundup(res.result.start, PAGE_SIZE);
4908c2ecf20Sopenharmony_ci	end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
4918c2ecf20Sopenharmony_ci	paddr = begin;
4928c2ecf20Sopenharmony_ci	size = end - begin;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) {
4958c2ecf20Sopenharmony_ci		pr_err("too small shared memory area\n");
4968c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	va = memremap(paddr, size, MEMREMAP_WB);
5008c2ecf20Sopenharmony_ci	if (!va) {
5018c2ecf20Sopenharmony_ci		pr_err("shared memory ioremap failed\n");
5028c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci	vaddr = (unsigned long)va;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
5078c2ecf20Sopenharmony_ci					    3 /* 8 bytes aligned */);
5088c2ecf20Sopenharmony_ci	if (IS_ERR(rc))
5098c2ecf20Sopenharmony_ci		goto err_memunmap;
5108c2ecf20Sopenharmony_ci	priv_mgr = rc;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	vaddr += sz;
5138c2ecf20Sopenharmony_ci	paddr += sz;
5148c2ecf20Sopenharmony_ci	size -= sz;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
5178c2ecf20Sopenharmony_ci	if (IS_ERR(rc))
5188c2ecf20Sopenharmony_ci		goto err_free_priv_mgr;
5198c2ecf20Sopenharmony_ci	dmabuf_mgr = rc;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
5228c2ecf20Sopenharmony_ci	if (IS_ERR(rc))
5238c2ecf20Sopenharmony_ci		goto err_free_dmabuf_mgr;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	*memremaped_shm = va;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return rc;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cierr_free_dmabuf_mgr:
5308c2ecf20Sopenharmony_ci	tee_shm_pool_mgr_destroy(dmabuf_mgr);
5318c2ecf20Sopenharmony_cierr_free_priv_mgr:
5328c2ecf20Sopenharmony_ci	tee_shm_pool_mgr_destroy(priv_mgr);
5338c2ecf20Sopenharmony_cierr_memunmap:
5348c2ecf20Sopenharmony_ci	memunmap(va);
5358c2ecf20Sopenharmony_ci	return rc;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci/* Simple wrapper functions to be able to use a function pointer */
5398c2ecf20Sopenharmony_cistatic void optee_smccc_smc(unsigned long a0, unsigned long a1,
5408c2ecf20Sopenharmony_ci			    unsigned long a2, unsigned long a3,
5418c2ecf20Sopenharmony_ci			    unsigned long a4, unsigned long a5,
5428c2ecf20Sopenharmony_ci			    unsigned long a6, unsigned long a7,
5438c2ecf20Sopenharmony_ci			    struct arm_smccc_res *res)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic void optee_smccc_hvc(unsigned long a0, unsigned long a1,
5498c2ecf20Sopenharmony_ci			    unsigned long a2, unsigned long a3,
5508c2ecf20Sopenharmony_ci			    unsigned long a4, unsigned long a5,
5518c2ecf20Sopenharmony_ci			    unsigned long a6, unsigned long a7,
5528c2ecf20Sopenharmony_ci			    struct arm_smccc_res *res)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic optee_invoke_fn *get_invoke_func(struct device *dev)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	const char *method;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	pr_info("probing for conduit method.\n");
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (device_property_read_string(dev, "method", &method)) {
5648c2ecf20Sopenharmony_ci		pr_warn("missing \"method\" property\n");
5658c2ecf20Sopenharmony_ci		return ERR_PTR(-ENXIO);
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!strcmp("hvc", method))
5698c2ecf20Sopenharmony_ci		return optee_smccc_hvc;
5708c2ecf20Sopenharmony_ci	else if (!strcmp("smc", method))
5718c2ecf20Sopenharmony_ci		return optee_smccc_smc;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	pr_warn("invalid \"method\" property: %s\n", method);
5748c2ecf20Sopenharmony_ci	return ERR_PTR(-EINVAL);
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/* optee_remove - Device Removal Routine
5788c2ecf20Sopenharmony_ci * @pdev: platform device information struct
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci * optee_remove is called by platform subsystem to alert the driver
5818c2ecf20Sopenharmony_ci * that it should release the device
5828c2ecf20Sopenharmony_ci */
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic int optee_remove(struct platform_device *pdev)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	struct optee *optee = platform_get_drvdata(pdev);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/* Unregister OP-TEE specific client devices on TEE bus */
5898c2ecf20Sopenharmony_ci	optee_unregister_devices();
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	teedev_close_context(optee->ctx);
5928c2ecf20Sopenharmony_ci	/*
5938c2ecf20Sopenharmony_ci	 * Ask OP-TEE to free all cached shared memory objects to decrease
5948c2ecf20Sopenharmony_ci	 * reference counters and also avoid wild pointers in secure world
5958c2ecf20Sopenharmony_ci	 * into the old shared memory range.
5968c2ecf20Sopenharmony_ci	 */
5978c2ecf20Sopenharmony_ci	optee_disable_shm_cache(optee);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	/*
6008c2ecf20Sopenharmony_ci	 * The two devices have to be unregistered before we can free the
6018c2ecf20Sopenharmony_ci	 * other resources.
6028c2ecf20Sopenharmony_ci	 */
6038c2ecf20Sopenharmony_ci	tee_device_unregister(optee->supp_teedev);
6048c2ecf20Sopenharmony_ci	tee_device_unregister(optee->teedev);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	tee_shm_pool_free(optee->pool);
6078c2ecf20Sopenharmony_ci	if (optee->memremaped_shm)
6088c2ecf20Sopenharmony_ci		memunmap(optee->memremaped_shm);
6098c2ecf20Sopenharmony_ci	optee_wait_queue_exit(&optee->wait_queue);
6108c2ecf20Sopenharmony_ci	optee_supp_uninit(&optee->supp);
6118c2ecf20Sopenharmony_ci	mutex_destroy(&optee->call_queue.mutex);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	kfree(optee);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return 0;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/* optee_shutdown - Device Removal Routine
6198c2ecf20Sopenharmony_ci * @pdev: platform device information struct
6208c2ecf20Sopenharmony_ci *
6218c2ecf20Sopenharmony_ci * platform_shutdown is called by the platform subsystem to alert
6228c2ecf20Sopenharmony_ci * the driver that a shutdown, reboot, or kexec is happening and
6238c2ecf20Sopenharmony_ci * device must be disabled.
6248c2ecf20Sopenharmony_ci */
6258c2ecf20Sopenharmony_cistatic void optee_shutdown(struct platform_device *pdev)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	optee_disable_shm_cache(platform_get_drvdata(pdev));
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic int optee_probe(struct platform_device *pdev)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	optee_invoke_fn *invoke_fn;
6338c2ecf20Sopenharmony_ci	struct tee_shm_pool *pool = ERR_PTR(-EINVAL);
6348c2ecf20Sopenharmony_ci	struct optee *optee = NULL;
6358c2ecf20Sopenharmony_ci	void *memremaped_shm = NULL;
6368c2ecf20Sopenharmony_ci	struct tee_device *teedev;
6378c2ecf20Sopenharmony_ci	struct tee_context *ctx;
6388c2ecf20Sopenharmony_ci	u32 sec_caps;
6398c2ecf20Sopenharmony_ci	int rc;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/*
6428c2ecf20Sopenharmony_ci	 * The kernel may have crashed at the same time that all available
6438c2ecf20Sopenharmony_ci	 * secure world threads were suspended and we cannot reschedule the
6448c2ecf20Sopenharmony_ci	 * suspended threads without access to the crashed kernel's wait_queue.
6458c2ecf20Sopenharmony_ci	 * Therefore, we cannot reliably initialize the OP-TEE driver in the
6468c2ecf20Sopenharmony_ci	 * kdump kernel.
6478c2ecf20Sopenharmony_ci	 */
6488c2ecf20Sopenharmony_ci	if (is_kdump_kernel())
6498c2ecf20Sopenharmony_ci		return -ENODEV;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	invoke_fn = get_invoke_func(&pdev->dev);
6528c2ecf20Sopenharmony_ci	if (IS_ERR(invoke_fn))
6538c2ecf20Sopenharmony_ci		return PTR_ERR(invoke_fn);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
6568c2ecf20Sopenharmony_ci		pr_warn("api uid mismatch\n");
6578c2ecf20Sopenharmony_ci		return -EINVAL;
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	optee_msg_get_os_revision(invoke_fn);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
6638c2ecf20Sopenharmony_ci		pr_warn("api revision mismatch\n");
6648c2ecf20Sopenharmony_ci		return -EINVAL;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
6688c2ecf20Sopenharmony_ci		pr_warn("capabilities mismatch\n");
6698c2ecf20Sopenharmony_ci		return -EINVAL;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/*
6738c2ecf20Sopenharmony_ci	 * Try to use dynamic shared memory if possible
6748c2ecf20Sopenharmony_ci	 */
6758c2ecf20Sopenharmony_ci	if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
6768c2ecf20Sopenharmony_ci		pool = optee_config_dyn_shm();
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/*
6798c2ecf20Sopenharmony_ci	 * If dynamic shared memory is not available or failed - try static one
6808c2ecf20Sopenharmony_ci	 */
6818c2ecf20Sopenharmony_ci	if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
6828c2ecf20Sopenharmony_ci		pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (IS_ERR(pool))
6858c2ecf20Sopenharmony_ci		return PTR_ERR(pool);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
6888c2ecf20Sopenharmony_ci	if (!optee) {
6898c2ecf20Sopenharmony_ci		rc = -ENOMEM;
6908c2ecf20Sopenharmony_ci		goto err;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	optee->invoke_fn = invoke_fn;
6948c2ecf20Sopenharmony_ci	optee->sec_caps = sec_caps;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
6978c2ecf20Sopenharmony_ci	if (IS_ERR(teedev)) {
6988c2ecf20Sopenharmony_ci		rc = PTR_ERR(teedev);
6998c2ecf20Sopenharmony_ci		goto err;
7008c2ecf20Sopenharmony_ci	}
7018c2ecf20Sopenharmony_ci	optee->teedev = teedev;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
7048c2ecf20Sopenharmony_ci	if (IS_ERR(teedev)) {
7058c2ecf20Sopenharmony_ci		rc = PTR_ERR(teedev);
7068c2ecf20Sopenharmony_ci		goto err;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci	optee->supp_teedev = teedev;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	rc = tee_device_register(optee->teedev);
7118c2ecf20Sopenharmony_ci	if (rc)
7128c2ecf20Sopenharmony_ci		goto err;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	rc = tee_device_register(optee->supp_teedev);
7158c2ecf20Sopenharmony_ci	if (rc)
7168c2ecf20Sopenharmony_ci		goto err;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	mutex_init(&optee->call_queue.mutex);
7198c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&optee->call_queue.waiters);
7208c2ecf20Sopenharmony_ci	optee_wait_queue_init(&optee->wait_queue);
7218c2ecf20Sopenharmony_ci	optee_supp_init(&optee->supp);
7228c2ecf20Sopenharmony_ci	optee->memremaped_shm = memremaped_shm;
7238c2ecf20Sopenharmony_ci	optee->pool = pool;
7248c2ecf20Sopenharmony_ci	ctx = teedev_open(optee->teedev);
7258c2ecf20Sopenharmony_ci	if (IS_ERR(ctx)) {
7268c2ecf20Sopenharmony_ci		rc = PTR_ERR(ctx);
7278c2ecf20Sopenharmony_ci		goto err;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci	optee->ctx = ctx;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/*
7328c2ecf20Sopenharmony_ci	 * Ensure that there are no pre-existing shm objects before enabling
7338c2ecf20Sopenharmony_ci	 * the shm cache so that there's no chance of receiving an invalid
7348c2ecf20Sopenharmony_ci	 * address during shutdown. This could occur, for example, if we're
7358c2ecf20Sopenharmony_ci	 * kexec booting from an older kernel that did not properly cleanup the
7368c2ecf20Sopenharmony_ci	 * shm cache.
7378c2ecf20Sopenharmony_ci	 */
7388c2ecf20Sopenharmony_ci	optee_disable_unmapped_shm_cache(optee);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	optee_enable_shm_cache(optee);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
7438c2ecf20Sopenharmony_ci		pr_info("dynamic shared memory is enabled\n");
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, optee);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
7488c2ecf20Sopenharmony_ci	if (rc) {
7498c2ecf20Sopenharmony_ci		optee_remove(pdev);
7508c2ecf20Sopenharmony_ci		return rc;
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	pr_info("initialized driver\n");
7548c2ecf20Sopenharmony_ci	return 0;
7558c2ecf20Sopenharmony_cierr:
7568c2ecf20Sopenharmony_ci	if (optee) {
7578c2ecf20Sopenharmony_ci		/*
7588c2ecf20Sopenharmony_ci		 * tee_device_unregister() is safe to call even if the
7598c2ecf20Sopenharmony_ci		 * devices hasn't been registered with
7608c2ecf20Sopenharmony_ci		 * tee_device_register() yet.
7618c2ecf20Sopenharmony_ci		 */
7628c2ecf20Sopenharmony_ci		tee_device_unregister(optee->supp_teedev);
7638c2ecf20Sopenharmony_ci		tee_device_unregister(optee->teedev);
7648c2ecf20Sopenharmony_ci		kfree(optee);
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci	if (pool)
7678c2ecf20Sopenharmony_ci		tee_shm_pool_free(pool);
7688c2ecf20Sopenharmony_ci	if (memremaped_shm)
7698c2ecf20Sopenharmony_ci		memunmap(memremaped_shm);
7708c2ecf20Sopenharmony_ci	return rc;
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic const struct of_device_id optee_dt_match[] = {
7748c2ecf20Sopenharmony_ci	{ .compatible = "linaro,optee-tz" },
7758c2ecf20Sopenharmony_ci	{},
7768c2ecf20Sopenharmony_ci};
7778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, optee_dt_match);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic struct platform_driver optee_driver = {
7808c2ecf20Sopenharmony_ci	.probe  = optee_probe,
7818c2ecf20Sopenharmony_ci	.remove = optee_remove,
7828c2ecf20Sopenharmony_ci	.shutdown = optee_shutdown,
7838c2ecf20Sopenharmony_ci	.driver = {
7848c2ecf20Sopenharmony_ci		.name = "optee",
7858c2ecf20Sopenharmony_ci		.of_match_table = optee_dt_match,
7868c2ecf20Sopenharmony_ci	},
7878c2ecf20Sopenharmony_ci};
7888c2ecf20Sopenharmony_cimodule_platform_driver(optee_driver);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linaro");
7918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OP-TEE driver");
7928c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("");
7938c2ecf20Sopenharmony_ciMODULE_VERSION("1.0");
7948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
7958c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:optee");
796