xref: /openbmc/linux/drivers/tee/optee/core.c (revision f349710e)
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 
537f349710eSArd Biesheuvel static optee_invoke_fn *get_invoke_func(struct device *dev)
5384fb0a5ebSJens Wiklander {
5394fb0a5ebSJens Wiklander 	const char *method;
5404fb0a5ebSJens Wiklander 
541f349710eSArd Biesheuvel 	pr_info("probing for conduit method.\n");
5424fb0a5ebSJens Wiklander 
543f349710eSArd Biesheuvel 	if (device_property_read_string(dev, "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 
557f349710eSArd Biesheuvel static int optee_remove(struct platform_device *pdev)
558f349710eSArd Biesheuvel {
559f349710eSArd Biesheuvel 	struct optee *optee = platform_get_drvdata(pdev);
560f349710eSArd Biesheuvel 
561f349710eSArd Biesheuvel 	/*
562f349710eSArd Biesheuvel 	 * Ask OP-TEE to free all cached shared memory objects to decrease
563f349710eSArd Biesheuvel 	 * reference counters and also avoid wild pointers in secure world
564f349710eSArd Biesheuvel 	 * into the old shared memory range.
565f349710eSArd Biesheuvel 	 */
566f349710eSArd Biesheuvel 	optee_disable_shm_cache(optee);
567f349710eSArd Biesheuvel 
568f349710eSArd Biesheuvel 	/*
569f349710eSArd Biesheuvel 	 * The two devices have to be unregistered before we can free the
570f349710eSArd Biesheuvel 	 * other resources.
571f349710eSArd Biesheuvel 	 */
572f349710eSArd Biesheuvel 	tee_device_unregister(optee->supp_teedev);
573f349710eSArd Biesheuvel 	tee_device_unregister(optee->teedev);
574f349710eSArd Biesheuvel 
575f349710eSArd Biesheuvel 	tee_shm_pool_free(optee->pool);
576f349710eSArd Biesheuvel 	if (optee->memremaped_shm)
577f349710eSArd Biesheuvel 		memunmap(optee->memremaped_shm);
578f349710eSArd Biesheuvel 	optee_wait_queue_exit(&optee->wait_queue);
579f349710eSArd Biesheuvel 	optee_supp_uninit(&optee->supp);
580f349710eSArd Biesheuvel 	mutex_destroy(&optee->call_queue.mutex);
581f349710eSArd Biesheuvel 
582f349710eSArd Biesheuvel 	kfree(optee);
583f349710eSArd Biesheuvel 
584f349710eSArd Biesheuvel 	return 0;
585f349710eSArd Biesheuvel }
586f349710eSArd Biesheuvel 
587f349710eSArd Biesheuvel static int optee_probe(struct platform_device *pdev)
5884fb0a5ebSJens Wiklander {
5894fb0a5ebSJens Wiklander 	optee_invoke_fn *invoke_fn;
5909733b072SVolodymyr Babchuk 	struct tee_shm_pool *pool = ERR_PTR(-EINVAL);
5914fb0a5ebSJens Wiklander 	struct optee *optee = NULL;
5924fb0a5ebSJens Wiklander 	void *memremaped_shm = NULL;
5934fb0a5ebSJens Wiklander 	struct tee_device *teedev;
5944fb0a5ebSJens Wiklander 	u32 sec_caps;
5954fb0a5ebSJens Wiklander 	int rc;
5964fb0a5ebSJens Wiklander 
597f349710eSArd Biesheuvel 	invoke_fn = get_invoke_func(&pdev->dev);
5984fb0a5ebSJens Wiklander 	if (IS_ERR(invoke_fn))
599f349710eSArd Biesheuvel 		return PTR_ERR(invoke_fn);
6004fb0a5ebSJens Wiklander 
6014fb0a5ebSJens Wiklander 	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
6024fb0a5ebSJens Wiklander 		pr_warn("api uid mismatch\n");
603f349710eSArd Biesheuvel 		return -EINVAL;
6044fb0a5ebSJens Wiklander 	}
6054fb0a5ebSJens Wiklander 
6065c5f8030SJérôme Forissier 	optee_msg_get_os_revision(invoke_fn);
6075c5f8030SJérôme Forissier 
6084fb0a5ebSJens Wiklander 	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
6094fb0a5ebSJens Wiklander 		pr_warn("api revision mismatch\n");
610f349710eSArd Biesheuvel 		return -EINVAL;
6114fb0a5ebSJens Wiklander 	}
6124fb0a5ebSJens Wiklander 
6134fb0a5ebSJens Wiklander 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
6144fb0a5ebSJens Wiklander 		pr_warn("capabilities mismatch\n");
615f349710eSArd Biesheuvel 		return -EINVAL;
6164fb0a5ebSJens Wiklander 	}
6174fb0a5ebSJens Wiklander 
6184fb0a5ebSJens Wiklander 	/*
6199733b072SVolodymyr Babchuk 	 * Try to use dynamic shared memory if possible
6204fb0a5ebSJens Wiklander 	 */
6219733b072SVolodymyr Babchuk 	if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
6229733b072SVolodymyr Babchuk 		pool = optee_config_dyn_shm();
6234fb0a5ebSJens Wiklander 
6249733b072SVolodymyr Babchuk 	/*
6259733b072SVolodymyr Babchuk 	 * If dynamic shared memory is not available or failed - try static one
6269733b072SVolodymyr Babchuk 	 */
6279733b072SVolodymyr Babchuk 	if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
6289733b072SVolodymyr Babchuk 		pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
6299733b072SVolodymyr Babchuk 
6304fb0a5ebSJens Wiklander 	if (IS_ERR(pool))
631f349710eSArd Biesheuvel 		return PTR_ERR(pool);
6324fb0a5ebSJens Wiklander 
6334fb0a5ebSJens Wiklander 	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
6344fb0a5ebSJens Wiklander 	if (!optee) {
6354fb0a5ebSJens Wiklander 		rc = -ENOMEM;
6364fb0a5ebSJens Wiklander 		goto err;
6374fb0a5ebSJens Wiklander 	}
6384fb0a5ebSJens Wiklander 
6394fb0a5ebSJens Wiklander 	optee->invoke_fn = invoke_fn;
640d885cc5eSVolodymyr Babchuk 	optee->sec_caps = sec_caps;
6414fb0a5ebSJens Wiklander 
6424fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
6434fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
6444fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
6454fb0a5ebSJens Wiklander 		goto err;
6464fb0a5ebSJens Wiklander 	}
6474fb0a5ebSJens Wiklander 	optee->teedev = teedev;
6484fb0a5ebSJens Wiklander 
6494fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
6504fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
6514fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
6524fb0a5ebSJens Wiklander 		goto err;
6534fb0a5ebSJens Wiklander 	}
6544fb0a5ebSJens Wiklander 	optee->supp_teedev = teedev;
6554fb0a5ebSJens Wiklander 
6564fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->teedev);
6574fb0a5ebSJens Wiklander 	if (rc)
6584fb0a5ebSJens Wiklander 		goto err;
6594fb0a5ebSJens Wiklander 
6604fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->supp_teedev);
6614fb0a5ebSJens Wiklander 	if (rc)
6624fb0a5ebSJens Wiklander 		goto err;
6634fb0a5ebSJens Wiklander 
6644fb0a5ebSJens Wiklander 	mutex_init(&optee->call_queue.mutex);
6654fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&optee->call_queue.waiters);
6664fb0a5ebSJens Wiklander 	optee_wait_queue_init(&optee->wait_queue);
6674fb0a5ebSJens Wiklander 	optee_supp_init(&optee->supp);
6684fb0a5ebSJens Wiklander 	optee->memremaped_shm = memremaped_shm;
6694fb0a5ebSJens Wiklander 	optee->pool = pool;
6704fb0a5ebSJens Wiklander 
6714fb0a5ebSJens Wiklander 	optee_enable_shm_cache(optee);
6724fb0a5ebSJens Wiklander 
6733c15ddb9SVictor Chong 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
6743c15ddb9SVictor Chong 		pr_info("dynamic shared memory is enabled\n");
6753c15ddb9SVictor Chong 
676f349710eSArd Biesheuvel 	platform_set_drvdata(pdev, optee);
677f349710eSArd Biesheuvel 
678f349710eSArd Biesheuvel 	rc = optee_enumerate_devices();
679f349710eSArd Biesheuvel 	if (rc) {
680f349710eSArd Biesheuvel 		optee_remove(pdev);
681f349710eSArd Biesheuvel 		return rc;
682f349710eSArd Biesheuvel 	}
683f349710eSArd Biesheuvel 
684f349710eSArd Biesheuvel 	pr_info("initialized driver\n");
685f349710eSArd Biesheuvel 	return 0;
6864fb0a5ebSJens Wiklander err:
6874fb0a5ebSJens Wiklander 	if (optee) {
6884fb0a5ebSJens Wiklander 		/*
6894fb0a5ebSJens Wiklander 		 * tee_device_unregister() is safe to call even if the
6904fb0a5ebSJens Wiklander 		 * devices hasn't been registered with
6914fb0a5ebSJens Wiklander 		 * tee_device_register() yet.
6924fb0a5ebSJens Wiklander 		 */
6934fb0a5ebSJens Wiklander 		tee_device_unregister(optee->supp_teedev);
6944fb0a5ebSJens Wiklander 		tee_device_unregister(optee->teedev);
6954fb0a5ebSJens Wiklander 		kfree(optee);
6964fb0a5ebSJens Wiklander 	}
6974fb0a5ebSJens Wiklander 	if (pool)
6984fb0a5ebSJens Wiklander 		tee_shm_pool_free(pool);
6994fb0a5ebSJens Wiklander 	if (memremaped_shm)
7004fb0a5ebSJens Wiklander 		memunmap(memremaped_shm);
70103212e34SJens Wiklander 	return rc;
70203212e34SJens Wiklander }
70303212e34SJens Wiklander 
704f349710eSArd Biesheuvel static const struct of_device_id optee_dt_match[] = {
705f349710eSArd Biesheuvel 	{ .compatible = "linaro,optee-tz" },
706f349710eSArd Biesheuvel 	{},
707f349710eSArd Biesheuvel };
708f349710eSArd Biesheuvel MODULE_DEVICE_TABLE(of, optee_dt_match);
70903212e34SJens Wiklander 
710f349710eSArd Biesheuvel static struct platform_driver optee_driver = {
711f349710eSArd Biesheuvel 	.probe  = optee_probe,
712f349710eSArd Biesheuvel 	.remove = optee_remove,
713f349710eSArd Biesheuvel 	.driver = {
714f349710eSArd Biesheuvel 		.name = "optee",
715f349710eSArd Biesheuvel 		.of_match_table = optee_dt_match,
716f349710eSArd Biesheuvel 	},
717f349710eSArd Biesheuvel };
718f349710eSArd Biesheuvel module_platform_driver(optee_driver);
7194fb0a5ebSJens Wiklander 
7204fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro");
7214fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver");
7224fb0a5ebSJens Wiklander MODULE_SUPPORTED_DEVICE("");
7234fb0a5ebSJens Wiklander MODULE_VERSION("1.0");
7244fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2");
725f349710eSArd Biesheuvel MODULE_ALIAS("platform:optee");
726