xref: /openbmc/linux/drivers/tee/optee/core.c (revision 64cf9d8a)
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"
314fb0a5ebSJens Wiklander 
324fb0a5ebSJens Wiklander #define DRIVER_NAME "optee"
334fb0a5ebSJens Wiklander 
344fb0a5ebSJens Wiklander #define OPTEE_SHM_NUM_PRIV_PAGES	1
354fb0a5ebSJens Wiklander 
364fb0a5ebSJens Wiklander /**
374fb0a5ebSJens Wiklander  * optee_from_msg_param() - convert from OPTEE_MSG parameters to
384fb0a5ebSJens Wiklander  *			    struct tee_param
394fb0a5ebSJens Wiklander  * @params:	subsystem internal parameter representation
404fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
414fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
424fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
434fb0a5ebSJens Wiklander  */
444fb0a5ebSJens Wiklander int optee_from_msg_param(struct tee_param *params, size_t num_params,
454fb0a5ebSJens Wiklander 			 const struct optee_msg_param *msg_params)
464fb0a5ebSJens Wiklander {
474fb0a5ebSJens Wiklander 	int rc;
484fb0a5ebSJens Wiklander 	size_t n;
494fb0a5ebSJens Wiklander 	struct tee_shm *shm;
504fb0a5ebSJens Wiklander 	phys_addr_t pa;
514fb0a5ebSJens Wiklander 
524fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
534fb0a5ebSJens Wiklander 		struct tee_param *p = params + n;
544fb0a5ebSJens Wiklander 		const struct optee_msg_param *mp = msg_params + n;
554fb0a5ebSJens Wiklander 		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
564fb0a5ebSJens Wiklander 
574fb0a5ebSJens Wiklander 		switch (attr) {
584fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_NONE:
594fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
604fb0a5ebSJens Wiklander 			memset(&p->u, 0, sizeof(p->u));
614fb0a5ebSJens Wiklander 			break;
624fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
634fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
644fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
654fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
664fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
674fb0a5ebSJens Wiklander 			p->u.value.a = mp->u.value.a;
684fb0a5ebSJens Wiklander 			p->u.value.b = mp->u.value.b;
694fb0a5ebSJens Wiklander 			p->u.value.c = mp->u.value.c;
704fb0a5ebSJens Wiklander 			break;
714fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
724fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
734fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
744fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
754fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
764fb0a5ebSJens Wiklander 			p->u.memref.size = mp->u.tmem.size;
774fb0a5ebSJens Wiklander 			shm = (struct tee_shm *)(unsigned long)
784fb0a5ebSJens Wiklander 				mp->u.tmem.shm_ref;
794fb0a5ebSJens Wiklander 			if (!shm) {
804fb0a5ebSJens Wiklander 				p->u.memref.shm_offs = 0;
814fb0a5ebSJens Wiklander 				p->u.memref.shm = NULL;
824fb0a5ebSJens Wiklander 				break;
834fb0a5ebSJens Wiklander 			}
844fb0a5ebSJens Wiklander 			rc = tee_shm_get_pa(shm, 0, &pa);
854fb0a5ebSJens Wiklander 			if (rc)
864fb0a5ebSJens Wiklander 				return rc;
874fb0a5ebSJens Wiklander 			p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
884fb0a5ebSJens Wiklander 			p->u.memref.shm = shm;
894fb0a5ebSJens Wiklander 
904fb0a5ebSJens Wiklander 			/* Check that the memref is covered by the shm object */
914fb0a5ebSJens Wiklander 			if (p->u.memref.size) {
924fb0a5ebSJens Wiklander 				size_t o = p->u.memref.shm_offs +
934fb0a5ebSJens Wiklander 					   p->u.memref.size - 1;
944fb0a5ebSJens Wiklander 
954fb0a5ebSJens Wiklander 				rc = tee_shm_get_pa(shm, o, NULL);
964fb0a5ebSJens Wiklander 				if (rc)
974fb0a5ebSJens Wiklander 					return rc;
984fb0a5ebSJens Wiklander 			}
994fb0a5ebSJens Wiklander 			break;
10064cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
10164cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
10264cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
10364cf9d8aSVolodymyr Babchuk 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
10464cf9d8aSVolodymyr Babchuk 				  attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
10564cf9d8aSVolodymyr Babchuk 			p->u.memref.size = mp->u.rmem.size;
10664cf9d8aSVolodymyr Babchuk 			shm = (struct tee_shm *)(unsigned long)
10764cf9d8aSVolodymyr Babchuk 				mp->u.rmem.shm_ref;
10864cf9d8aSVolodymyr Babchuk 
10964cf9d8aSVolodymyr Babchuk 			if (!shm) {
11064cf9d8aSVolodymyr Babchuk 				p->u.memref.shm_offs = 0;
11164cf9d8aSVolodymyr Babchuk 				p->u.memref.shm = NULL;
11264cf9d8aSVolodymyr Babchuk 				break;
11364cf9d8aSVolodymyr Babchuk 			}
11464cf9d8aSVolodymyr Babchuk 			p->u.memref.shm_offs = mp->u.rmem.offs;
11564cf9d8aSVolodymyr Babchuk 			p->u.memref.shm = shm;
11664cf9d8aSVolodymyr Babchuk 
11764cf9d8aSVolodymyr Babchuk 			break;
11864cf9d8aSVolodymyr Babchuk 
1194fb0a5ebSJens Wiklander 		default:
1204fb0a5ebSJens Wiklander 			return -EINVAL;
1214fb0a5ebSJens Wiklander 		}
1224fb0a5ebSJens Wiklander 	}
1234fb0a5ebSJens Wiklander 	return 0;
1244fb0a5ebSJens Wiklander }
1254fb0a5ebSJens Wiklander 
12664cf9d8aSVolodymyr Babchuk static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
12764cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
12864cf9d8aSVolodymyr Babchuk {
12964cf9d8aSVolodymyr Babchuk 	int rc;
13064cf9d8aSVolodymyr Babchuk 	phys_addr_t pa;
13164cf9d8aSVolodymyr Babchuk 
13264cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
13364cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
13464cf9d8aSVolodymyr Babchuk 
13564cf9d8aSVolodymyr Babchuk 	mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
13664cf9d8aSVolodymyr Babchuk 	mp->u.tmem.size = p->u.memref.size;
13764cf9d8aSVolodymyr Babchuk 
13864cf9d8aSVolodymyr Babchuk 	if (!p->u.memref.shm) {
13964cf9d8aSVolodymyr Babchuk 		mp->u.tmem.buf_ptr = 0;
14064cf9d8aSVolodymyr Babchuk 		return 0;
14164cf9d8aSVolodymyr Babchuk 	}
14264cf9d8aSVolodymyr Babchuk 
14364cf9d8aSVolodymyr Babchuk 	rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
14464cf9d8aSVolodymyr Babchuk 	if (rc)
14564cf9d8aSVolodymyr Babchuk 		return rc;
14664cf9d8aSVolodymyr Babchuk 
14764cf9d8aSVolodymyr Babchuk 	mp->u.tmem.buf_ptr = pa;
14864cf9d8aSVolodymyr Babchuk 	mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
14964cf9d8aSVolodymyr Babchuk 		    OPTEE_MSG_ATTR_CACHE_SHIFT;
15064cf9d8aSVolodymyr Babchuk 
15164cf9d8aSVolodymyr Babchuk 	return 0;
15264cf9d8aSVolodymyr Babchuk }
15364cf9d8aSVolodymyr Babchuk 
15464cf9d8aSVolodymyr Babchuk static int to_msg_param_reg_mem(struct optee_msg_param *mp,
15564cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
15664cf9d8aSVolodymyr Babchuk {
15764cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
15864cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
15964cf9d8aSVolodymyr Babchuk 
16064cf9d8aSVolodymyr Babchuk 	mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
16164cf9d8aSVolodymyr Babchuk 	mp->u.rmem.size = p->u.memref.size;
16264cf9d8aSVolodymyr Babchuk 	mp->u.rmem.offs = p->u.memref.shm_offs;
16364cf9d8aSVolodymyr Babchuk 	return 0;
16464cf9d8aSVolodymyr Babchuk }
16564cf9d8aSVolodymyr Babchuk 
1664fb0a5ebSJens Wiklander /**
1674fb0a5ebSJens Wiklander  * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
1684fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
1694fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
1704fb0a5ebSJens Wiklander  * @params:	subsystem itnernal parameter representation
1714fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
1724fb0a5ebSJens Wiklander  */
1734fb0a5ebSJens Wiklander int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
1744fb0a5ebSJens Wiklander 		       const struct tee_param *params)
1754fb0a5ebSJens Wiklander {
1764fb0a5ebSJens Wiklander 	int rc;
1774fb0a5ebSJens Wiklander 	size_t n;
1784fb0a5ebSJens Wiklander 
1794fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
1804fb0a5ebSJens Wiklander 		const struct tee_param *p = params + n;
1814fb0a5ebSJens Wiklander 		struct optee_msg_param *mp = msg_params + n;
1824fb0a5ebSJens Wiklander 
1834fb0a5ebSJens Wiklander 		switch (p->attr) {
1844fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
1854fb0a5ebSJens Wiklander 			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
1864fb0a5ebSJens Wiklander 			memset(&mp->u, 0, sizeof(mp->u));
1874fb0a5ebSJens Wiklander 			break;
1884fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
1894fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
1904fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
1914fb0a5ebSJens Wiklander 			mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
1924fb0a5ebSJens Wiklander 				   TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
1934fb0a5ebSJens Wiklander 			mp->u.value.a = p->u.value.a;
1944fb0a5ebSJens Wiklander 			mp->u.value.b = p->u.value.b;
1954fb0a5ebSJens Wiklander 			mp->u.value.c = p->u.value.c;
1964fb0a5ebSJens Wiklander 			break;
1974fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
1984fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
1994fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
20064cf9d8aSVolodymyr Babchuk 			if (tee_shm_is_registered(p->u.memref.shm))
20164cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_reg_mem(mp, p);
20264cf9d8aSVolodymyr Babchuk 			else
20364cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_tmp_mem(mp, p);
2044fb0a5ebSJens Wiklander 			if (rc)
2054fb0a5ebSJens Wiklander 				return rc;
2064fb0a5ebSJens Wiklander 			break;
2074fb0a5ebSJens Wiklander 		default:
2084fb0a5ebSJens Wiklander 			return -EINVAL;
2094fb0a5ebSJens Wiklander 		}
2104fb0a5ebSJens Wiklander 	}
2114fb0a5ebSJens Wiklander 	return 0;
2124fb0a5ebSJens Wiklander }
2134fb0a5ebSJens Wiklander 
2144fb0a5ebSJens Wiklander static void optee_get_version(struct tee_device *teedev,
2154fb0a5ebSJens Wiklander 			      struct tee_ioctl_version_data *vers)
2164fb0a5ebSJens Wiklander {
2174fb0a5ebSJens Wiklander 	struct tee_ioctl_version_data v = {
2184fb0a5ebSJens Wiklander 		.impl_id = TEE_IMPL_ID_OPTEE,
2194fb0a5ebSJens Wiklander 		.impl_caps = TEE_OPTEE_CAP_TZ,
2204fb0a5ebSJens Wiklander 		.gen_caps = TEE_GEN_CAP_GP,
2214fb0a5ebSJens Wiklander 	};
2224fb0a5ebSJens Wiklander 	*vers = v;
2234fb0a5ebSJens Wiklander }
2244fb0a5ebSJens Wiklander 
2254fb0a5ebSJens Wiklander static int optee_open(struct tee_context *ctx)
2264fb0a5ebSJens Wiklander {
2274fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata;
2284fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2294fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2304fb0a5ebSJens Wiklander 
2314fb0a5ebSJens Wiklander 	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
2324fb0a5ebSJens Wiklander 	if (!ctxdata)
2334fb0a5ebSJens Wiklander 		return -ENOMEM;
2344fb0a5ebSJens Wiklander 
2354fb0a5ebSJens Wiklander 	if (teedev == optee->supp_teedev) {
2364fb0a5ebSJens Wiklander 		bool busy = true;
2374fb0a5ebSJens Wiklander 
2384fb0a5ebSJens Wiklander 		mutex_lock(&optee->supp.ctx_mutex);
2394fb0a5ebSJens Wiklander 		if (!optee->supp.ctx) {
2404fb0a5ebSJens Wiklander 			busy = false;
2414fb0a5ebSJens Wiklander 			optee->supp.ctx = ctx;
2424fb0a5ebSJens Wiklander 		}
2434fb0a5ebSJens Wiklander 		mutex_unlock(&optee->supp.ctx_mutex);
2444fb0a5ebSJens Wiklander 		if (busy) {
2454fb0a5ebSJens Wiklander 			kfree(ctxdata);
2464fb0a5ebSJens Wiklander 			return -EBUSY;
2474fb0a5ebSJens Wiklander 		}
2484fb0a5ebSJens Wiklander 	}
2494fb0a5ebSJens Wiklander 
2504fb0a5ebSJens Wiklander 	mutex_init(&ctxdata->mutex);
2514fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&ctxdata->sess_list);
2524fb0a5ebSJens Wiklander 
2534fb0a5ebSJens Wiklander 	ctx->data = ctxdata;
2544fb0a5ebSJens Wiklander 	return 0;
2554fb0a5ebSJens Wiklander }
2564fb0a5ebSJens Wiklander 
2574fb0a5ebSJens Wiklander static void optee_release(struct tee_context *ctx)
2584fb0a5ebSJens Wiklander {
2594fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata = ctx->data;
2604fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2614fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2624fb0a5ebSJens Wiklander 	struct tee_shm *shm;
2634fb0a5ebSJens Wiklander 	struct optee_msg_arg *arg = NULL;
2644fb0a5ebSJens Wiklander 	phys_addr_t parg;
2654fb0a5ebSJens Wiklander 	struct optee_session *sess;
2664fb0a5ebSJens Wiklander 	struct optee_session *sess_tmp;
2674fb0a5ebSJens Wiklander 
2684fb0a5ebSJens Wiklander 	if (!ctxdata)
2694fb0a5ebSJens Wiklander 		return;
2704fb0a5ebSJens Wiklander 
2714fb0a5ebSJens Wiklander 	shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED);
2724fb0a5ebSJens Wiklander 	if (!IS_ERR(shm)) {
2734fb0a5ebSJens Wiklander 		arg = tee_shm_get_va(shm, 0);
2744fb0a5ebSJens Wiklander 		/*
275efb14036SJens Wiklander 		 * If va2pa fails for some reason, we can't call into
276efb14036SJens Wiklander 		 * secure world, only free the memory. Secure OS will leak
277efb14036SJens Wiklander 		 * sessions and finally refuse more sessions, but we will
278efb14036SJens Wiklander 		 * at least let normal world reclaim its memory.
2794fb0a5ebSJens Wiklander 		 */
2804fb0a5ebSJens Wiklander 		if (!IS_ERR(arg))
281efb14036SJens Wiklander 			if (tee_shm_va2pa(shm, arg, &parg))
282efb14036SJens Wiklander 				arg = NULL; /* prevent usage of parg below */
2834fb0a5ebSJens Wiklander 	}
2844fb0a5ebSJens Wiklander 
2854fb0a5ebSJens Wiklander 	list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
2864fb0a5ebSJens Wiklander 				 list_node) {
2874fb0a5ebSJens Wiklander 		list_del(&sess->list_node);
2884fb0a5ebSJens Wiklander 		if (!IS_ERR_OR_NULL(arg)) {
2894fb0a5ebSJens Wiklander 			memset(arg, 0, sizeof(*arg));
2904fb0a5ebSJens Wiklander 			arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
2914fb0a5ebSJens Wiklander 			arg->session = sess->session_id;
2924fb0a5ebSJens Wiklander 			optee_do_call_with_arg(ctx, parg);
2934fb0a5ebSJens Wiklander 		}
2944fb0a5ebSJens Wiklander 		kfree(sess);
2954fb0a5ebSJens Wiklander 	}
2964fb0a5ebSJens Wiklander 	kfree(ctxdata);
2974fb0a5ebSJens Wiklander 
2984fb0a5ebSJens Wiklander 	if (!IS_ERR(shm))
2994fb0a5ebSJens Wiklander 		tee_shm_free(shm);
3004fb0a5ebSJens Wiklander 
3014fb0a5ebSJens Wiklander 	ctx->data = NULL;
3024fb0a5ebSJens Wiklander 
3034fb0a5ebSJens Wiklander 	if (teedev == optee->supp_teedev) {
3044fb0a5ebSJens Wiklander 		mutex_lock(&optee->supp.ctx_mutex);
3054fb0a5ebSJens Wiklander 		optee->supp.ctx = NULL;
3064fb0a5ebSJens Wiklander 		mutex_unlock(&optee->supp.ctx_mutex);
3074fb0a5ebSJens Wiklander 	}
3084fb0a5ebSJens Wiklander }
3094fb0a5ebSJens Wiklander 
31096e72ddeSBhumika Goyal static const struct tee_driver_ops optee_ops = {
3114fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3124fb0a5ebSJens Wiklander 	.open = optee_open,
3134fb0a5ebSJens Wiklander 	.release = optee_release,
3144fb0a5ebSJens Wiklander 	.open_session = optee_open_session,
3154fb0a5ebSJens Wiklander 	.close_session = optee_close_session,
3164fb0a5ebSJens Wiklander 	.invoke_func = optee_invoke_func,
3174fb0a5ebSJens Wiklander 	.cancel_req = optee_cancel_req,
31806ca7917SVolodymyr Babchuk 	.shm_register = optee_shm_register,
31906ca7917SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister,
3204fb0a5ebSJens Wiklander };
3214fb0a5ebSJens Wiklander 
32296e72ddeSBhumika Goyal static const struct tee_desc optee_desc = {
3234fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-clnt",
3244fb0a5ebSJens Wiklander 	.ops = &optee_ops,
3254fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3264fb0a5ebSJens Wiklander };
3274fb0a5ebSJens Wiklander 
32896e72ddeSBhumika Goyal static const struct tee_driver_ops optee_supp_ops = {
3294fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3304fb0a5ebSJens Wiklander 	.open = optee_open,
3314fb0a5ebSJens Wiklander 	.release = optee_release,
3324fb0a5ebSJens Wiklander 	.supp_recv = optee_supp_recv,
3334fb0a5ebSJens Wiklander 	.supp_send = optee_supp_send,
3344fb0a5ebSJens Wiklander };
3354fb0a5ebSJens Wiklander 
33696e72ddeSBhumika Goyal static const struct tee_desc optee_supp_desc = {
3374fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-supp",
3384fb0a5ebSJens Wiklander 	.ops = &optee_supp_ops,
3394fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3404fb0a5ebSJens Wiklander 	.flags = TEE_DESC_PRIVILEGED,
3414fb0a5ebSJens Wiklander };
3424fb0a5ebSJens Wiklander 
3434fb0a5ebSJens Wiklander static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
3444fb0a5ebSJens Wiklander {
3454fb0a5ebSJens Wiklander 	struct arm_smccc_res res;
3464fb0a5ebSJens Wiklander 
3474fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
3484fb0a5ebSJens Wiklander 
3494fb0a5ebSJens Wiklander 	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
3504fb0a5ebSJens Wiklander 	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
3514fb0a5ebSJens Wiklander 		return true;
3524fb0a5ebSJens Wiklander 	return false;
3534fb0a5ebSJens Wiklander }
3544fb0a5ebSJens Wiklander 
3554fb0a5ebSJens Wiklander static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
3564fb0a5ebSJens Wiklander {
3574fb0a5ebSJens Wiklander 	union {
3584fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
3594fb0a5ebSJens Wiklander 		struct optee_smc_calls_revision_result result;
3604fb0a5ebSJens Wiklander 	} res;
3614fb0a5ebSJens Wiklander 
3624fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
3634fb0a5ebSJens Wiklander 
3644fb0a5ebSJens Wiklander 	if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
3654fb0a5ebSJens Wiklander 	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
3664fb0a5ebSJens Wiklander 		return true;
3674fb0a5ebSJens Wiklander 	return false;
3684fb0a5ebSJens Wiklander }
3694fb0a5ebSJens Wiklander 
3704fb0a5ebSJens Wiklander static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
3714fb0a5ebSJens Wiklander 					    u32 *sec_caps)
3724fb0a5ebSJens Wiklander {
3734fb0a5ebSJens Wiklander 	union {
3744fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
3754fb0a5ebSJens Wiklander 		struct optee_smc_exchange_capabilities_result result;
3764fb0a5ebSJens Wiklander 	} res;
3774fb0a5ebSJens Wiklander 	u32 a1 = 0;
3784fb0a5ebSJens Wiklander 
3794fb0a5ebSJens Wiklander 	/*
3804fb0a5ebSJens Wiklander 	 * TODO This isn't enough to tell if it's UP system (from kernel
3814fb0a5ebSJens Wiklander 	 * point of view) or not, is_smp() returns the the information
3824fb0a5ebSJens Wiklander 	 * needed, but can't be called directly from here.
3834fb0a5ebSJens Wiklander 	 */
3844fb0a5ebSJens Wiklander 	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1)
3854fb0a5ebSJens Wiklander 		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
3864fb0a5ebSJens Wiklander 
3874fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0,
3884fb0a5ebSJens Wiklander 		  &res.smccc);
3894fb0a5ebSJens Wiklander 
3904fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK)
3914fb0a5ebSJens Wiklander 		return false;
3924fb0a5ebSJens Wiklander 
3934fb0a5ebSJens Wiklander 	*sec_caps = res.result.capabilities;
3944fb0a5ebSJens Wiklander 	return true;
3954fb0a5ebSJens Wiklander }
3964fb0a5ebSJens Wiklander 
3974fb0a5ebSJens Wiklander static struct tee_shm_pool *
3984fb0a5ebSJens Wiklander optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
3994fb0a5ebSJens Wiklander {
4004fb0a5ebSJens Wiklander 	union {
4014fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4024fb0a5ebSJens Wiklander 		struct optee_smc_get_shm_config_result result;
4034fb0a5ebSJens Wiklander 	} res;
4044fb0a5ebSJens Wiklander 	struct tee_shm_pool *pool;
4054fb0a5ebSJens Wiklander 	unsigned long vaddr;
4064fb0a5ebSJens Wiklander 	phys_addr_t paddr;
4074fb0a5ebSJens Wiklander 	size_t size;
4084fb0a5ebSJens Wiklander 	phys_addr_t begin;
4094fb0a5ebSJens Wiklander 	phys_addr_t end;
4104fb0a5ebSJens Wiklander 	void *va;
4114fb0a5ebSJens Wiklander 	struct tee_shm_pool_mem_info priv_info;
4124fb0a5ebSJens Wiklander 	struct tee_shm_pool_mem_info dmabuf_info;
4134fb0a5ebSJens Wiklander 
4144fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
4154fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK) {
4164fb0a5ebSJens Wiklander 		pr_info("shm service not available\n");
4174fb0a5ebSJens Wiklander 		return ERR_PTR(-ENOENT);
4184fb0a5ebSJens Wiklander 	}
4194fb0a5ebSJens Wiklander 
4204fb0a5ebSJens Wiklander 	if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
4214fb0a5ebSJens Wiklander 		pr_err("only normal cached shared memory supported\n");
4224fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4234fb0a5ebSJens Wiklander 	}
4244fb0a5ebSJens Wiklander 
4254fb0a5ebSJens Wiklander 	begin = roundup(res.result.start, PAGE_SIZE);
4264fb0a5ebSJens Wiklander 	end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
4274fb0a5ebSJens Wiklander 	paddr = begin;
4284fb0a5ebSJens Wiklander 	size = end - begin;
4294fb0a5ebSJens Wiklander 
4304fb0a5ebSJens Wiklander 	if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) {
4314fb0a5ebSJens Wiklander 		pr_err("too small shared memory area\n");
4324fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4334fb0a5ebSJens Wiklander 	}
4344fb0a5ebSJens Wiklander 
4354fb0a5ebSJens Wiklander 	va = memremap(paddr, size, MEMREMAP_WB);
4364fb0a5ebSJens Wiklander 	if (!va) {
4374fb0a5ebSJens Wiklander 		pr_err("shared memory ioremap failed\n");
4384fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4394fb0a5ebSJens Wiklander 	}
4404fb0a5ebSJens Wiklander 	vaddr = (unsigned long)va;
4414fb0a5ebSJens Wiklander 
4424fb0a5ebSJens Wiklander 	priv_info.vaddr = vaddr;
4434fb0a5ebSJens Wiklander 	priv_info.paddr = paddr;
4444fb0a5ebSJens Wiklander 	priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4454fb0a5ebSJens Wiklander 	dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4464fb0a5ebSJens Wiklander 	dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4474fb0a5ebSJens Wiklander 	dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4484fb0a5ebSJens Wiklander 
4494fb0a5ebSJens Wiklander 	pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info);
4504fb0a5ebSJens Wiklander 	if (IS_ERR(pool)) {
4514fb0a5ebSJens Wiklander 		memunmap(va);
4524fb0a5ebSJens Wiklander 		goto out;
4534fb0a5ebSJens Wiklander 	}
4544fb0a5ebSJens Wiklander 
4554fb0a5ebSJens Wiklander 	*memremaped_shm = va;
4564fb0a5ebSJens Wiklander out:
4574fb0a5ebSJens Wiklander 	return pool;
4584fb0a5ebSJens Wiklander }
4594fb0a5ebSJens Wiklander 
4604fb0a5ebSJens Wiklander /* Simple wrapper functions to be able to use a function pointer */
4614fb0a5ebSJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1,
4624fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
4634fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
4644fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
4654fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
4664fb0a5ebSJens Wiklander {
4674fb0a5ebSJens Wiklander 	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
4684fb0a5ebSJens Wiklander }
4694fb0a5ebSJens Wiklander 
4704fb0a5ebSJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
4714fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
4724fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
4734fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
4744fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
4754fb0a5ebSJens Wiklander {
4764fb0a5ebSJens Wiklander 	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
4774fb0a5ebSJens Wiklander }
4784fb0a5ebSJens Wiklander 
4794fb0a5ebSJens Wiklander static optee_invoke_fn *get_invoke_func(struct device_node *np)
4804fb0a5ebSJens Wiklander {
4814fb0a5ebSJens Wiklander 	const char *method;
4824fb0a5ebSJens Wiklander 
4834fb0a5ebSJens Wiklander 	pr_info("probing for conduit method from DT.\n");
4844fb0a5ebSJens Wiklander 
4854fb0a5ebSJens Wiklander 	if (of_property_read_string(np, "method", &method)) {
4864fb0a5ebSJens Wiklander 		pr_warn("missing \"method\" property\n");
4874fb0a5ebSJens Wiklander 		return ERR_PTR(-ENXIO);
4884fb0a5ebSJens Wiklander 	}
4894fb0a5ebSJens Wiklander 
4904fb0a5ebSJens Wiklander 	if (!strcmp("hvc", method))
4914fb0a5ebSJens Wiklander 		return optee_smccc_hvc;
4924fb0a5ebSJens Wiklander 	else if (!strcmp("smc", method))
4934fb0a5ebSJens Wiklander 		return optee_smccc_smc;
4944fb0a5ebSJens Wiklander 
4954fb0a5ebSJens Wiklander 	pr_warn("invalid \"method\" property: %s\n", method);
4964fb0a5ebSJens Wiklander 	return ERR_PTR(-EINVAL);
4974fb0a5ebSJens Wiklander }
4984fb0a5ebSJens Wiklander 
4994fb0a5ebSJens Wiklander static struct optee *optee_probe(struct device_node *np)
5004fb0a5ebSJens Wiklander {
5014fb0a5ebSJens Wiklander 	optee_invoke_fn *invoke_fn;
5024fb0a5ebSJens Wiklander 	struct tee_shm_pool *pool;
5034fb0a5ebSJens Wiklander 	struct optee *optee = NULL;
5044fb0a5ebSJens Wiklander 	void *memremaped_shm = NULL;
5054fb0a5ebSJens Wiklander 	struct tee_device *teedev;
5064fb0a5ebSJens Wiklander 	u32 sec_caps;
5074fb0a5ebSJens Wiklander 	int rc;
5084fb0a5ebSJens Wiklander 
5094fb0a5ebSJens Wiklander 	invoke_fn = get_invoke_func(np);
5104fb0a5ebSJens Wiklander 	if (IS_ERR(invoke_fn))
5114fb0a5ebSJens Wiklander 		return (void *)invoke_fn;
5124fb0a5ebSJens Wiklander 
5134fb0a5ebSJens Wiklander 	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
5144fb0a5ebSJens Wiklander 		pr_warn("api uid mismatch\n");
5154fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5164fb0a5ebSJens Wiklander 	}
5174fb0a5ebSJens Wiklander 
5184fb0a5ebSJens Wiklander 	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
5194fb0a5ebSJens Wiklander 		pr_warn("api revision mismatch\n");
5204fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5214fb0a5ebSJens Wiklander 	}
5224fb0a5ebSJens Wiklander 
5234fb0a5ebSJens Wiklander 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
5244fb0a5ebSJens Wiklander 		pr_warn("capabilities mismatch\n");
5254fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5264fb0a5ebSJens Wiklander 	}
5274fb0a5ebSJens Wiklander 
5284fb0a5ebSJens Wiklander 	/*
5294fb0a5ebSJens Wiklander 	 * We have no other option for shared memory, if secure world
5304fb0a5ebSJens Wiklander 	 * doesn't have any reserved memory we can use we can't continue.
5314fb0a5ebSJens Wiklander 	 */
5324fb0a5ebSJens Wiklander 	if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
5334fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5344fb0a5ebSJens Wiklander 
5354fb0a5ebSJens Wiklander 	pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
5364fb0a5ebSJens Wiklander 	if (IS_ERR(pool))
5374fb0a5ebSJens Wiklander 		return (void *)pool;
5384fb0a5ebSJens Wiklander 
5394fb0a5ebSJens Wiklander 	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
5404fb0a5ebSJens Wiklander 	if (!optee) {
5414fb0a5ebSJens Wiklander 		rc = -ENOMEM;
5424fb0a5ebSJens Wiklander 		goto err;
5434fb0a5ebSJens Wiklander 	}
5444fb0a5ebSJens Wiklander 
5454fb0a5ebSJens Wiklander 	optee->invoke_fn = invoke_fn;
5464fb0a5ebSJens Wiklander 
5474fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
5484fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
5494fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
5504fb0a5ebSJens Wiklander 		goto err;
5514fb0a5ebSJens Wiklander 	}
5524fb0a5ebSJens Wiklander 	optee->teedev = teedev;
5534fb0a5ebSJens Wiklander 
5544fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
5554fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
5564fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
5574fb0a5ebSJens Wiklander 		goto err;
5584fb0a5ebSJens Wiklander 	}
5594fb0a5ebSJens Wiklander 	optee->supp_teedev = teedev;
5604fb0a5ebSJens Wiklander 
5614fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->teedev);
5624fb0a5ebSJens Wiklander 	if (rc)
5634fb0a5ebSJens Wiklander 		goto err;
5644fb0a5ebSJens Wiklander 
5654fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->supp_teedev);
5664fb0a5ebSJens Wiklander 	if (rc)
5674fb0a5ebSJens Wiklander 		goto err;
5684fb0a5ebSJens Wiklander 
5694fb0a5ebSJens Wiklander 	mutex_init(&optee->call_queue.mutex);
5704fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&optee->call_queue.waiters);
5714fb0a5ebSJens Wiklander 	optee_wait_queue_init(&optee->wait_queue);
5724fb0a5ebSJens Wiklander 	optee_supp_init(&optee->supp);
5734fb0a5ebSJens Wiklander 	optee->memremaped_shm = memremaped_shm;
5744fb0a5ebSJens Wiklander 	optee->pool = pool;
5754fb0a5ebSJens Wiklander 
5764fb0a5ebSJens Wiklander 	optee_enable_shm_cache(optee);
5774fb0a5ebSJens Wiklander 
5784fb0a5ebSJens Wiklander 	pr_info("initialized driver\n");
5794fb0a5ebSJens Wiklander 	return optee;
5804fb0a5ebSJens Wiklander err:
5814fb0a5ebSJens Wiklander 	if (optee) {
5824fb0a5ebSJens Wiklander 		/*
5834fb0a5ebSJens Wiklander 		 * tee_device_unregister() is safe to call even if the
5844fb0a5ebSJens Wiklander 		 * devices hasn't been registered with
5854fb0a5ebSJens Wiklander 		 * tee_device_register() yet.
5864fb0a5ebSJens Wiklander 		 */
5874fb0a5ebSJens Wiklander 		tee_device_unregister(optee->supp_teedev);
5884fb0a5ebSJens Wiklander 		tee_device_unregister(optee->teedev);
5894fb0a5ebSJens Wiklander 		kfree(optee);
5904fb0a5ebSJens Wiklander 	}
5914fb0a5ebSJens Wiklander 	if (pool)
5924fb0a5ebSJens Wiklander 		tee_shm_pool_free(pool);
5934fb0a5ebSJens Wiklander 	if (memremaped_shm)
5944fb0a5ebSJens Wiklander 		memunmap(memremaped_shm);
5954fb0a5ebSJens Wiklander 	return ERR_PTR(rc);
5964fb0a5ebSJens Wiklander }
5974fb0a5ebSJens Wiklander 
5984fb0a5ebSJens Wiklander static void optee_remove(struct optee *optee)
5994fb0a5ebSJens Wiklander {
6004fb0a5ebSJens Wiklander 	/*
6014fb0a5ebSJens Wiklander 	 * Ask OP-TEE to free all cached shared memory objects to decrease
6024fb0a5ebSJens Wiklander 	 * reference counters and also avoid wild pointers in secure world
6034fb0a5ebSJens Wiklander 	 * into the old shared memory range.
6044fb0a5ebSJens Wiklander 	 */
6054fb0a5ebSJens Wiklander 	optee_disable_shm_cache(optee);
6064fb0a5ebSJens Wiklander 
6074fb0a5ebSJens Wiklander 	/*
6084fb0a5ebSJens Wiklander 	 * The two devices has to be unregistered before we can free the
6094fb0a5ebSJens Wiklander 	 * other resources.
6104fb0a5ebSJens Wiklander 	 */
6114fb0a5ebSJens Wiklander 	tee_device_unregister(optee->supp_teedev);
6124fb0a5ebSJens Wiklander 	tee_device_unregister(optee->teedev);
6134fb0a5ebSJens Wiklander 
6144fb0a5ebSJens Wiklander 	tee_shm_pool_free(optee->pool);
6154fb0a5ebSJens Wiklander 	if (optee->memremaped_shm)
6164fb0a5ebSJens Wiklander 		memunmap(optee->memremaped_shm);
6174fb0a5ebSJens Wiklander 	optee_wait_queue_exit(&optee->wait_queue);
6184fb0a5ebSJens Wiklander 	optee_supp_uninit(&optee->supp);
6194fb0a5ebSJens Wiklander 	mutex_destroy(&optee->call_queue.mutex);
6204fb0a5ebSJens Wiklander 
6214fb0a5ebSJens Wiklander 	kfree(optee);
6224fb0a5ebSJens Wiklander }
6234fb0a5ebSJens Wiklander 
6244fb0a5ebSJens Wiklander static const struct of_device_id optee_match[] = {
6254fb0a5ebSJens Wiklander 	{ .compatible = "linaro,optee-tz" },
6264fb0a5ebSJens Wiklander 	{},
6274fb0a5ebSJens Wiklander };
6284fb0a5ebSJens Wiklander 
6294fb0a5ebSJens Wiklander static struct optee *optee_svc;
6304fb0a5ebSJens Wiklander 
6314fb0a5ebSJens Wiklander static int __init optee_driver_init(void)
6324fb0a5ebSJens Wiklander {
6334fb0a5ebSJens Wiklander 	struct device_node *fw_np;
6344fb0a5ebSJens Wiklander 	struct device_node *np;
6354fb0a5ebSJens Wiklander 	struct optee *optee;
6364fb0a5ebSJens Wiklander 
6374fb0a5ebSJens Wiklander 	/* Node is supposed to be below /firmware */
6384fb0a5ebSJens Wiklander 	fw_np = of_find_node_by_name(NULL, "firmware");
6394fb0a5ebSJens Wiklander 	if (!fw_np)
6404fb0a5ebSJens Wiklander 		return -ENODEV;
6414fb0a5ebSJens Wiklander 
6424fb0a5ebSJens Wiklander 	np = of_find_matching_node(fw_np, optee_match);
6434fb0a5ebSJens Wiklander 	if (!np)
6444fb0a5ebSJens Wiklander 		return -ENODEV;
6454fb0a5ebSJens Wiklander 
6464fb0a5ebSJens Wiklander 	optee = optee_probe(np);
6474fb0a5ebSJens Wiklander 	of_node_put(np);
6484fb0a5ebSJens Wiklander 
6494fb0a5ebSJens Wiklander 	if (IS_ERR(optee))
6504fb0a5ebSJens Wiklander 		return PTR_ERR(optee);
6514fb0a5ebSJens Wiklander 
6524fb0a5ebSJens Wiklander 	optee_svc = optee;
6534fb0a5ebSJens Wiklander 
6544fb0a5ebSJens Wiklander 	return 0;
6554fb0a5ebSJens Wiklander }
6564fb0a5ebSJens Wiklander module_init(optee_driver_init);
6574fb0a5ebSJens Wiklander 
6584fb0a5ebSJens Wiklander static void __exit optee_driver_exit(void)
6594fb0a5ebSJens Wiklander {
6604fb0a5ebSJens Wiklander 	struct optee *optee = optee_svc;
6614fb0a5ebSJens Wiklander 
6624fb0a5ebSJens Wiklander 	optee_svc = NULL;
6634fb0a5ebSJens Wiklander 	if (optee)
6644fb0a5ebSJens Wiklander 		optee_remove(optee);
6654fb0a5ebSJens Wiklander }
6664fb0a5ebSJens Wiklander module_exit(optee_driver_exit);
6674fb0a5ebSJens Wiklander 
6684fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro");
6694fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver");
6704fb0a5ebSJens Wiklander MODULE_SUPPORTED_DEVICE("");
6714fb0a5ebSJens Wiklander MODULE_VERSION("1.0");
6724fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2");
673