xref: /openbmc/linux/drivers/tee/optee/core.c (revision 9c92ab61)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24fb0a5ebSJens Wiklander /*
34fb0a5ebSJens Wiklander  * Copyright (c) 2015, Linaro Limited
44fb0a5ebSJens Wiklander  */
54fb0a5ebSJens Wiklander 
64fb0a5ebSJens Wiklander #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
74fb0a5ebSJens Wiklander 
84fb0a5ebSJens Wiklander #include <linux/arm-smccc.h>
94fb0a5ebSJens Wiklander #include <linux/errno.h>
104fb0a5ebSJens Wiklander #include <linux/io.h>
114fb0a5ebSJens Wiklander #include <linux/module.h>
124fb0a5ebSJens Wiklander #include <linux/of.h>
134fb0a5ebSJens Wiklander #include <linux/of_platform.h>
144fb0a5ebSJens Wiklander #include <linux/platform_device.h>
154fb0a5ebSJens Wiklander #include <linux/slab.h>
164fb0a5ebSJens Wiklander #include <linux/string.h>
174fb0a5ebSJens Wiklander #include <linux/tee_drv.h>
184fb0a5ebSJens Wiklander #include <linux/types.h>
194fb0a5ebSJens Wiklander #include <linux/uaccess.h>
204fb0a5ebSJens Wiklander #include "optee_private.h"
214fb0a5ebSJens Wiklander #include "optee_smc.h"
22f58e236cSVolodymyr Babchuk #include "shm_pool.h"
234fb0a5ebSJens Wiklander 
244fb0a5ebSJens Wiklander #define DRIVER_NAME "optee"
254fb0a5ebSJens Wiklander 
263249527fSSahil Malhotra #define OPTEE_SHM_NUM_PRIV_PAGES	CONFIG_OPTEE_SHM_NUM_PRIV_PAGES
274fb0a5ebSJens Wiklander 
284fb0a5ebSJens Wiklander /**
294fb0a5ebSJens Wiklander  * optee_from_msg_param() - convert from OPTEE_MSG parameters to
304fb0a5ebSJens Wiklander  *			    struct tee_param
314fb0a5ebSJens Wiklander  * @params:	subsystem internal parameter representation
324fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
334fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
344fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
354fb0a5ebSJens Wiklander  */
364fb0a5ebSJens Wiklander int optee_from_msg_param(struct tee_param *params, size_t num_params,
374fb0a5ebSJens Wiklander 			 const struct optee_msg_param *msg_params)
384fb0a5ebSJens Wiklander {
394fb0a5ebSJens Wiklander 	int rc;
404fb0a5ebSJens Wiklander 	size_t n;
414fb0a5ebSJens Wiklander 	struct tee_shm *shm;
424fb0a5ebSJens Wiklander 	phys_addr_t pa;
434fb0a5ebSJens Wiklander 
444fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
454fb0a5ebSJens Wiklander 		struct tee_param *p = params + n;
464fb0a5ebSJens Wiklander 		const struct optee_msg_param *mp = msg_params + n;
474fb0a5ebSJens Wiklander 		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
484fb0a5ebSJens Wiklander 
494fb0a5ebSJens Wiklander 		switch (attr) {
504fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_NONE:
514fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
524fb0a5ebSJens Wiklander 			memset(&p->u, 0, sizeof(p->u));
534fb0a5ebSJens Wiklander 			break;
544fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
554fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
564fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
574fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
584fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
594fb0a5ebSJens Wiklander 			p->u.value.a = mp->u.value.a;
604fb0a5ebSJens Wiklander 			p->u.value.b = mp->u.value.b;
614fb0a5ebSJens Wiklander 			p->u.value.c = mp->u.value.c;
624fb0a5ebSJens Wiklander 			break;
634fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
644fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
654fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
664fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
674fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
684fb0a5ebSJens Wiklander 			p->u.memref.size = mp->u.tmem.size;
694fb0a5ebSJens Wiklander 			shm = (struct tee_shm *)(unsigned long)
704fb0a5ebSJens Wiklander 				mp->u.tmem.shm_ref;
714fb0a5ebSJens Wiklander 			if (!shm) {
724fb0a5ebSJens Wiklander 				p->u.memref.shm_offs = 0;
734fb0a5ebSJens Wiklander 				p->u.memref.shm = NULL;
744fb0a5ebSJens Wiklander 				break;
754fb0a5ebSJens Wiklander 			}
764fb0a5ebSJens Wiklander 			rc = tee_shm_get_pa(shm, 0, &pa);
774fb0a5ebSJens Wiklander 			if (rc)
784fb0a5ebSJens Wiklander 				return rc;
794fb0a5ebSJens Wiklander 			p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
804fb0a5ebSJens Wiklander 			p->u.memref.shm = shm;
814fb0a5ebSJens Wiklander 
824fb0a5ebSJens Wiklander 			/* Check that the memref is covered by the shm object */
834fb0a5ebSJens Wiklander 			if (p->u.memref.size) {
844fb0a5ebSJens Wiklander 				size_t o = p->u.memref.shm_offs +
854fb0a5ebSJens Wiklander 					   p->u.memref.size - 1;
864fb0a5ebSJens Wiklander 
874fb0a5ebSJens Wiklander 				rc = tee_shm_get_pa(shm, o, NULL);
884fb0a5ebSJens Wiklander 				if (rc)
894fb0a5ebSJens Wiklander 					return rc;
904fb0a5ebSJens Wiklander 			}
914fb0a5ebSJens Wiklander 			break;
9264cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
9364cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
9464cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
9564cf9d8aSVolodymyr Babchuk 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
9664cf9d8aSVolodymyr Babchuk 				  attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
9764cf9d8aSVolodymyr Babchuk 			p->u.memref.size = mp->u.rmem.size;
9864cf9d8aSVolodymyr Babchuk 			shm = (struct tee_shm *)(unsigned long)
9964cf9d8aSVolodymyr Babchuk 				mp->u.rmem.shm_ref;
10064cf9d8aSVolodymyr Babchuk 
10164cf9d8aSVolodymyr Babchuk 			if (!shm) {
10264cf9d8aSVolodymyr Babchuk 				p->u.memref.shm_offs = 0;
10364cf9d8aSVolodymyr Babchuk 				p->u.memref.shm = NULL;
10464cf9d8aSVolodymyr Babchuk 				break;
10564cf9d8aSVolodymyr Babchuk 			}
10664cf9d8aSVolodymyr Babchuk 			p->u.memref.shm_offs = mp->u.rmem.offs;
10764cf9d8aSVolodymyr Babchuk 			p->u.memref.shm = shm;
10864cf9d8aSVolodymyr Babchuk 
10964cf9d8aSVolodymyr Babchuk 			break;
11064cf9d8aSVolodymyr Babchuk 
1114fb0a5ebSJens Wiklander 		default:
1124fb0a5ebSJens Wiklander 			return -EINVAL;
1134fb0a5ebSJens Wiklander 		}
1144fb0a5ebSJens Wiklander 	}
1154fb0a5ebSJens Wiklander 	return 0;
1164fb0a5ebSJens Wiklander }
1174fb0a5ebSJens Wiklander 
11864cf9d8aSVolodymyr Babchuk static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
11964cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
12064cf9d8aSVolodymyr Babchuk {
12164cf9d8aSVolodymyr Babchuk 	int rc;
12264cf9d8aSVolodymyr Babchuk 	phys_addr_t pa;
12364cf9d8aSVolodymyr Babchuk 
12464cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
12564cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
12664cf9d8aSVolodymyr Babchuk 
12764cf9d8aSVolodymyr Babchuk 	mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
12864cf9d8aSVolodymyr Babchuk 	mp->u.tmem.size = p->u.memref.size;
12964cf9d8aSVolodymyr Babchuk 
13064cf9d8aSVolodymyr Babchuk 	if (!p->u.memref.shm) {
13164cf9d8aSVolodymyr Babchuk 		mp->u.tmem.buf_ptr = 0;
13264cf9d8aSVolodymyr Babchuk 		return 0;
13364cf9d8aSVolodymyr Babchuk 	}
13464cf9d8aSVolodymyr Babchuk 
13564cf9d8aSVolodymyr Babchuk 	rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
13664cf9d8aSVolodymyr Babchuk 	if (rc)
13764cf9d8aSVolodymyr Babchuk 		return rc;
13864cf9d8aSVolodymyr Babchuk 
13964cf9d8aSVolodymyr Babchuk 	mp->u.tmem.buf_ptr = pa;
14064cf9d8aSVolodymyr Babchuk 	mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
14164cf9d8aSVolodymyr Babchuk 		    OPTEE_MSG_ATTR_CACHE_SHIFT;
14264cf9d8aSVolodymyr Babchuk 
14364cf9d8aSVolodymyr Babchuk 	return 0;
14464cf9d8aSVolodymyr Babchuk }
14564cf9d8aSVolodymyr Babchuk 
14664cf9d8aSVolodymyr Babchuk static int to_msg_param_reg_mem(struct optee_msg_param *mp,
14764cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
14864cf9d8aSVolodymyr Babchuk {
14964cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
15064cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
15164cf9d8aSVolodymyr Babchuk 
15264cf9d8aSVolodymyr Babchuk 	mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
15364cf9d8aSVolodymyr Babchuk 	mp->u.rmem.size = p->u.memref.size;
15464cf9d8aSVolodymyr Babchuk 	mp->u.rmem.offs = p->u.memref.shm_offs;
15564cf9d8aSVolodymyr Babchuk 	return 0;
15664cf9d8aSVolodymyr Babchuk }
15764cf9d8aSVolodymyr Babchuk 
1584fb0a5ebSJens Wiklander /**
1594fb0a5ebSJens Wiklander  * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
1604fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
1614fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
1624fb0a5ebSJens Wiklander  * @params:	subsystem itnernal parameter representation
1634fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
1644fb0a5ebSJens Wiklander  */
1654fb0a5ebSJens Wiklander int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
1664fb0a5ebSJens Wiklander 		       const struct tee_param *params)
1674fb0a5ebSJens Wiklander {
1684fb0a5ebSJens Wiklander 	int rc;
1694fb0a5ebSJens Wiklander 	size_t n;
1704fb0a5ebSJens Wiklander 
1714fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
1724fb0a5ebSJens Wiklander 		const struct tee_param *p = params + n;
1734fb0a5ebSJens Wiklander 		struct optee_msg_param *mp = msg_params + n;
1744fb0a5ebSJens Wiklander 
1754fb0a5ebSJens Wiklander 		switch (p->attr) {
1764fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
1774fb0a5ebSJens Wiklander 			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
1784fb0a5ebSJens Wiklander 			memset(&mp->u, 0, sizeof(mp->u));
1794fb0a5ebSJens Wiklander 			break;
1804fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
1814fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
1824fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
1834fb0a5ebSJens Wiklander 			mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
1844fb0a5ebSJens Wiklander 				   TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
1854fb0a5ebSJens Wiklander 			mp->u.value.a = p->u.value.a;
1864fb0a5ebSJens Wiklander 			mp->u.value.b = p->u.value.b;
1874fb0a5ebSJens Wiklander 			mp->u.value.c = p->u.value.c;
1884fb0a5ebSJens Wiklander 			break;
1894fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
1904fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
1914fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
19264cf9d8aSVolodymyr Babchuk 			if (tee_shm_is_registered(p->u.memref.shm))
19364cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_reg_mem(mp, p);
19464cf9d8aSVolodymyr Babchuk 			else
19564cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_tmp_mem(mp, p);
1964fb0a5ebSJens Wiklander 			if (rc)
1974fb0a5ebSJens Wiklander 				return rc;
1984fb0a5ebSJens Wiklander 			break;
1994fb0a5ebSJens Wiklander 		default:
2004fb0a5ebSJens Wiklander 			return -EINVAL;
2014fb0a5ebSJens Wiklander 		}
2024fb0a5ebSJens Wiklander 	}
2034fb0a5ebSJens Wiklander 	return 0;
2044fb0a5ebSJens Wiklander }
2054fb0a5ebSJens Wiklander 
2064fb0a5ebSJens Wiklander static void optee_get_version(struct tee_device *teedev,
2074fb0a5ebSJens Wiklander 			      struct tee_ioctl_version_data *vers)
2084fb0a5ebSJens Wiklander {
2094fb0a5ebSJens Wiklander 	struct tee_ioctl_version_data v = {
2104fb0a5ebSJens Wiklander 		.impl_id = TEE_IMPL_ID_OPTEE,
2114fb0a5ebSJens Wiklander 		.impl_caps = TEE_OPTEE_CAP_TZ,
2124fb0a5ebSJens Wiklander 		.gen_caps = TEE_GEN_CAP_GP,
2134fb0a5ebSJens Wiklander 	};
214f58e236cSVolodymyr Babchuk 	struct optee *optee = tee_get_drvdata(teedev);
215f58e236cSVolodymyr Babchuk 
216f58e236cSVolodymyr Babchuk 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
217f58e236cSVolodymyr Babchuk 		v.gen_caps |= TEE_GEN_CAP_REG_MEM;
2184fb0a5ebSJens Wiklander 	*vers = v;
2194fb0a5ebSJens Wiklander }
2204fb0a5ebSJens Wiklander 
2214fb0a5ebSJens Wiklander static int optee_open(struct tee_context *ctx)
2224fb0a5ebSJens Wiklander {
2234fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata;
2244fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2254fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2264fb0a5ebSJens Wiklander 
2274fb0a5ebSJens Wiklander 	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
2284fb0a5ebSJens Wiklander 	if (!ctxdata)
2294fb0a5ebSJens Wiklander 		return -ENOMEM;
2304fb0a5ebSJens Wiklander 
2314fb0a5ebSJens Wiklander 	if (teedev == optee->supp_teedev) {
2324fb0a5ebSJens Wiklander 		bool busy = true;
2334fb0a5ebSJens Wiklander 
2341647a5acSJens Wiklander 		mutex_lock(&optee->supp.mutex);
2354fb0a5ebSJens Wiklander 		if (!optee->supp.ctx) {
2364fb0a5ebSJens Wiklander 			busy = false;
2374fb0a5ebSJens Wiklander 			optee->supp.ctx = ctx;
2384fb0a5ebSJens Wiklander 		}
2391647a5acSJens Wiklander 		mutex_unlock(&optee->supp.mutex);
2404fb0a5ebSJens Wiklander 		if (busy) {
2414fb0a5ebSJens Wiklander 			kfree(ctxdata);
2424fb0a5ebSJens Wiklander 			return -EBUSY;
2434fb0a5ebSJens Wiklander 		}
2444fb0a5ebSJens Wiklander 	}
2454fb0a5ebSJens Wiklander 
2464fb0a5ebSJens Wiklander 	mutex_init(&ctxdata->mutex);
2474fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&ctxdata->sess_list);
2484fb0a5ebSJens Wiklander 
2494fb0a5ebSJens Wiklander 	ctx->data = ctxdata;
2504fb0a5ebSJens Wiklander 	return 0;
2514fb0a5ebSJens Wiklander }
2524fb0a5ebSJens Wiklander 
2534fb0a5ebSJens Wiklander static void optee_release(struct tee_context *ctx)
2544fb0a5ebSJens Wiklander {
2554fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata = ctx->data;
2564fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2574fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2584fb0a5ebSJens Wiklander 	struct tee_shm *shm;
2594fb0a5ebSJens Wiklander 	struct optee_msg_arg *arg = NULL;
2604fb0a5ebSJens Wiklander 	phys_addr_t parg;
2614fb0a5ebSJens Wiklander 	struct optee_session *sess;
2624fb0a5ebSJens Wiklander 	struct optee_session *sess_tmp;
2634fb0a5ebSJens Wiklander 
2644fb0a5ebSJens Wiklander 	if (!ctxdata)
2654fb0a5ebSJens Wiklander 		return;
2664fb0a5ebSJens Wiklander 
2674fb0a5ebSJens Wiklander 	shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED);
2684fb0a5ebSJens Wiklander 	if (!IS_ERR(shm)) {
2694fb0a5ebSJens Wiklander 		arg = tee_shm_get_va(shm, 0);
2704fb0a5ebSJens Wiklander 		/*
271efb14036SJens Wiklander 		 * If va2pa fails for some reason, we can't call into
272efb14036SJens Wiklander 		 * secure world, only free the memory. Secure OS will leak
273efb14036SJens Wiklander 		 * sessions and finally refuse more sessions, but we will
274efb14036SJens Wiklander 		 * at least let normal world reclaim its memory.
2754fb0a5ebSJens Wiklander 		 */
2764fb0a5ebSJens Wiklander 		if (!IS_ERR(arg))
277efb14036SJens Wiklander 			if (tee_shm_va2pa(shm, arg, &parg))
278efb14036SJens Wiklander 				arg = NULL; /* prevent usage of parg below */
2794fb0a5ebSJens Wiklander 	}
2804fb0a5ebSJens Wiklander 
2814fb0a5ebSJens Wiklander 	list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
2824fb0a5ebSJens Wiklander 				 list_node) {
2834fb0a5ebSJens Wiklander 		list_del(&sess->list_node);
2844fb0a5ebSJens Wiklander 		if (!IS_ERR_OR_NULL(arg)) {
2854fb0a5ebSJens Wiklander 			memset(arg, 0, sizeof(*arg));
2864fb0a5ebSJens Wiklander 			arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
2874fb0a5ebSJens Wiklander 			arg->session = sess->session_id;
2884fb0a5ebSJens Wiklander 			optee_do_call_with_arg(ctx, parg);
2894fb0a5ebSJens Wiklander 		}
2904fb0a5ebSJens Wiklander 		kfree(sess);
2914fb0a5ebSJens Wiklander 	}
2924fb0a5ebSJens Wiklander 	kfree(ctxdata);
2934fb0a5ebSJens Wiklander 
2944fb0a5ebSJens Wiklander 	if (!IS_ERR(shm))
2954fb0a5ebSJens Wiklander 		tee_shm_free(shm);
2964fb0a5ebSJens Wiklander 
2974fb0a5ebSJens Wiklander 	ctx->data = NULL;
2984fb0a5ebSJens Wiklander 
2991647a5acSJens Wiklander 	if (teedev == optee->supp_teedev)
3001647a5acSJens Wiklander 		optee_supp_release(&optee->supp);
3014fb0a5ebSJens Wiklander }
3024fb0a5ebSJens Wiklander 
30396e72ddeSBhumika Goyal static const struct tee_driver_ops optee_ops = {
3044fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3054fb0a5ebSJens Wiklander 	.open = optee_open,
3064fb0a5ebSJens Wiklander 	.release = optee_release,
3074fb0a5ebSJens Wiklander 	.open_session = optee_open_session,
3084fb0a5ebSJens Wiklander 	.close_session = optee_close_session,
3094fb0a5ebSJens Wiklander 	.invoke_func = optee_invoke_func,
3104fb0a5ebSJens Wiklander 	.cancel_req = optee_cancel_req,
31106ca7917SVolodymyr Babchuk 	.shm_register = optee_shm_register,
31206ca7917SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister,
3134fb0a5ebSJens Wiklander };
3144fb0a5ebSJens Wiklander 
31596e72ddeSBhumika Goyal static const struct tee_desc optee_desc = {
3164fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-clnt",
3174fb0a5ebSJens Wiklander 	.ops = &optee_ops,
3184fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3194fb0a5ebSJens Wiklander };
3204fb0a5ebSJens Wiklander 
32196e72ddeSBhumika Goyal static const struct tee_driver_ops optee_supp_ops = {
3224fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3234fb0a5ebSJens Wiklander 	.open = optee_open,
3244fb0a5ebSJens Wiklander 	.release = optee_release,
3254fb0a5ebSJens Wiklander 	.supp_recv = optee_supp_recv,
3264fb0a5ebSJens Wiklander 	.supp_send = optee_supp_send,
32753a107c8SVolodymyr Babchuk 	.shm_register = optee_shm_register_supp,
32853a107c8SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister_supp,
3294fb0a5ebSJens Wiklander };
3304fb0a5ebSJens Wiklander 
33196e72ddeSBhumika Goyal static const struct tee_desc optee_supp_desc = {
3324fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-supp",
3334fb0a5ebSJens Wiklander 	.ops = &optee_supp_ops,
3344fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3354fb0a5ebSJens Wiklander 	.flags = TEE_DESC_PRIVILEGED,
3364fb0a5ebSJens Wiklander };
3374fb0a5ebSJens Wiklander 
3384fb0a5ebSJens Wiklander static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
3394fb0a5ebSJens Wiklander {
3404fb0a5ebSJens Wiklander 	struct arm_smccc_res res;
3414fb0a5ebSJens Wiklander 
3424fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
3434fb0a5ebSJens Wiklander 
3444fb0a5ebSJens Wiklander 	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
3454fb0a5ebSJens Wiklander 	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
3464fb0a5ebSJens Wiklander 		return true;
3474fb0a5ebSJens Wiklander 	return false;
3484fb0a5ebSJens Wiklander }
3494fb0a5ebSJens Wiklander 
3505c5f8030SJérôme Forissier static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
3515c5f8030SJérôme Forissier {
3525c5f8030SJérôme Forissier 	union {
3535c5f8030SJérôme Forissier 		struct arm_smccc_res smccc;
3545c5f8030SJérôme Forissier 		struct optee_smc_call_get_os_revision_result result;
3555c5f8030SJérôme Forissier 	} res = {
3565c5f8030SJérôme Forissier 		.result = {
3575c5f8030SJérôme Forissier 			.build_id = 0
3585c5f8030SJérôme Forissier 		}
3595c5f8030SJérôme Forissier 	};
3605c5f8030SJérôme Forissier 
3615c5f8030SJérôme Forissier 	invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
3625c5f8030SJérôme Forissier 		  &res.smccc);
3635c5f8030SJérôme Forissier 
3645c5f8030SJérôme Forissier 	if (res.result.build_id)
3655c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu (%08lx)", res.result.major,
3665c5f8030SJérôme Forissier 			res.result.minor, res.result.build_id);
3675c5f8030SJérôme Forissier 	else
3685c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu", res.result.major, res.result.minor);
3695c5f8030SJérôme Forissier }
3705c5f8030SJérôme Forissier 
3714fb0a5ebSJens Wiklander static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
3724fb0a5ebSJens Wiklander {
3734fb0a5ebSJens Wiklander 	union {
3744fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
3754fb0a5ebSJens Wiklander 		struct optee_smc_calls_revision_result result;
3764fb0a5ebSJens Wiklander 	} res;
3774fb0a5ebSJens Wiklander 
3784fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
3794fb0a5ebSJens Wiklander 
3804fb0a5ebSJens Wiklander 	if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
3814fb0a5ebSJens Wiklander 	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
3824fb0a5ebSJens Wiklander 		return true;
3834fb0a5ebSJens Wiklander 	return false;
3844fb0a5ebSJens Wiklander }
3854fb0a5ebSJens Wiklander 
3864fb0a5ebSJens Wiklander static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
3874fb0a5ebSJens Wiklander 					    u32 *sec_caps)
3884fb0a5ebSJens Wiklander {
3894fb0a5ebSJens Wiklander 	union {
3904fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
3914fb0a5ebSJens Wiklander 		struct optee_smc_exchange_capabilities_result result;
3924fb0a5ebSJens Wiklander 	} res;
3934fb0a5ebSJens Wiklander 	u32 a1 = 0;
3944fb0a5ebSJens Wiklander 
3954fb0a5ebSJens Wiklander 	/*
3964fb0a5ebSJens Wiklander 	 * TODO This isn't enough to tell if it's UP system (from kernel
3974fb0a5ebSJens Wiklander 	 * point of view) or not, is_smp() returns the the information
3984fb0a5ebSJens Wiklander 	 * needed, but can't be called directly from here.
3994fb0a5ebSJens Wiklander 	 */
4004fb0a5ebSJens Wiklander 	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1)
4014fb0a5ebSJens Wiklander 		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
4024fb0a5ebSJens Wiklander 
4034fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0,
4044fb0a5ebSJens Wiklander 		  &res.smccc);
4054fb0a5ebSJens Wiklander 
4064fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK)
4074fb0a5ebSJens Wiklander 		return false;
4084fb0a5ebSJens Wiklander 
4094fb0a5ebSJens Wiklander 	*sec_caps = res.result.capabilities;
4104fb0a5ebSJens Wiklander 	return true;
4114fb0a5ebSJens Wiklander }
4124fb0a5ebSJens Wiklander 
4139733b072SVolodymyr Babchuk static struct tee_shm_pool *optee_config_dyn_shm(void)
4149733b072SVolodymyr Babchuk {
4159733b072SVolodymyr Babchuk 	struct tee_shm_pool_mgr *priv_mgr;
4169733b072SVolodymyr Babchuk 	struct tee_shm_pool_mgr *dmabuf_mgr;
4179733b072SVolodymyr Babchuk 	void *rc;
4189733b072SVolodymyr Babchuk 
4199733b072SVolodymyr Babchuk 	rc = optee_shm_pool_alloc_pages();
4209733b072SVolodymyr Babchuk 	if (IS_ERR(rc))
4219733b072SVolodymyr Babchuk 		return rc;
4229733b072SVolodymyr Babchuk 	priv_mgr = rc;
4239733b072SVolodymyr Babchuk 
4249733b072SVolodymyr Babchuk 	rc = optee_shm_pool_alloc_pages();
4259733b072SVolodymyr Babchuk 	if (IS_ERR(rc)) {
4269733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(priv_mgr);
4279733b072SVolodymyr Babchuk 		return rc;
4289733b072SVolodymyr Babchuk 	}
4299733b072SVolodymyr Babchuk 	dmabuf_mgr = rc;
4309733b072SVolodymyr Babchuk 
4319733b072SVolodymyr Babchuk 	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
4329733b072SVolodymyr Babchuk 	if (IS_ERR(rc)) {
4339733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(priv_mgr);
4349733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(dmabuf_mgr);
4359733b072SVolodymyr Babchuk 	}
4369733b072SVolodymyr Babchuk 
4379733b072SVolodymyr Babchuk 	return rc;
4389733b072SVolodymyr Babchuk }
4399733b072SVolodymyr Babchuk 
4404fb0a5ebSJens Wiklander static struct tee_shm_pool *
4419733b072SVolodymyr Babchuk optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
4424fb0a5ebSJens Wiklander {
4434fb0a5ebSJens Wiklander 	union {
4444fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4454fb0a5ebSJens Wiklander 		struct optee_smc_get_shm_config_result result;
4464fb0a5ebSJens Wiklander 	} res;
4474fb0a5ebSJens Wiklander 	unsigned long vaddr;
4484fb0a5ebSJens Wiklander 	phys_addr_t paddr;
4494fb0a5ebSJens Wiklander 	size_t size;
4504fb0a5ebSJens Wiklander 	phys_addr_t begin;
4514fb0a5ebSJens Wiklander 	phys_addr_t end;
4524fb0a5ebSJens Wiklander 	void *va;
453f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *priv_mgr;
454f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *dmabuf_mgr;
455f58e236cSVolodymyr Babchuk 	void *rc;
4569733b072SVolodymyr Babchuk 	const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4574fb0a5ebSJens Wiklander 
4584fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
4594fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK) {
4609733b072SVolodymyr Babchuk 		pr_err("static shm service not available\n");
4614fb0a5ebSJens Wiklander 		return ERR_PTR(-ENOENT);
4624fb0a5ebSJens Wiklander 	}
4634fb0a5ebSJens Wiklander 
4644fb0a5ebSJens Wiklander 	if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
4654fb0a5ebSJens Wiklander 		pr_err("only normal cached shared memory supported\n");
4664fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4674fb0a5ebSJens Wiklander 	}
4684fb0a5ebSJens Wiklander 
4694fb0a5ebSJens Wiklander 	begin = roundup(res.result.start, PAGE_SIZE);
4704fb0a5ebSJens Wiklander 	end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
4714fb0a5ebSJens Wiklander 	paddr = begin;
4724fb0a5ebSJens Wiklander 	size = end - begin;
4734fb0a5ebSJens Wiklander 
4744fb0a5ebSJens Wiklander 	if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) {
4754fb0a5ebSJens Wiklander 		pr_err("too small shared memory area\n");
4764fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4774fb0a5ebSJens Wiklander 	}
4784fb0a5ebSJens Wiklander 
4794fb0a5ebSJens Wiklander 	va = memremap(paddr, size, MEMREMAP_WB);
4804fb0a5ebSJens Wiklander 	if (!va) {
4814fb0a5ebSJens Wiklander 		pr_err("shared memory ioremap failed\n");
4824fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4834fb0a5ebSJens Wiklander 	}
4844fb0a5ebSJens Wiklander 	vaddr = (unsigned long)va;
4854fb0a5ebSJens Wiklander 
486f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
487f58e236cSVolodymyr Babchuk 					    3 /* 8 bytes aligned */);
488f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
489f58e236cSVolodymyr Babchuk 		goto err_memunmap;
490f58e236cSVolodymyr Babchuk 	priv_mgr = rc;
491f58e236cSVolodymyr Babchuk 
492f58e236cSVolodymyr Babchuk 	vaddr += sz;
493f58e236cSVolodymyr Babchuk 	paddr += sz;
494f58e236cSVolodymyr Babchuk 	size -= sz;
4954fb0a5ebSJens Wiklander 
496f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
497f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
498f58e236cSVolodymyr Babchuk 		goto err_free_priv_mgr;
499f58e236cSVolodymyr Babchuk 	dmabuf_mgr = rc;
500f58e236cSVolodymyr Babchuk 
501f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
502f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
503f58e236cSVolodymyr Babchuk 		goto err_free_dmabuf_mgr;
504f58e236cSVolodymyr Babchuk 
5054fb0a5ebSJens Wiklander 	*memremaped_shm = va;
506f58e236cSVolodymyr Babchuk 
507f58e236cSVolodymyr Babchuk 	return rc;
508f58e236cSVolodymyr Babchuk 
509f58e236cSVolodymyr Babchuk err_free_dmabuf_mgr:
510f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(dmabuf_mgr);
511f58e236cSVolodymyr Babchuk err_free_priv_mgr:
512f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(priv_mgr);
513f58e236cSVolodymyr Babchuk err_memunmap:
514f58e236cSVolodymyr Babchuk 	memunmap(va);
515f58e236cSVolodymyr Babchuk 	return rc;
5164fb0a5ebSJens Wiklander }
5174fb0a5ebSJens Wiklander 
5184fb0a5ebSJens Wiklander /* Simple wrapper functions to be able to use a function pointer */
5194fb0a5ebSJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1,
5204fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5214fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5224fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5234fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5244fb0a5ebSJens Wiklander {
5254fb0a5ebSJens Wiklander 	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5264fb0a5ebSJens Wiklander }
5274fb0a5ebSJens Wiklander 
5284fb0a5ebSJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
5294fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5304fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5314fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5324fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5334fb0a5ebSJens Wiklander {
5344fb0a5ebSJens Wiklander 	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5354fb0a5ebSJens Wiklander }
5364fb0a5ebSJens Wiklander 
5374fb0a5ebSJens Wiklander static optee_invoke_fn *get_invoke_func(struct device_node *np)
5384fb0a5ebSJens Wiklander {
5394fb0a5ebSJens Wiklander 	const char *method;
5404fb0a5ebSJens Wiklander 
5414fb0a5ebSJens Wiklander 	pr_info("probing for conduit method from DT.\n");
5424fb0a5ebSJens Wiklander 
5434fb0a5ebSJens Wiklander 	if (of_property_read_string(np, "method", &method)) {
5444fb0a5ebSJens Wiklander 		pr_warn("missing \"method\" property\n");
5454fb0a5ebSJens Wiklander 		return ERR_PTR(-ENXIO);
5464fb0a5ebSJens Wiklander 	}
5474fb0a5ebSJens Wiklander 
5484fb0a5ebSJens Wiklander 	if (!strcmp("hvc", method))
5494fb0a5ebSJens Wiklander 		return optee_smccc_hvc;
5504fb0a5ebSJens Wiklander 	else if (!strcmp("smc", method))
5514fb0a5ebSJens Wiklander 		return optee_smccc_smc;
5524fb0a5ebSJens Wiklander 
5534fb0a5ebSJens Wiklander 	pr_warn("invalid \"method\" property: %s\n", method);
5544fb0a5ebSJens Wiklander 	return ERR_PTR(-EINVAL);
5554fb0a5ebSJens Wiklander }
5564fb0a5ebSJens Wiklander 
5574fb0a5ebSJens Wiklander static struct optee *optee_probe(struct device_node *np)
5584fb0a5ebSJens Wiklander {
5594fb0a5ebSJens Wiklander 	optee_invoke_fn *invoke_fn;
5609733b072SVolodymyr Babchuk 	struct tee_shm_pool *pool = ERR_PTR(-EINVAL);
5614fb0a5ebSJens Wiklander 	struct optee *optee = NULL;
5624fb0a5ebSJens Wiklander 	void *memremaped_shm = NULL;
5634fb0a5ebSJens Wiklander 	struct tee_device *teedev;
5644fb0a5ebSJens Wiklander 	u32 sec_caps;
5654fb0a5ebSJens Wiklander 	int rc;
5664fb0a5ebSJens Wiklander 
5674fb0a5ebSJens Wiklander 	invoke_fn = get_invoke_func(np);
5684fb0a5ebSJens Wiklander 	if (IS_ERR(invoke_fn))
5694fb0a5ebSJens Wiklander 		return (void *)invoke_fn;
5704fb0a5ebSJens Wiklander 
5714fb0a5ebSJens Wiklander 	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
5724fb0a5ebSJens Wiklander 		pr_warn("api uid mismatch\n");
5734fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5744fb0a5ebSJens Wiklander 	}
5754fb0a5ebSJens Wiklander 
5765c5f8030SJérôme Forissier 	optee_msg_get_os_revision(invoke_fn);
5775c5f8030SJérôme Forissier 
5784fb0a5ebSJens Wiklander 	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
5794fb0a5ebSJens Wiklander 		pr_warn("api revision mismatch\n");
5804fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5814fb0a5ebSJens Wiklander 	}
5824fb0a5ebSJens Wiklander 
5834fb0a5ebSJens Wiklander 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
5844fb0a5ebSJens Wiklander 		pr_warn("capabilities mismatch\n");
5854fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5864fb0a5ebSJens Wiklander 	}
5874fb0a5ebSJens Wiklander 
5884fb0a5ebSJens Wiklander 	/*
5899733b072SVolodymyr Babchuk 	 * Try to use dynamic shared memory if possible
5904fb0a5ebSJens Wiklander 	 */
5919733b072SVolodymyr Babchuk 	if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
5929733b072SVolodymyr Babchuk 		pool = optee_config_dyn_shm();
5934fb0a5ebSJens Wiklander 
5949733b072SVolodymyr Babchuk 	/*
5959733b072SVolodymyr Babchuk 	 * If dynamic shared memory is not available or failed - try static one
5969733b072SVolodymyr Babchuk 	 */
5979733b072SVolodymyr Babchuk 	if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
5989733b072SVolodymyr Babchuk 		pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
5999733b072SVolodymyr Babchuk 
6004fb0a5ebSJens Wiklander 	if (IS_ERR(pool))
6014fb0a5ebSJens Wiklander 		return (void *)pool;
6024fb0a5ebSJens Wiklander 
6034fb0a5ebSJens Wiklander 	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
6044fb0a5ebSJens Wiklander 	if (!optee) {
6054fb0a5ebSJens Wiklander 		rc = -ENOMEM;
6064fb0a5ebSJens Wiklander 		goto err;
6074fb0a5ebSJens Wiklander 	}
6084fb0a5ebSJens Wiklander 
6094fb0a5ebSJens Wiklander 	optee->invoke_fn = invoke_fn;
610d885cc5eSVolodymyr Babchuk 	optee->sec_caps = sec_caps;
6114fb0a5ebSJens Wiklander 
6124fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
6134fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
6144fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
6154fb0a5ebSJens Wiklander 		goto err;
6164fb0a5ebSJens Wiklander 	}
6174fb0a5ebSJens Wiklander 	optee->teedev = teedev;
6184fb0a5ebSJens Wiklander 
6194fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
6204fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
6214fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
6224fb0a5ebSJens Wiklander 		goto err;
6234fb0a5ebSJens Wiklander 	}
6244fb0a5ebSJens Wiklander 	optee->supp_teedev = teedev;
6254fb0a5ebSJens Wiklander 
6264fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->teedev);
6274fb0a5ebSJens Wiklander 	if (rc)
6284fb0a5ebSJens Wiklander 		goto err;
6294fb0a5ebSJens Wiklander 
6304fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->supp_teedev);
6314fb0a5ebSJens Wiklander 	if (rc)
6324fb0a5ebSJens Wiklander 		goto err;
6334fb0a5ebSJens Wiklander 
6344fb0a5ebSJens Wiklander 	mutex_init(&optee->call_queue.mutex);
6354fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&optee->call_queue.waiters);
6364fb0a5ebSJens Wiklander 	optee_wait_queue_init(&optee->wait_queue);
6374fb0a5ebSJens Wiklander 	optee_supp_init(&optee->supp);
6384fb0a5ebSJens Wiklander 	optee->memremaped_shm = memremaped_shm;
6394fb0a5ebSJens Wiklander 	optee->pool = pool;
6404fb0a5ebSJens Wiklander 
6414fb0a5ebSJens Wiklander 	optee_enable_shm_cache(optee);
6424fb0a5ebSJens Wiklander 
6433c15ddb9SVictor Chong 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
6443c15ddb9SVictor Chong 		pr_info("dynamic shared memory is enabled\n");
6453c15ddb9SVictor Chong 
646c3fa24afSSumit Garg 	rc = optee_enumerate_devices();
647c3fa24afSSumit Garg 	if (rc)
648c3fa24afSSumit Garg 		goto err;
649c3fa24afSSumit Garg 
6504fb0a5ebSJens Wiklander 	pr_info("initialized driver\n");
6514fb0a5ebSJens Wiklander 	return optee;
6524fb0a5ebSJens Wiklander err:
6534fb0a5ebSJens Wiklander 	if (optee) {
6544fb0a5ebSJens Wiklander 		/*
6554fb0a5ebSJens Wiklander 		 * tee_device_unregister() is safe to call even if the
6564fb0a5ebSJens Wiklander 		 * devices hasn't been registered with
6574fb0a5ebSJens Wiklander 		 * tee_device_register() yet.
6584fb0a5ebSJens Wiklander 		 */
6594fb0a5ebSJens Wiklander 		tee_device_unregister(optee->supp_teedev);
6604fb0a5ebSJens Wiklander 		tee_device_unregister(optee->teedev);
6614fb0a5ebSJens Wiklander 		kfree(optee);
6624fb0a5ebSJens Wiklander 	}
6634fb0a5ebSJens Wiklander 	if (pool)
6644fb0a5ebSJens Wiklander 		tee_shm_pool_free(pool);
6654fb0a5ebSJens Wiklander 	if (memremaped_shm)
6664fb0a5ebSJens Wiklander 		memunmap(memremaped_shm);
6674fb0a5ebSJens Wiklander 	return ERR_PTR(rc);
6684fb0a5ebSJens Wiklander }
6694fb0a5ebSJens Wiklander 
6704fb0a5ebSJens Wiklander static void optee_remove(struct optee *optee)
6714fb0a5ebSJens Wiklander {
6724fb0a5ebSJens Wiklander 	/*
6734fb0a5ebSJens Wiklander 	 * Ask OP-TEE to free all cached shared memory objects to decrease
6744fb0a5ebSJens Wiklander 	 * reference counters and also avoid wild pointers in secure world
6754fb0a5ebSJens Wiklander 	 * into the old shared memory range.
6764fb0a5ebSJens Wiklander 	 */
6774fb0a5ebSJens Wiklander 	optee_disable_shm_cache(optee);
6784fb0a5ebSJens Wiklander 
6794fb0a5ebSJens Wiklander 	/*
6804fb0a5ebSJens Wiklander 	 * The two devices has to be unregistered before we can free the
6814fb0a5ebSJens Wiklander 	 * other resources.
6824fb0a5ebSJens Wiklander 	 */
6834fb0a5ebSJens Wiklander 	tee_device_unregister(optee->supp_teedev);
6844fb0a5ebSJens Wiklander 	tee_device_unregister(optee->teedev);
6854fb0a5ebSJens Wiklander 
6864fb0a5ebSJens Wiklander 	tee_shm_pool_free(optee->pool);
6874fb0a5ebSJens Wiklander 	if (optee->memremaped_shm)
6884fb0a5ebSJens Wiklander 		memunmap(optee->memremaped_shm);
6894fb0a5ebSJens Wiklander 	optee_wait_queue_exit(&optee->wait_queue);
6904fb0a5ebSJens Wiklander 	optee_supp_uninit(&optee->supp);
6914fb0a5ebSJens Wiklander 	mutex_destroy(&optee->call_queue.mutex);
6924fb0a5ebSJens Wiklander 
6934fb0a5ebSJens Wiklander 	kfree(optee);
6944fb0a5ebSJens Wiklander }
6954fb0a5ebSJens Wiklander 
6964fb0a5ebSJens Wiklander static const struct of_device_id optee_match[] = {
6974fb0a5ebSJens Wiklander 	{ .compatible = "linaro,optee-tz" },
6984fb0a5ebSJens Wiklander 	{},
6994fb0a5ebSJens Wiklander };
7004fb0a5ebSJens Wiklander 
7014fb0a5ebSJens Wiklander static struct optee *optee_svc;
7024fb0a5ebSJens Wiklander 
7034fb0a5ebSJens Wiklander static int __init optee_driver_init(void)
7044fb0a5ebSJens Wiklander {
7054fb0a5ebSJens Wiklander 	struct device_node *fw_np;
7064fb0a5ebSJens Wiklander 	struct device_node *np;
7074fb0a5ebSJens Wiklander 	struct optee *optee;
7084fb0a5ebSJens Wiklander 
7094fb0a5ebSJens Wiklander 	/* Node is supposed to be below /firmware */
7104fb0a5ebSJens Wiklander 	fw_np = of_find_node_by_name(NULL, "firmware");
7114fb0a5ebSJens Wiklander 	if (!fw_np)
7124fb0a5ebSJens Wiklander 		return -ENODEV;
7134fb0a5ebSJens Wiklander 
7144fb0a5ebSJens Wiklander 	np = of_find_matching_node(fw_np, optee_match);
715c7c0d8dfSJulia Lawall 	if (!np || !of_device_is_available(np)) {
716c7c0d8dfSJulia Lawall 		of_node_put(np);
7174fb0a5ebSJens Wiklander 		return -ENODEV;
718c7c0d8dfSJulia Lawall 	}
7194fb0a5ebSJens Wiklander 
7204fb0a5ebSJens Wiklander 	optee = optee_probe(np);
7214fb0a5ebSJens Wiklander 	of_node_put(np);
7224fb0a5ebSJens Wiklander 
7234fb0a5ebSJens Wiklander 	if (IS_ERR(optee))
7244fb0a5ebSJens Wiklander 		return PTR_ERR(optee);
7254fb0a5ebSJens Wiklander 
7264fb0a5ebSJens Wiklander 	optee_svc = optee;
7274fb0a5ebSJens Wiklander 
7284fb0a5ebSJens Wiklander 	return 0;
7294fb0a5ebSJens Wiklander }
7304fb0a5ebSJens Wiklander module_init(optee_driver_init);
7314fb0a5ebSJens Wiklander 
7324fb0a5ebSJens Wiklander static void __exit optee_driver_exit(void)
7334fb0a5ebSJens Wiklander {
7344fb0a5ebSJens Wiklander 	struct optee *optee = optee_svc;
7354fb0a5ebSJens Wiklander 
7364fb0a5ebSJens Wiklander 	optee_svc = NULL;
7374fb0a5ebSJens Wiklander 	if (optee)
7384fb0a5ebSJens Wiklander 		optee_remove(optee);
7394fb0a5ebSJens Wiklander }
7404fb0a5ebSJens Wiklander module_exit(optee_driver_exit);
7414fb0a5ebSJens Wiklander 
7424fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro");
7434fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver");
7444fb0a5ebSJens Wiklander MODULE_SUPPORTED_DEVICE("");
7454fb0a5ebSJens Wiklander MODULE_VERSION("1.0");
7464fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2");
747