xref: /openbmc/linux/drivers/tee/optee/core.c (revision c7c0d8df)
14fb0a5ebSJens Wiklander /*
24fb0a5ebSJens Wiklander  * Copyright (c) 2015, Linaro Limited
34fb0a5ebSJens Wiklander  *
44fb0a5ebSJens Wiklander  * This software is licensed under the terms of the GNU General Public
54fb0a5ebSJens Wiklander  * License version 2, as published by the Free Software Foundation, and
64fb0a5ebSJens Wiklander  * may be copied, distributed, and modified under those terms.
74fb0a5ebSJens Wiklander  *
84fb0a5ebSJens Wiklander  * This program is distributed in the hope that it will be useful,
94fb0a5ebSJens Wiklander  * but WITHOUT ANY WARRANTY; without even the implied warranty of
104fb0a5ebSJens Wiklander  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
114fb0a5ebSJens Wiklander  * GNU General Public License for more details.
124fb0a5ebSJens Wiklander  *
134fb0a5ebSJens Wiklander  */
144fb0a5ebSJens Wiklander 
154fb0a5ebSJens Wiklander #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
164fb0a5ebSJens Wiklander 
174fb0a5ebSJens Wiklander #include <linux/arm-smccc.h>
184fb0a5ebSJens Wiklander #include <linux/errno.h>
194fb0a5ebSJens Wiklander #include <linux/io.h>
204fb0a5ebSJens Wiklander #include <linux/module.h>
214fb0a5ebSJens Wiklander #include <linux/of.h>
224fb0a5ebSJens Wiklander #include <linux/of_platform.h>
234fb0a5ebSJens Wiklander #include <linux/platform_device.h>
244fb0a5ebSJens Wiklander #include <linux/slab.h>
254fb0a5ebSJens Wiklander #include <linux/string.h>
264fb0a5ebSJens Wiklander #include <linux/tee_drv.h>
274fb0a5ebSJens Wiklander #include <linux/types.h>
284fb0a5ebSJens Wiklander #include <linux/uaccess.h>
294fb0a5ebSJens Wiklander #include "optee_private.h"
304fb0a5ebSJens Wiklander #include "optee_smc.h"
31f58e236cSVolodymyr Babchuk #include "shm_pool.h"
324fb0a5ebSJens Wiklander 
334fb0a5ebSJens Wiklander #define DRIVER_NAME "optee"
344fb0a5ebSJens Wiklander 
353249527fSSahil Malhotra #define OPTEE_SHM_NUM_PRIV_PAGES	CONFIG_OPTEE_SHM_NUM_PRIV_PAGES
364fb0a5ebSJens Wiklander 
374fb0a5ebSJens Wiklander /**
384fb0a5ebSJens Wiklander  * optee_from_msg_param() - convert from OPTEE_MSG parameters to
394fb0a5ebSJens Wiklander  *			    struct tee_param
404fb0a5ebSJens Wiklander  * @params:	subsystem internal parameter representation
414fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
424fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
434fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
444fb0a5ebSJens Wiklander  */
454fb0a5ebSJens Wiklander int optee_from_msg_param(struct tee_param *params, size_t num_params,
464fb0a5ebSJens Wiklander 			 const struct optee_msg_param *msg_params)
474fb0a5ebSJens Wiklander {
484fb0a5ebSJens Wiklander 	int rc;
494fb0a5ebSJens Wiklander 	size_t n;
504fb0a5ebSJens Wiklander 	struct tee_shm *shm;
514fb0a5ebSJens Wiklander 	phys_addr_t pa;
524fb0a5ebSJens Wiklander 
534fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
544fb0a5ebSJens Wiklander 		struct tee_param *p = params + n;
554fb0a5ebSJens Wiklander 		const struct optee_msg_param *mp = msg_params + n;
564fb0a5ebSJens Wiklander 		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
574fb0a5ebSJens Wiklander 
584fb0a5ebSJens Wiklander 		switch (attr) {
594fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_NONE:
604fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
614fb0a5ebSJens Wiklander 			memset(&p->u, 0, sizeof(p->u));
624fb0a5ebSJens Wiklander 			break;
634fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
644fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
654fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
664fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
674fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
684fb0a5ebSJens Wiklander 			p->u.value.a = mp->u.value.a;
694fb0a5ebSJens Wiklander 			p->u.value.b = mp->u.value.b;
704fb0a5ebSJens Wiklander 			p->u.value.c = mp->u.value.c;
714fb0a5ebSJens Wiklander 			break;
724fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
734fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
744fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
754fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
764fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
774fb0a5ebSJens Wiklander 			p->u.memref.size = mp->u.tmem.size;
784fb0a5ebSJens Wiklander 			shm = (struct tee_shm *)(unsigned long)
794fb0a5ebSJens Wiklander 				mp->u.tmem.shm_ref;
804fb0a5ebSJens Wiklander 			if (!shm) {
814fb0a5ebSJens Wiklander 				p->u.memref.shm_offs = 0;
824fb0a5ebSJens Wiklander 				p->u.memref.shm = NULL;
834fb0a5ebSJens Wiklander 				break;
844fb0a5ebSJens Wiklander 			}
854fb0a5ebSJens Wiklander 			rc = tee_shm_get_pa(shm, 0, &pa);
864fb0a5ebSJens Wiklander 			if (rc)
874fb0a5ebSJens Wiklander 				return rc;
884fb0a5ebSJens Wiklander 			p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
894fb0a5ebSJens Wiklander 			p->u.memref.shm = shm;
904fb0a5ebSJens Wiklander 
914fb0a5ebSJens Wiklander 			/* Check that the memref is covered by the shm object */
924fb0a5ebSJens Wiklander 			if (p->u.memref.size) {
934fb0a5ebSJens Wiklander 				size_t o = p->u.memref.shm_offs +
944fb0a5ebSJens Wiklander 					   p->u.memref.size - 1;
954fb0a5ebSJens Wiklander 
964fb0a5ebSJens Wiklander 				rc = tee_shm_get_pa(shm, o, NULL);
974fb0a5ebSJens Wiklander 				if (rc)
984fb0a5ebSJens Wiklander 					return rc;
994fb0a5ebSJens Wiklander 			}
1004fb0a5ebSJens Wiklander 			break;
10164cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
10264cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
10364cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
10464cf9d8aSVolodymyr Babchuk 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
10564cf9d8aSVolodymyr Babchuk 				  attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
10664cf9d8aSVolodymyr Babchuk 			p->u.memref.size = mp->u.rmem.size;
10764cf9d8aSVolodymyr Babchuk 			shm = (struct tee_shm *)(unsigned long)
10864cf9d8aSVolodymyr Babchuk 				mp->u.rmem.shm_ref;
10964cf9d8aSVolodymyr Babchuk 
11064cf9d8aSVolodymyr Babchuk 			if (!shm) {
11164cf9d8aSVolodymyr Babchuk 				p->u.memref.shm_offs = 0;
11264cf9d8aSVolodymyr Babchuk 				p->u.memref.shm = NULL;
11364cf9d8aSVolodymyr Babchuk 				break;
11464cf9d8aSVolodymyr Babchuk 			}
11564cf9d8aSVolodymyr Babchuk 			p->u.memref.shm_offs = mp->u.rmem.offs;
11664cf9d8aSVolodymyr Babchuk 			p->u.memref.shm = shm;
11764cf9d8aSVolodymyr Babchuk 
11864cf9d8aSVolodymyr Babchuk 			break;
11964cf9d8aSVolodymyr Babchuk 
1204fb0a5ebSJens Wiklander 		default:
1214fb0a5ebSJens Wiklander 			return -EINVAL;
1224fb0a5ebSJens Wiklander 		}
1234fb0a5ebSJens Wiklander 	}
1244fb0a5ebSJens Wiklander 	return 0;
1254fb0a5ebSJens Wiklander }
1264fb0a5ebSJens Wiklander 
12764cf9d8aSVolodymyr Babchuk static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
12864cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
12964cf9d8aSVolodymyr Babchuk {
13064cf9d8aSVolodymyr Babchuk 	int rc;
13164cf9d8aSVolodymyr Babchuk 	phys_addr_t pa;
13264cf9d8aSVolodymyr Babchuk 
13364cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
13464cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
13564cf9d8aSVolodymyr Babchuk 
13664cf9d8aSVolodymyr Babchuk 	mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
13764cf9d8aSVolodymyr Babchuk 	mp->u.tmem.size = p->u.memref.size;
13864cf9d8aSVolodymyr Babchuk 
13964cf9d8aSVolodymyr Babchuk 	if (!p->u.memref.shm) {
14064cf9d8aSVolodymyr Babchuk 		mp->u.tmem.buf_ptr = 0;
14164cf9d8aSVolodymyr Babchuk 		return 0;
14264cf9d8aSVolodymyr Babchuk 	}
14364cf9d8aSVolodymyr Babchuk 
14464cf9d8aSVolodymyr Babchuk 	rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
14564cf9d8aSVolodymyr Babchuk 	if (rc)
14664cf9d8aSVolodymyr Babchuk 		return rc;
14764cf9d8aSVolodymyr Babchuk 
14864cf9d8aSVolodymyr Babchuk 	mp->u.tmem.buf_ptr = pa;
14964cf9d8aSVolodymyr Babchuk 	mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
15064cf9d8aSVolodymyr Babchuk 		    OPTEE_MSG_ATTR_CACHE_SHIFT;
15164cf9d8aSVolodymyr Babchuk 
15264cf9d8aSVolodymyr Babchuk 	return 0;
15364cf9d8aSVolodymyr Babchuk }
15464cf9d8aSVolodymyr Babchuk 
15564cf9d8aSVolodymyr Babchuk static int to_msg_param_reg_mem(struct optee_msg_param *mp,
15664cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
15764cf9d8aSVolodymyr Babchuk {
15864cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
15964cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
16064cf9d8aSVolodymyr Babchuk 
16164cf9d8aSVolodymyr Babchuk 	mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
16264cf9d8aSVolodymyr Babchuk 	mp->u.rmem.size = p->u.memref.size;
16364cf9d8aSVolodymyr Babchuk 	mp->u.rmem.offs = p->u.memref.shm_offs;
16464cf9d8aSVolodymyr Babchuk 	return 0;
16564cf9d8aSVolodymyr Babchuk }
16664cf9d8aSVolodymyr Babchuk 
1674fb0a5ebSJens Wiklander /**
1684fb0a5ebSJens Wiklander  * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
1694fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
1704fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
1714fb0a5ebSJens Wiklander  * @params:	subsystem itnernal parameter representation
1724fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
1734fb0a5ebSJens Wiklander  */
1744fb0a5ebSJens Wiklander int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
1754fb0a5ebSJens Wiklander 		       const struct tee_param *params)
1764fb0a5ebSJens Wiklander {
1774fb0a5ebSJens Wiklander 	int rc;
1784fb0a5ebSJens Wiklander 	size_t n;
1794fb0a5ebSJens Wiklander 
1804fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
1814fb0a5ebSJens Wiklander 		const struct tee_param *p = params + n;
1824fb0a5ebSJens Wiklander 		struct optee_msg_param *mp = msg_params + n;
1834fb0a5ebSJens Wiklander 
1844fb0a5ebSJens Wiklander 		switch (p->attr) {
1854fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
1864fb0a5ebSJens Wiklander 			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
1874fb0a5ebSJens Wiklander 			memset(&mp->u, 0, sizeof(mp->u));
1884fb0a5ebSJens Wiklander 			break;
1894fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
1904fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
1914fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
1924fb0a5ebSJens Wiklander 			mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
1934fb0a5ebSJens Wiklander 				   TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
1944fb0a5ebSJens Wiklander 			mp->u.value.a = p->u.value.a;
1954fb0a5ebSJens Wiklander 			mp->u.value.b = p->u.value.b;
1964fb0a5ebSJens Wiklander 			mp->u.value.c = p->u.value.c;
1974fb0a5ebSJens Wiklander 			break;
1984fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
1994fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
2004fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
20164cf9d8aSVolodymyr Babchuk 			if (tee_shm_is_registered(p->u.memref.shm))
20264cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_reg_mem(mp, p);
20364cf9d8aSVolodymyr Babchuk 			else
20464cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_tmp_mem(mp, p);
2054fb0a5ebSJens Wiklander 			if (rc)
2064fb0a5ebSJens Wiklander 				return rc;
2074fb0a5ebSJens Wiklander 			break;
2084fb0a5ebSJens Wiklander 		default:
2094fb0a5ebSJens Wiklander 			return -EINVAL;
2104fb0a5ebSJens Wiklander 		}
2114fb0a5ebSJens Wiklander 	}
2124fb0a5ebSJens Wiklander 	return 0;
2134fb0a5ebSJens Wiklander }
2144fb0a5ebSJens Wiklander 
2154fb0a5ebSJens Wiklander static void optee_get_version(struct tee_device *teedev,
2164fb0a5ebSJens Wiklander 			      struct tee_ioctl_version_data *vers)
2174fb0a5ebSJens Wiklander {
2184fb0a5ebSJens Wiklander 	struct tee_ioctl_version_data v = {
2194fb0a5ebSJens Wiklander 		.impl_id = TEE_IMPL_ID_OPTEE,
2204fb0a5ebSJens Wiklander 		.impl_caps = TEE_OPTEE_CAP_TZ,
2214fb0a5ebSJens Wiklander 		.gen_caps = TEE_GEN_CAP_GP,
2224fb0a5ebSJens Wiklander 	};
223f58e236cSVolodymyr Babchuk 	struct optee *optee = tee_get_drvdata(teedev);
224f58e236cSVolodymyr Babchuk 
225f58e236cSVolodymyr Babchuk 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
226f58e236cSVolodymyr Babchuk 		v.gen_caps |= TEE_GEN_CAP_REG_MEM;
2274fb0a5ebSJens Wiklander 	*vers = v;
2284fb0a5ebSJens Wiklander }
2294fb0a5ebSJens Wiklander 
2304fb0a5ebSJens Wiklander static int optee_open(struct tee_context *ctx)
2314fb0a5ebSJens Wiklander {
2324fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata;
2334fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2344fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2354fb0a5ebSJens Wiklander 
2364fb0a5ebSJens Wiklander 	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
2374fb0a5ebSJens Wiklander 	if (!ctxdata)
2384fb0a5ebSJens Wiklander 		return -ENOMEM;
2394fb0a5ebSJens Wiklander 
2404fb0a5ebSJens Wiklander 	if (teedev == optee->supp_teedev) {
2414fb0a5ebSJens Wiklander 		bool busy = true;
2424fb0a5ebSJens Wiklander 
2431647a5acSJens Wiklander 		mutex_lock(&optee->supp.mutex);
2444fb0a5ebSJens Wiklander 		if (!optee->supp.ctx) {
2454fb0a5ebSJens Wiklander 			busy = false;
2464fb0a5ebSJens Wiklander 			optee->supp.ctx = ctx;
2474fb0a5ebSJens Wiklander 		}
2481647a5acSJens Wiklander 		mutex_unlock(&optee->supp.mutex);
2494fb0a5ebSJens Wiklander 		if (busy) {
2504fb0a5ebSJens Wiklander 			kfree(ctxdata);
2514fb0a5ebSJens Wiklander 			return -EBUSY;
2524fb0a5ebSJens Wiklander 		}
2534fb0a5ebSJens Wiklander 	}
2544fb0a5ebSJens Wiklander 
2554fb0a5ebSJens Wiklander 	mutex_init(&ctxdata->mutex);
2564fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&ctxdata->sess_list);
2574fb0a5ebSJens Wiklander 
2584fb0a5ebSJens Wiklander 	ctx->data = ctxdata;
2594fb0a5ebSJens Wiklander 	return 0;
2604fb0a5ebSJens Wiklander }
2614fb0a5ebSJens Wiklander 
2624fb0a5ebSJens Wiklander static void optee_release(struct tee_context *ctx)
2634fb0a5ebSJens Wiklander {
2644fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata = ctx->data;
2654fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2664fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2674fb0a5ebSJens Wiklander 	struct tee_shm *shm;
2684fb0a5ebSJens Wiklander 	struct optee_msg_arg *arg = NULL;
2694fb0a5ebSJens Wiklander 	phys_addr_t parg;
2704fb0a5ebSJens Wiklander 	struct optee_session *sess;
2714fb0a5ebSJens Wiklander 	struct optee_session *sess_tmp;
2724fb0a5ebSJens Wiklander 
2734fb0a5ebSJens Wiklander 	if (!ctxdata)
2744fb0a5ebSJens Wiklander 		return;
2754fb0a5ebSJens Wiklander 
2764fb0a5ebSJens Wiklander 	shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED);
2774fb0a5ebSJens Wiklander 	if (!IS_ERR(shm)) {
2784fb0a5ebSJens Wiklander 		arg = tee_shm_get_va(shm, 0);
2794fb0a5ebSJens Wiklander 		/*
280efb14036SJens Wiklander 		 * If va2pa fails for some reason, we can't call into
281efb14036SJens Wiklander 		 * secure world, only free the memory. Secure OS will leak
282efb14036SJens Wiklander 		 * sessions and finally refuse more sessions, but we will
283efb14036SJens Wiklander 		 * at least let normal world reclaim its memory.
2844fb0a5ebSJens Wiklander 		 */
2854fb0a5ebSJens Wiklander 		if (!IS_ERR(arg))
286efb14036SJens Wiklander 			if (tee_shm_va2pa(shm, arg, &parg))
287efb14036SJens Wiklander 				arg = NULL; /* prevent usage of parg below */
2884fb0a5ebSJens Wiklander 	}
2894fb0a5ebSJens Wiklander 
2904fb0a5ebSJens Wiklander 	list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
2914fb0a5ebSJens Wiklander 				 list_node) {
2924fb0a5ebSJens Wiklander 		list_del(&sess->list_node);
2934fb0a5ebSJens Wiklander 		if (!IS_ERR_OR_NULL(arg)) {
2944fb0a5ebSJens Wiklander 			memset(arg, 0, sizeof(*arg));
2954fb0a5ebSJens Wiklander 			arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
2964fb0a5ebSJens Wiklander 			arg->session = sess->session_id;
2974fb0a5ebSJens Wiklander 			optee_do_call_with_arg(ctx, parg);
2984fb0a5ebSJens Wiklander 		}
2994fb0a5ebSJens Wiklander 		kfree(sess);
3004fb0a5ebSJens Wiklander 	}
3014fb0a5ebSJens Wiklander 	kfree(ctxdata);
3024fb0a5ebSJens Wiklander 
3034fb0a5ebSJens Wiklander 	if (!IS_ERR(shm))
3044fb0a5ebSJens Wiklander 		tee_shm_free(shm);
3054fb0a5ebSJens Wiklander 
3064fb0a5ebSJens Wiklander 	ctx->data = NULL;
3074fb0a5ebSJens Wiklander 
3081647a5acSJens Wiklander 	if (teedev == optee->supp_teedev)
3091647a5acSJens Wiklander 		optee_supp_release(&optee->supp);
3104fb0a5ebSJens Wiklander }
3114fb0a5ebSJens Wiklander 
31296e72ddeSBhumika Goyal static const struct tee_driver_ops optee_ops = {
3134fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3144fb0a5ebSJens Wiklander 	.open = optee_open,
3154fb0a5ebSJens Wiklander 	.release = optee_release,
3164fb0a5ebSJens Wiklander 	.open_session = optee_open_session,
3174fb0a5ebSJens Wiklander 	.close_session = optee_close_session,
3184fb0a5ebSJens Wiklander 	.invoke_func = optee_invoke_func,
3194fb0a5ebSJens Wiklander 	.cancel_req = optee_cancel_req,
32006ca7917SVolodymyr Babchuk 	.shm_register = optee_shm_register,
32106ca7917SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister,
3224fb0a5ebSJens Wiklander };
3234fb0a5ebSJens Wiklander 
32496e72ddeSBhumika Goyal static const struct tee_desc optee_desc = {
3254fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-clnt",
3264fb0a5ebSJens Wiklander 	.ops = &optee_ops,
3274fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3284fb0a5ebSJens Wiklander };
3294fb0a5ebSJens Wiklander 
33096e72ddeSBhumika Goyal static const struct tee_driver_ops optee_supp_ops = {
3314fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3324fb0a5ebSJens Wiklander 	.open = optee_open,
3334fb0a5ebSJens Wiklander 	.release = optee_release,
3344fb0a5ebSJens Wiklander 	.supp_recv = optee_supp_recv,
3354fb0a5ebSJens Wiklander 	.supp_send = optee_supp_send,
33653a107c8SVolodymyr Babchuk 	.shm_register = optee_shm_register_supp,
33753a107c8SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister_supp,
3384fb0a5ebSJens Wiklander };
3394fb0a5ebSJens Wiklander 
34096e72ddeSBhumika Goyal static const struct tee_desc optee_supp_desc = {
3414fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-supp",
3424fb0a5ebSJens Wiklander 	.ops = &optee_supp_ops,
3434fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3444fb0a5ebSJens Wiklander 	.flags = TEE_DESC_PRIVILEGED,
3454fb0a5ebSJens Wiklander };
3464fb0a5ebSJens Wiklander 
3474fb0a5ebSJens Wiklander static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
3484fb0a5ebSJens Wiklander {
3494fb0a5ebSJens Wiklander 	struct arm_smccc_res res;
3504fb0a5ebSJens Wiklander 
3514fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
3524fb0a5ebSJens Wiklander 
3534fb0a5ebSJens Wiklander 	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
3544fb0a5ebSJens Wiklander 	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
3554fb0a5ebSJens Wiklander 		return true;
3564fb0a5ebSJens Wiklander 	return false;
3574fb0a5ebSJens Wiklander }
3584fb0a5ebSJens Wiklander 
3595c5f8030SJérôme Forissier static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
3605c5f8030SJérôme Forissier {
3615c5f8030SJérôme Forissier 	union {
3625c5f8030SJérôme Forissier 		struct arm_smccc_res smccc;
3635c5f8030SJérôme Forissier 		struct optee_smc_call_get_os_revision_result result;
3645c5f8030SJérôme Forissier 	} res = {
3655c5f8030SJérôme Forissier 		.result = {
3665c5f8030SJérôme Forissier 			.build_id = 0
3675c5f8030SJérôme Forissier 		}
3685c5f8030SJérôme Forissier 	};
3695c5f8030SJérôme Forissier 
3705c5f8030SJérôme Forissier 	invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
3715c5f8030SJérôme Forissier 		  &res.smccc);
3725c5f8030SJérôme Forissier 
3735c5f8030SJérôme Forissier 	if (res.result.build_id)
3745c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu (%08lx)", res.result.major,
3755c5f8030SJérôme Forissier 			res.result.minor, res.result.build_id);
3765c5f8030SJérôme Forissier 	else
3775c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu", res.result.major, res.result.minor);
3785c5f8030SJérôme Forissier }
3795c5f8030SJérôme Forissier 
3804fb0a5ebSJens Wiklander static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
3814fb0a5ebSJens Wiklander {
3824fb0a5ebSJens Wiklander 	union {
3834fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
3844fb0a5ebSJens Wiklander 		struct optee_smc_calls_revision_result result;
3854fb0a5ebSJens Wiklander 	} res;
3864fb0a5ebSJens Wiklander 
3874fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
3884fb0a5ebSJens Wiklander 
3894fb0a5ebSJens Wiklander 	if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
3904fb0a5ebSJens Wiklander 	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
3914fb0a5ebSJens Wiklander 		return true;
3924fb0a5ebSJens Wiklander 	return false;
3934fb0a5ebSJens Wiklander }
3944fb0a5ebSJens Wiklander 
3954fb0a5ebSJens Wiklander static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
3964fb0a5ebSJens Wiklander 					    u32 *sec_caps)
3974fb0a5ebSJens Wiklander {
3984fb0a5ebSJens Wiklander 	union {
3994fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4004fb0a5ebSJens Wiklander 		struct optee_smc_exchange_capabilities_result result;
4014fb0a5ebSJens Wiklander 	} res;
4024fb0a5ebSJens Wiklander 	u32 a1 = 0;
4034fb0a5ebSJens Wiklander 
4044fb0a5ebSJens Wiklander 	/*
4054fb0a5ebSJens Wiklander 	 * TODO This isn't enough to tell if it's UP system (from kernel
4064fb0a5ebSJens Wiklander 	 * point of view) or not, is_smp() returns the the information
4074fb0a5ebSJens Wiklander 	 * needed, but can't be called directly from here.
4084fb0a5ebSJens Wiklander 	 */
4094fb0a5ebSJens Wiklander 	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1)
4104fb0a5ebSJens Wiklander 		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
4114fb0a5ebSJens Wiklander 
4124fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0,
4134fb0a5ebSJens Wiklander 		  &res.smccc);
4144fb0a5ebSJens Wiklander 
4154fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK)
4164fb0a5ebSJens Wiklander 		return false;
4174fb0a5ebSJens Wiklander 
4184fb0a5ebSJens Wiklander 	*sec_caps = res.result.capabilities;
4194fb0a5ebSJens Wiklander 	return true;
4204fb0a5ebSJens Wiklander }
4214fb0a5ebSJens Wiklander 
4224fb0a5ebSJens Wiklander static struct tee_shm_pool *
423f58e236cSVolodymyr Babchuk optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm,
424f58e236cSVolodymyr Babchuk 			  u32 sec_caps)
4254fb0a5ebSJens Wiklander {
4264fb0a5ebSJens Wiklander 	union {
4274fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4284fb0a5ebSJens Wiklander 		struct optee_smc_get_shm_config_result result;
4294fb0a5ebSJens Wiklander 	} res;
4304fb0a5ebSJens Wiklander 	unsigned long vaddr;
4314fb0a5ebSJens Wiklander 	phys_addr_t paddr;
4324fb0a5ebSJens Wiklander 	size_t size;
4334fb0a5ebSJens Wiklander 	phys_addr_t begin;
4344fb0a5ebSJens Wiklander 	phys_addr_t end;
4354fb0a5ebSJens Wiklander 	void *va;
436f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *priv_mgr;
437f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *dmabuf_mgr;
438f58e236cSVolodymyr Babchuk 	void *rc;
4394fb0a5ebSJens Wiklander 
4404fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
4414fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK) {
4424fb0a5ebSJens Wiklander 		pr_info("shm service not available\n");
4434fb0a5ebSJens Wiklander 		return ERR_PTR(-ENOENT);
4444fb0a5ebSJens Wiklander 	}
4454fb0a5ebSJens Wiklander 
4464fb0a5ebSJens Wiklander 	if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
4474fb0a5ebSJens Wiklander 		pr_err("only normal cached shared memory supported\n");
4484fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4494fb0a5ebSJens Wiklander 	}
4504fb0a5ebSJens Wiklander 
4514fb0a5ebSJens Wiklander 	begin = roundup(res.result.start, PAGE_SIZE);
4524fb0a5ebSJens Wiklander 	end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
4534fb0a5ebSJens Wiklander 	paddr = begin;
4544fb0a5ebSJens Wiklander 	size = end - begin;
4554fb0a5ebSJens Wiklander 
4564fb0a5ebSJens Wiklander 	if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) {
4574fb0a5ebSJens Wiklander 		pr_err("too small shared memory area\n");
4584fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4594fb0a5ebSJens Wiklander 	}
4604fb0a5ebSJens Wiklander 
4614fb0a5ebSJens Wiklander 	va = memremap(paddr, size, MEMREMAP_WB);
4624fb0a5ebSJens Wiklander 	if (!va) {
4634fb0a5ebSJens Wiklander 		pr_err("shared memory ioremap failed\n");
4644fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4654fb0a5ebSJens Wiklander 	}
4664fb0a5ebSJens Wiklander 	vaddr = (unsigned long)va;
4674fb0a5ebSJens Wiklander 
468f58e236cSVolodymyr Babchuk 	/*
469f58e236cSVolodymyr Babchuk 	 * If OP-TEE can work with unregistered SHM, we will use own pool
470f58e236cSVolodymyr Babchuk 	 * for private shm
471f58e236cSVolodymyr Babchuk 	 */
472f58e236cSVolodymyr Babchuk 	if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) {
473f58e236cSVolodymyr Babchuk 		rc = optee_shm_pool_alloc_pages();
474f58e236cSVolodymyr Babchuk 		if (IS_ERR(rc))
475f58e236cSVolodymyr Babchuk 			goto err_memunmap;
476f58e236cSVolodymyr Babchuk 		priv_mgr = rc;
477f58e236cSVolodymyr Babchuk 	} else {
478f58e236cSVolodymyr Babchuk 		const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4794fb0a5ebSJens Wiklander 
480f58e236cSVolodymyr Babchuk 		rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
481f58e236cSVolodymyr Babchuk 						    3 /* 8 bytes aligned */);
482f58e236cSVolodymyr Babchuk 		if (IS_ERR(rc))
483f58e236cSVolodymyr Babchuk 			goto err_memunmap;
484f58e236cSVolodymyr Babchuk 		priv_mgr = rc;
485f58e236cSVolodymyr Babchuk 
486f58e236cSVolodymyr Babchuk 		vaddr += sz;
487f58e236cSVolodymyr Babchuk 		paddr += sz;
488f58e236cSVolodymyr Babchuk 		size -= sz;
4894fb0a5ebSJens Wiklander 	}
4904fb0a5ebSJens Wiklander 
491f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
492f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
493f58e236cSVolodymyr Babchuk 		goto err_free_priv_mgr;
494f58e236cSVolodymyr Babchuk 	dmabuf_mgr = rc;
495f58e236cSVolodymyr Babchuk 
496f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
497f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
498f58e236cSVolodymyr Babchuk 		goto err_free_dmabuf_mgr;
499f58e236cSVolodymyr Babchuk 
5004fb0a5ebSJens Wiklander 	*memremaped_shm = va;
501f58e236cSVolodymyr Babchuk 
502f58e236cSVolodymyr Babchuk 	return rc;
503f58e236cSVolodymyr Babchuk 
504f58e236cSVolodymyr Babchuk err_free_dmabuf_mgr:
505f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(dmabuf_mgr);
506f58e236cSVolodymyr Babchuk err_free_priv_mgr:
507f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(priv_mgr);
508f58e236cSVolodymyr Babchuk err_memunmap:
509f58e236cSVolodymyr Babchuk 	memunmap(va);
510f58e236cSVolodymyr Babchuk 	return rc;
5114fb0a5ebSJens Wiklander }
5124fb0a5ebSJens Wiklander 
5134fb0a5ebSJens Wiklander /* Simple wrapper functions to be able to use a function pointer */
5144fb0a5ebSJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1,
5154fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5164fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5174fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5184fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5194fb0a5ebSJens Wiklander {
5204fb0a5ebSJens Wiklander 	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5214fb0a5ebSJens Wiklander }
5224fb0a5ebSJens Wiklander 
5234fb0a5ebSJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
5244fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5254fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5264fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5274fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5284fb0a5ebSJens Wiklander {
5294fb0a5ebSJens Wiklander 	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5304fb0a5ebSJens Wiklander }
5314fb0a5ebSJens Wiklander 
5324fb0a5ebSJens Wiklander static optee_invoke_fn *get_invoke_func(struct device_node *np)
5334fb0a5ebSJens Wiklander {
5344fb0a5ebSJens Wiklander 	const char *method;
5354fb0a5ebSJens Wiklander 
5364fb0a5ebSJens Wiklander 	pr_info("probing for conduit method from DT.\n");
5374fb0a5ebSJens Wiklander 
5384fb0a5ebSJens Wiklander 	if (of_property_read_string(np, "method", &method)) {
5394fb0a5ebSJens Wiklander 		pr_warn("missing \"method\" property\n");
5404fb0a5ebSJens Wiklander 		return ERR_PTR(-ENXIO);
5414fb0a5ebSJens Wiklander 	}
5424fb0a5ebSJens Wiklander 
5434fb0a5ebSJens Wiklander 	if (!strcmp("hvc", method))
5444fb0a5ebSJens Wiklander 		return optee_smccc_hvc;
5454fb0a5ebSJens Wiklander 	else if (!strcmp("smc", method))
5464fb0a5ebSJens Wiklander 		return optee_smccc_smc;
5474fb0a5ebSJens Wiklander 
5484fb0a5ebSJens Wiklander 	pr_warn("invalid \"method\" property: %s\n", method);
5494fb0a5ebSJens Wiklander 	return ERR_PTR(-EINVAL);
5504fb0a5ebSJens Wiklander }
5514fb0a5ebSJens Wiklander 
5524fb0a5ebSJens Wiklander static struct optee *optee_probe(struct device_node *np)
5534fb0a5ebSJens Wiklander {
5544fb0a5ebSJens Wiklander 	optee_invoke_fn *invoke_fn;
5554fb0a5ebSJens Wiklander 	struct tee_shm_pool *pool;
5564fb0a5ebSJens Wiklander 	struct optee *optee = NULL;
5574fb0a5ebSJens Wiklander 	void *memremaped_shm = NULL;
5584fb0a5ebSJens Wiklander 	struct tee_device *teedev;
5594fb0a5ebSJens Wiklander 	u32 sec_caps;
5604fb0a5ebSJens Wiklander 	int rc;
5614fb0a5ebSJens Wiklander 
5624fb0a5ebSJens Wiklander 	invoke_fn = get_invoke_func(np);
5634fb0a5ebSJens Wiklander 	if (IS_ERR(invoke_fn))
5644fb0a5ebSJens Wiklander 		return (void *)invoke_fn;
5654fb0a5ebSJens Wiklander 
5664fb0a5ebSJens Wiklander 	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
5674fb0a5ebSJens Wiklander 		pr_warn("api uid mismatch\n");
5684fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5694fb0a5ebSJens Wiklander 	}
5704fb0a5ebSJens Wiklander 
5715c5f8030SJérôme Forissier 	optee_msg_get_os_revision(invoke_fn);
5725c5f8030SJérôme Forissier 
5734fb0a5ebSJens Wiklander 	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
5744fb0a5ebSJens Wiklander 		pr_warn("api revision mismatch\n");
5754fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5764fb0a5ebSJens Wiklander 	}
5774fb0a5ebSJens Wiklander 
5784fb0a5ebSJens Wiklander 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
5794fb0a5ebSJens Wiklander 		pr_warn("capabilities mismatch\n");
5804fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5814fb0a5ebSJens Wiklander 	}
5824fb0a5ebSJens Wiklander 
5834fb0a5ebSJens Wiklander 	/*
5844fb0a5ebSJens Wiklander 	 * We have no other option for shared memory, if secure world
5854fb0a5ebSJens Wiklander 	 * doesn't have any reserved memory we can use we can't continue.
5864fb0a5ebSJens Wiklander 	 */
5874fb0a5ebSJens Wiklander 	if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
5884fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5894fb0a5ebSJens Wiklander 
590f58e236cSVolodymyr Babchuk 	pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm, sec_caps);
5914fb0a5ebSJens Wiklander 	if (IS_ERR(pool))
5924fb0a5ebSJens Wiklander 		return (void *)pool;
5934fb0a5ebSJens Wiklander 
5944fb0a5ebSJens Wiklander 	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
5954fb0a5ebSJens Wiklander 	if (!optee) {
5964fb0a5ebSJens Wiklander 		rc = -ENOMEM;
5974fb0a5ebSJens Wiklander 		goto err;
5984fb0a5ebSJens Wiklander 	}
5994fb0a5ebSJens Wiklander 
6004fb0a5ebSJens Wiklander 	optee->invoke_fn = invoke_fn;
601d885cc5eSVolodymyr Babchuk 	optee->sec_caps = sec_caps;
6024fb0a5ebSJens Wiklander 
6034fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
6044fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
6054fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
6064fb0a5ebSJens Wiklander 		goto err;
6074fb0a5ebSJens Wiklander 	}
6084fb0a5ebSJens Wiklander 	optee->teedev = teedev;
6094fb0a5ebSJens Wiklander 
6104fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
6114fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
6124fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
6134fb0a5ebSJens Wiklander 		goto err;
6144fb0a5ebSJens Wiklander 	}
6154fb0a5ebSJens Wiklander 	optee->supp_teedev = teedev;
6164fb0a5ebSJens Wiklander 
6174fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->teedev);
6184fb0a5ebSJens Wiklander 	if (rc)
6194fb0a5ebSJens Wiklander 		goto err;
6204fb0a5ebSJens Wiklander 
6214fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->supp_teedev);
6224fb0a5ebSJens Wiklander 	if (rc)
6234fb0a5ebSJens Wiklander 		goto err;
6244fb0a5ebSJens Wiklander 
6254fb0a5ebSJens Wiklander 	mutex_init(&optee->call_queue.mutex);
6264fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&optee->call_queue.waiters);
6274fb0a5ebSJens Wiklander 	optee_wait_queue_init(&optee->wait_queue);
6284fb0a5ebSJens Wiklander 	optee_supp_init(&optee->supp);
6294fb0a5ebSJens Wiklander 	optee->memremaped_shm = memremaped_shm;
6304fb0a5ebSJens Wiklander 	optee->pool = pool;
6314fb0a5ebSJens Wiklander 
6324fb0a5ebSJens Wiklander 	optee_enable_shm_cache(optee);
6334fb0a5ebSJens Wiklander 
6343c15ddb9SVictor Chong 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
6353c15ddb9SVictor Chong 		pr_info("dynamic shared memory is enabled\n");
6363c15ddb9SVictor Chong 
6374fb0a5ebSJens Wiklander 	pr_info("initialized driver\n");
6384fb0a5ebSJens Wiklander 	return optee;
6394fb0a5ebSJens Wiklander err:
6404fb0a5ebSJens Wiklander 	if (optee) {
6414fb0a5ebSJens Wiklander 		/*
6424fb0a5ebSJens Wiklander 		 * tee_device_unregister() is safe to call even if the
6434fb0a5ebSJens Wiklander 		 * devices hasn't been registered with
6444fb0a5ebSJens Wiklander 		 * tee_device_register() yet.
6454fb0a5ebSJens Wiklander 		 */
6464fb0a5ebSJens Wiklander 		tee_device_unregister(optee->supp_teedev);
6474fb0a5ebSJens Wiklander 		tee_device_unregister(optee->teedev);
6484fb0a5ebSJens Wiklander 		kfree(optee);
6494fb0a5ebSJens Wiklander 	}
6504fb0a5ebSJens Wiklander 	if (pool)
6514fb0a5ebSJens Wiklander 		tee_shm_pool_free(pool);
6524fb0a5ebSJens Wiklander 	if (memremaped_shm)
6534fb0a5ebSJens Wiklander 		memunmap(memremaped_shm);
6544fb0a5ebSJens Wiklander 	return ERR_PTR(rc);
6554fb0a5ebSJens Wiklander }
6564fb0a5ebSJens Wiklander 
6574fb0a5ebSJens Wiklander static void optee_remove(struct optee *optee)
6584fb0a5ebSJens Wiklander {
6594fb0a5ebSJens Wiklander 	/*
6604fb0a5ebSJens Wiklander 	 * Ask OP-TEE to free all cached shared memory objects to decrease
6614fb0a5ebSJens Wiklander 	 * reference counters and also avoid wild pointers in secure world
6624fb0a5ebSJens Wiklander 	 * into the old shared memory range.
6634fb0a5ebSJens Wiklander 	 */
6644fb0a5ebSJens Wiklander 	optee_disable_shm_cache(optee);
6654fb0a5ebSJens Wiklander 
6664fb0a5ebSJens Wiklander 	/*
6674fb0a5ebSJens Wiklander 	 * The two devices has to be unregistered before we can free the
6684fb0a5ebSJens Wiklander 	 * other resources.
6694fb0a5ebSJens Wiklander 	 */
6704fb0a5ebSJens Wiklander 	tee_device_unregister(optee->supp_teedev);
6714fb0a5ebSJens Wiklander 	tee_device_unregister(optee->teedev);
6724fb0a5ebSJens Wiklander 
6734fb0a5ebSJens Wiklander 	tee_shm_pool_free(optee->pool);
6744fb0a5ebSJens Wiklander 	if (optee->memremaped_shm)
6754fb0a5ebSJens Wiklander 		memunmap(optee->memremaped_shm);
6764fb0a5ebSJens Wiklander 	optee_wait_queue_exit(&optee->wait_queue);
6774fb0a5ebSJens Wiklander 	optee_supp_uninit(&optee->supp);
6784fb0a5ebSJens Wiklander 	mutex_destroy(&optee->call_queue.mutex);
6794fb0a5ebSJens Wiklander 
6804fb0a5ebSJens Wiklander 	kfree(optee);
6814fb0a5ebSJens Wiklander }
6824fb0a5ebSJens Wiklander 
6834fb0a5ebSJens Wiklander static const struct of_device_id optee_match[] = {
6844fb0a5ebSJens Wiklander 	{ .compatible = "linaro,optee-tz" },
6854fb0a5ebSJens Wiklander 	{},
6864fb0a5ebSJens Wiklander };
6874fb0a5ebSJens Wiklander 
6884fb0a5ebSJens Wiklander static struct optee *optee_svc;
6894fb0a5ebSJens Wiklander 
6904fb0a5ebSJens Wiklander static int __init optee_driver_init(void)
6914fb0a5ebSJens Wiklander {
6924fb0a5ebSJens Wiklander 	struct device_node *fw_np;
6934fb0a5ebSJens Wiklander 	struct device_node *np;
6944fb0a5ebSJens Wiklander 	struct optee *optee;
6954fb0a5ebSJens Wiklander 
6964fb0a5ebSJens Wiklander 	/* Node is supposed to be below /firmware */
6974fb0a5ebSJens Wiklander 	fw_np = of_find_node_by_name(NULL, "firmware");
6984fb0a5ebSJens Wiklander 	if (!fw_np)
6994fb0a5ebSJens Wiklander 		return -ENODEV;
7004fb0a5ebSJens Wiklander 
7014fb0a5ebSJens Wiklander 	np = of_find_matching_node(fw_np, optee_match);
702c7c0d8dfSJulia Lawall 	if (!np || !of_device_is_available(np)) {
703c7c0d8dfSJulia Lawall 		of_node_put(np);
7044fb0a5ebSJens Wiklander 		return -ENODEV;
705c7c0d8dfSJulia Lawall 	}
7064fb0a5ebSJens Wiklander 
7074fb0a5ebSJens Wiklander 	optee = optee_probe(np);
7084fb0a5ebSJens Wiklander 	of_node_put(np);
7094fb0a5ebSJens Wiklander 
7104fb0a5ebSJens Wiklander 	if (IS_ERR(optee))
7114fb0a5ebSJens Wiklander 		return PTR_ERR(optee);
7124fb0a5ebSJens Wiklander 
7134fb0a5ebSJens Wiklander 	optee_svc = optee;
7144fb0a5ebSJens Wiklander 
7154fb0a5ebSJens Wiklander 	return 0;
7164fb0a5ebSJens Wiklander }
7174fb0a5ebSJens Wiklander module_init(optee_driver_init);
7184fb0a5ebSJens Wiklander 
7194fb0a5ebSJens Wiklander static void __exit optee_driver_exit(void)
7204fb0a5ebSJens Wiklander {
7214fb0a5ebSJens Wiklander 	struct optee *optee = optee_svc;
7224fb0a5ebSJens Wiklander 
7234fb0a5ebSJens Wiklander 	optee_svc = NULL;
7244fb0a5ebSJens Wiklander 	if (optee)
7254fb0a5ebSJens Wiklander 		optee_remove(optee);
7264fb0a5ebSJens Wiklander }
7274fb0a5ebSJens Wiklander module_exit(optee_driver_exit);
7284fb0a5ebSJens Wiklander 
7294fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro");
7304fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver");
7314fb0a5ebSJens Wiklander MODULE_SUPPORTED_DEVICE("");
7324fb0a5ebSJens Wiklander MODULE_VERSION("1.0");
7334fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2");
734