xref: /openbmc/linux/drivers/tee/optee/core.c (revision 7f565d0e)
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>
9adf752afSTyler Hicks #include <linux/crash_dump.h>
104fb0a5ebSJens Wiklander #include <linux/errno.h>
114fb0a5ebSJens Wiklander #include <linux/io.h>
124fb0a5ebSJens Wiklander #include <linux/module.h>
134fb0a5ebSJens Wiklander #include <linux/of.h>
144fb0a5ebSJens Wiklander #include <linux/of_platform.h>
154fb0a5ebSJens Wiklander #include <linux/platform_device.h>
164fb0a5ebSJens Wiklander #include <linux/slab.h>
174fb0a5ebSJens Wiklander #include <linux/string.h>
184fb0a5ebSJens Wiklander #include <linux/tee_drv.h>
194fb0a5ebSJens Wiklander #include <linux/types.h>
204fb0a5ebSJens Wiklander #include <linux/uaccess.h>
215f178bb7SMaxim Uvarov #include <linux/workqueue.h>
224fb0a5ebSJens Wiklander #include "optee_private.h"
234fb0a5ebSJens Wiklander #include "optee_smc.h"
24f58e236cSVolodymyr Babchuk #include "shm_pool.h"
254fb0a5ebSJens Wiklander 
264fb0a5ebSJens Wiklander #define DRIVER_NAME "optee"
274fb0a5ebSJens Wiklander 
283249527fSSahil Malhotra #define OPTEE_SHM_NUM_PRIV_PAGES	CONFIG_OPTEE_SHM_NUM_PRIV_PAGES
294fb0a5ebSJens Wiklander 
304fb0a5ebSJens Wiklander /**
314fb0a5ebSJens Wiklander  * optee_from_msg_param() - convert from OPTEE_MSG parameters to
324fb0a5ebSJens Wiklander  *			    struct tee_param
334fb0a5ebSJens Wiklander  * @params:	subsystem internal parameter representation
344fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
354fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
364fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
374fb0a5ebSJens Wiklander  */
384fb0a5ebSJens Wiklander int optee_from_msg_param(struct tee_param *params, size_t num_params,
394fb0a5ebSJens Wiklander 			 const struct optee_msg_param *msg_params)
404fb0a5ebSJens Wiklander {
414fb0a5ebSJens Wiklander 	int rc;
424fb0a5ebSJens Wiklander 	size_t n;
434fb0a5ebSJens Wiklander 	struct tee_shm *shm;
444fb0a5ebSJens Wiklander 	phys_addr_t pa;
454fb0a5ebSJens Wiklander 
464fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
474fb0a5ebSJens Wiklander 		struct tee_param *p = params + n;
484fb0a5ebSJens Wiklander 		const struct optee_msg_param *mp = msg_params + n;
494fb0a5ebSJens Wiklander 		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
504fb0a5ebSJens Wiklander 
514fb0a5ebSJens Wiklander 		switch (attr) {
524fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_NONE:
534fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
544fb0a5ebSJens Wiklander 			memset(&p->u, 0, sizeof(p->u));
554fb0a5ebSJens Wiklander 			break;
564fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
574fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
584fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
594fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
604fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
614fb0a5ebSJens Wiklander 			p->u.value.a = mp->u.value.a;
624fb0a5ebSJens Wiklander 			p->u.value.b = mp->u.value.b;
634fb0a5ebSJens Wiklander 			p->u.value.c = mp->u.value.c;
644fb0a5ebSJens Wiklander 			break;
654fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
664fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
674fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
684fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
694fb0a5ebSJens Wiklander 				  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
704fb0a5ebSJens Wiklander 			p->u.memref.size = mp->u.tmem.size;
714fb0a5ebSJens Wiklander 			shm = (struct tee_shm *)(unsigned long)
724fb0a5ebSJens Wiklander 				mp->u.tmem.shm_ref;
734fb0a5ebSJens Wiklander 			if (!shm) {
744fb0a5ebSJens Wiklander 				p->u.memref.shm_offs = 0;
754fb0a5ebSJens Wiklander 				p->u.memref.shm = NULL;
764fb0a5ebSJens Wiklander 				break;
774fb0a5ebSJens Wiklander 			}
784fb0a5ebSJens Wiklander 			rc = tee_shm_get_pa(shm, 0, &pa);
794fb0a5ebSJens Wiklander 			if (rc)
804fb0a5ebSJens Wiklander 				return rc;
814fb0a5ebSJens Wiklander 			p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
824fb0a5ebSJens Wiklander 			p->u.memref.shm = shm;
834fb0a5ebSJens Wiklander 			break;
8464cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
8564cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
8664cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
8764cf9d8aSVolodymyr Babchuk 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
8864cf9d8aSVolodymyr Babchuk 				  attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
8964cf9d8aSVolodymyr Babchuk 			p->u.memref.size = mp->u.rmem.size;
9064cf9d8aSVolodymyr Babchuk 			shm = (struct tee_shm *)(unsigned long)
9164cf9d8aSVolodymyr Babchuk 				mp->u.rmem.shm_ref;
9264cf9d8aSVolodymyr Babchuk 
9364cf9d8aSVolodymyr Babchuk 			if (!shm) {
9464cf9d8aSVolodymyr Babchuk 				p->u.memref.shm_offs = 0;
9564cf9d8aSVolodymyr Babchuk 				p->u.memref.shm = NULL;
9664cf9d8aSVolodymyr Babchuk 				break;
9764cf9d8aSVolodymyr Babchuk 			}
9864cf9d8aSVolodymyr Babchuk 			p->u.memref.shm_offs = mp->u.rmem.offs;
9964cf9d8aSVolodymyr Babchuk 			p->u.memref.shm = shm;
10064cf9d8aSVolodymyr Babchuk 
10164cf9d8aSVolodymyr Babchuk 			break;
10264cf9d8aSVolodymyr Babchuk 
1034fb0a5ebSJens Wiklander 		default:
1044fb0a5ebSJens Wiklander 			return -EINVAL;
1054fb0a5ebSJens Wiklander 		}
1064fb0a5ebSJens Wiklander 	}
1074fb0a5ebSJens Wiklander 	return 0;
1084fb0a5ebSJens Wiklander }
1094fb0a5ebSJens Wiklander 
11064cf9d8aSVolodymyr Babchuk static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
11164cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
11264cf9d8aSVolodymyr Babchuk {
11364cf9d8aSVolodymyr Babchuk 	int rc;
11464cf9d8aSVolodymyr Babchuk 	phys_addr_t pa;
11564cf9d8aSVolodymyr Babchuk 
11664cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
11764cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
11864cf9d8aSVolodymyr Babchuk 
11964cf9d8aSVolodymyr Babchuk 	mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
12064cf9d8aSVolodymyr Babchuk 	mp->u.tmem.size = p->u.memref.size;
12164cf9d8aSVolodymyr Babchuk 
12264cf9d8aSVolodymyr Babchuk 	if (!p->u.memref.shm) {
12364cf9d8aSVolodymyr Babchuk 		mp->u.tmem.buf_ptr = 0;
12464cf9d8aSVolodymyr Babchuk 		return 0;
12564cf9d8aSVolodymyr Babchuk 	}
12664cf9d8aSVolodymyr Babchuk 
12764cf9d8aSVolodymyr Babchuk 	rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
12864cf9d8aSVolodymyr Babchuk 	if (rc)
12964cf9d8aSVolodymyr Babchuk 		return rc;
13064cf9d8aSVolodymyr Babchuk 
13164cf9d8aSVolodymyr Babchuk 	mp->u.tmem.buf_ptr = pa;
13264cf9d8aSVolodymyr Babchuk 	mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
13364cf9d8aSVolodymyr Babchuk 		    OPTEE_MSG_ATTR_CACHE_SHIFT;
13464cf9d8aSVolodymyr Babchuk 
13564cf9d8aSVolodymyr Babchuk 	return 0;
13664cf9d8aSVolodymyr Babchuk }
13764cf9d8aSVolodymyr Babchuk 
13864cf9d8aSVolodymyr Babchuk static int to_msg_param_reg_mem(struct optee_msg_param *mp,
13964cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
14064cf9d8aSVolodymyr Babchuk {
14164cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
14264cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
14364cf9d8aSVolodymyr Babchuk 
14464cf9d8aSVolodymyr Babchuk 	mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
14564cf9d8aSVolodymyr Babchuk 	mp->u.rmem.size = p->u.memref.size;
14664cf9d8aSVolodymyr Babchuk 	mp->u.rmem.offs = p->u.memref.shm_offs;
14764cf9d8aSVolodymyr Babchuk 	return 0;
14864cf9d8aSVolodymyr Babchuk }
14964cf9d8aSVolodymyr Babchuk 
1504fb0a5ebSJens Wiklander /**
1514fb0a5ebSJens Wiklander  * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
1524fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
1534fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
1544fb0a5ebSJens Wiklander  * @params:	subsystem itnernal parameter representation
1554fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
1564fb0a5ebSJens Wiklander  */
1574fb0a5ebSJens Wiklander int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
1584fb0a5ebSJens Wiklander 		       const struct tee_param *params)
1594fb0a5ebSJens Wiklander {
1604fb0a5ebSJens Wiklander 	int rc;
1614fb0a5ebSJens Wiklander 	size_t n;
1624fb0a5ebSJens Wiklander 
1634fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
1644fb0a5ebSJens Wiklander 		const struct tee_param *p = params + n;
1654fb0a5ebSJens Wiklander 		struct optee_msg_param *mp = msg_params + n;
1664fb0a5ebSJens Wiklander 
1674fb0a5ebSJens Wiklander 		switch (p->attr) {
1684fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
1694fb0a5ebSJens Wiklander 			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
1704fb0a5ebSJens Wiklander 			memset(&mp->u, 0, sizeof(mp->u));
1714fb0a5ebSJens Wiklander 			break;
1724fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
1734fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
1744fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
1754fb0a5ebSJens Wiklander 			mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
1764fb0a5ebSJens Wiklander 				   TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
1774fb0a5ebSJens Wiklander 			mp->u.value.a = p->u.value.a;
1784fb0a5ebSJens Wiklander 			mp->u.value.b = p->u.value.b;
1794fb0a5ebSJens Wiklander 			mp->u.value.c = p->u.value.c;
1804fb0a5ebSJens Wiklander 			break;
1814fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
1824fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
1834fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
18464cf9d8aSVolodymyr Babchuk 			if (tee_shm_is_registered(p->u.memref.shm))
18564cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_reg_mem(mp, p);
18664cf9d8aSVolodymyr Babchuk 			else
18764cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_tmp_mem(mp, p);
1884fb0a5ebSJens Wiklander 			if (rc)
1894fb0a5ebSJens Wiklander 				return rc;
1904fb0a5ebSJens Wiklander 			break;
1914fb0a5ebSJens Wiklander 		default:
1924fb0a5ebSJens Wiklander 			return -EINVAL;
1934fb0a5ebSJens Wiklander 		}
1944fb0a5ebSJens Wiklander 	}
1954fb0a5ebSJens Wiklander 	return 0;
1964fb0a5ebSJens Wiklander }
1974fb0a5ebSJens Wiklander 
1984fb0a5ebSJens Wiklander static void optee_get_version(struct tee_device *teedev,
1994fb0a5ebSJens Wiklander 			      struct tee_ioctl_version_data *vers)
2004fb0a5ebSJens Wiklander {
2014fb0a5ebSJens Wiklander 	struct tee_ioctl_version_data v = {
2024fb0a5ebSJens Wiklander 		.impl_id = TEE_IMPL_ID_OPTEE,
2034fb0a5ebSJens Wiklander 		.impl_caps = TEE_OPTEE_CAP_TZ,
2044fb0a5ebSJens Wiklander 		.gen_caps = TEE_GEN_CAP_GP,
2054fb0a5ebSJens Wiklander 	};
206f58e236cSVolodymyr Babchuk 	struct optee *optee = tee_get_drvdata(teedev);
207f58e236cSVolodymyr Babchuk 
208f58e236cSVolodymyr Babchuk 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
209f58e236cSVolodymyr Babchuk 		v.gen_caps |= TEE_GEN_CAP_REG_MEM;
210ba171d3fSCedric Neveux 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
211ba171d3fSCedric Neveux 		v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL;
2124fb0a5ebSJens Wiklander 	*vers = v;
2134fb0a5ebSJens Wiklander }
2144fb0a5ebSJens Wiklander 
2155f178bb7SMaxim Uvarov static void optee_bus_scan(struct work_struct *work)
2165f178bb7SMaxim Uvarov {
2175f178bb7SMaxim Uvarov 	WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
2185f178bb7SMaxim Uvarov }
2195f178bb7SMaxim Uvarov 
2204fb0a5ebSJens Wiklander static int optee_open(struct tee_context *ctx)
2214fb0a5ebSJens Wiklander {
2224fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata;
2234fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2244fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2254fb0a5ebSJens Wiklander 
2264fb0a5ebSJens Wiklander 	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
2274fb0a5ebSJens Wiklander 	if (!ctxdata)
2284fb0a5ebSJens Wiklander 		return -ENOMEM;
2294fb0a5ebSJens Wiklander 
2304fb0a5ebSJens Wiklander 	if (teedev == optee->supp_teedev) {
2314fb0a5ebSJens Wiklander 		bool busy = true;
2324fb0a5ebSJens Wiklander 
2331647a5acSJens Wiklander 		mutex_lock(&optee->supp.mutex);
2344fb0a5ebSJens Wiklander 		if (!optee->supp.ctx) {
2354fb0a5ebSJens Wiklander 			busy = false;
2364fb0a5ebSJens Wiklander 			optee->supp.ctx = ctx;
2374fb0a5ebSJens Wiklander 		}
2381647a5acSJens Wiklander 		mutex_unlock(&optee->supp.mutex);
2394fb0a5ebSJens Wiklander 		if (busy) {
2404fb0a5ebSJens Wiklander 			kfree(ctxdata);
2414fb0a5ebSJens Wiklander 			return -EBUSY;
2424fb0a5ebSJens Wiklander 		}
2434fb0a5ebSJens Wiklander 
2445f178bb7SMaxim Uvarov 		if (!optee->scan_bus_done) {
2455f178bb7SMaxim Uvarov 			INIT_WORK(&optee->scan_bus_work, optee_bus_scan);
2465f178bb7SMaxim Uvarov 			optee->scan_bus_wq = create_workqueue("optee_bus_scan");
2475f178bb7SMaxim Uvarov 			if (!optee->scan_bus_wq) {
2485f178bb7SMaxim Uvarov 				kfree(ctxdata);
2495f178bb7SMaxim Uvarov 				return -ECHILD;
2505f178bb7SMaxim Uvarov 			}
2515f178bb7SMaxim Uvarov 			queue_work(optee->scan_bus_wq, &optee->scan_bus_work);
2525f178bb7SMaxim Uvarov 			optee->scan_bus_done = true;
2535f178bb7SMaxim Uvarov 		}
2545f178bb7SMaxim Uvarov 	}
2554fb0a5ebSJens Wiklander 	mutex_init(&ctxdata->mutex);
2564fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&ctxdata->sess_list);
2574fb0a5ebSJens Wiklander 
258ba171d3fSCedric Neveux 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
259ba171d3fSCedric Neveux 		ctx->cap_memref_null  = true;
260ba171d3fSCedric Neveux 	else
261ba171d3fSCedric Neveux 		ctx->cap_memref_null = false;
262ba171d3fSCedric Neveux 
2634fb0a5ebSJens Wiklander 	ctx->data = ctxdata;
2644fb0a5ebSJens Wiklander 	return 0;
2654fb0a5ebSJens Wiklander }
2664fb0a5ebSJens Wiklander 
2674fb0a5ebSJens Wiklander static void optee_release(struct tee_context *ctx)
2684fb0a5ebSJens Wiklander {
2694fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata = ctx->data;
2704fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2714fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2724fb0a5ebSJens Wiklander 	struct tee_shm *shm;
2734fb0a5ebSJens Wiklander 	struct optee_msg_arg *arg = NULL;
2744fb0a5ebSJens Wiklander 	phys_addr_t parg;
2754fb0a5ebSJens Wiklander 	struct optee_session *sess;
2764fb0a5ebSJens Wiklander 	struct optee_session *sess_tmp;
2774fb0a5ebSJens Wiklander 
2784fb0a5ebSJens Wiklander 	if (!ctxdata)
2794fb0a5ebSJens Wiklander 		return;
2804fb0a5ebSJens Wiklander 
281376e4199SSumit Garg 	shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg),
282376e4199SSumit Garg 			    TEE_SHM_MAPPED | TEE_SHM_PRIV);
2834fb0a5ebSJens Wiklander 	if (!IS_ERR(shm)) {
2844fb0a5ebSJens Wiklander 		arg = tee_shm_get_va(shm, 0);
2854fb0a5ebSJens Wiklander 		/*
286efb14036SJens Wiklander 		 * If va2pa fails for some reason, we can't call into
287efb14036SJens Wiklander 		 * secure world, only free the memory. Secure OS will leak
288efb14036SJens Wiklander 		 * sessions and finally refuse more sessions, but we will
289efb14036SJens Wiklander 		 * at least let normal world reclaim its memory.
2904fb0a5ebSJens Wiklander 		 */
2914fb0a5ebSJens Wiklander 		if (!IS_ERR(arg))
292efb14036SJens Wiklander 			if (tee_shm_va2pa(shm, arg, &parg))
293efb14036SJens Wiklander 				arg = NULL; /* prevent usage of parg below */
2944fb0a5ebSJens Wiklander 	}
2954fb0a5ebSJens Wiklander 
2964fb0a5ebSJens Wiklander 	list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
2974fb0a5ebSJens Wiklander 				 list_node) {
2984fb0a5ebSJens Wiklander 		list_del(&sess->list_node);
2994fb0a5ebSJens Wiklander 		if (!IS_ERR_OR_NULL(arg)) {
3004fb0a5ebSJens Wiklander 			memset(arg, 0, sizeof(*arg));
3014fb0a5ebSJens Wiklander 			arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
3024fb0a5ebSJens Wiklander 			arg->session = sess->session_id;
3034fb0a5ebSJens Wiklander 			optee_do_call_with_arg(ctx, parg);
3044fb0a5ebSJens Wiklander 		}
3054fb0a5ebSJens Wiklander 		kfree(sess);
3064fb0a5ebSJens Wiklander 	}
3074fb0a5ebSJens Wiklander 	kfree(ctxdata);
3084fb0a5ebSJens Wiklander 
3094fb0a5ebSJens Wiklander 	if (!IS_ERR(shm))
3104fb0a5ebSJens Wiklander 		tee_shm_free(shm);
3114fb0a5ebSJens Wiklander 
3124fb0a5ebSJens Wiklander 	ctx->data = NULL;
3134fb0a5ebSJens Wiklander 
3145f178bb7SMaxim Uvarov 	if (teedev == optee->supp_teedev) {
3155f178bb7SMaxim Uvarov 		if (optee->scan_bus_wq) {
3165f178bb7SMaxim Uvarov 			destroy_workqueue(optee->scan_bus_wq);
3175f178bb7SMaxim Uvarov 			optee->scan_bus_wq = NULL;
3185f178bb7SMaxim Uvarov 		}
3191647a5acSJens Wiklander 		optee_supp_release(&optee->supp);
3204fb0a5ebSJens Wiklander 	}
3215f178bb7SMaxim Uvarov }
3224fb0a5ebSJens Wiklander 
32396e72ddeSBhumika Goyal static const struct tee_driver_ops optee_ops = {
3244fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3254fb0a5ebSJens Wiklander 	.open = optee_open,
3264fb0a5ebSJens Wiklander 	.release = optee_release,
3274fb0a5ebSJens Wiklander 	.open_session = optee_open_session,
3284fb0a5ebSJens Wiklander 	.close_session = optee_close_session,
3294fb0a5ebSJens Wiklander 	.invoke_func = optee_invoke_func,
3304fb0a5ebSJens Wiklander 	.cancel_req = optee_cancel_req,
33106ca7917SVolodymyr Babchuk 	.shm_register = optee_shm_register,
33206ca7917SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister,
3334fb0a5ebSJens Wiklander };
3344fb0a5ebSJens Wiklander 
33596e72ddeSBhumika Goyal static const struct tee_desc optee_desc = {
3364fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-clnt",
3374fb0a5ebSJens Wiklander 	.ops = &optee_ops,
3384fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3394fb0a5ebSJens Wiklander };
3404fb0a5ebSJens Wiklander 
34196e72ddeSBhumika Goyal static const struct tee_driver_ops optee_supp_ops = {
3424fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3434fb0a5ebSJens Wiklander 	.open = optee_open,
3444fb0a5ebSJens Wiklander 	.release = optee_release,
3454fb0a5ebSJens Wiklander 	.supp_recv = optee_supp_recv,
3464fb0a5ebSJens Wiklander 	.supp_send = optee_supp_send,
34753a107c8SVolodymyr Babchuk 	.shm_register = optee_shm_register_supp,
34853a107c8SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister_supp,
3494fb0a5ebSJens Wiklander };
3504fb0a5ebSJens Wiklander 
35196e72ddeSBhumika Goyal static const struct tee_desc optee_supp_desc = {
3524fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-supp",
3534fb0a5ebSJens Wiklander 	.ops = &optee_supp_ops,
3544fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3554fb0a5ebSJens Wiklander 	.flags = TEE_DESC_PRIVILEGED,
3564fb0a5ebSJens Wiklander };
3574fb0a5ebSJens Wiklander 
3584fb0a5ebSJens Wiklander static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
3594fb0a5ebSJens Wiklander {
3604fb0a5ebSJens Wiklander 	struct arm_smccc_res res;
3614fb0a5ebSJens Wiklander 
3624fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
3634fb0a5ebSJens Wiklander 
3644fb0a5ebSJens Wiklander 	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
3654fb0a5ebSJens Wiklander 	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
3664fb0a5ebSJens Wiklander 		return true;
3674fb0a5ebSJens Wiklander 	return false;
3684fb0a5ebSJens Wiklander }
3694fb0a5ebSJens Wiklander 
3705c5f8030SJérôme Forissier static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
3715c5f8030SJérôme Forissier {
3725c5f8030SJérôme Forissier 	union {
3735c5f8030SJérôme Forissier 		struct arm_smccc_res smccc;
3745c5f8030SJérôme Forissier 		struct optee_smc_call_get_os_revision_result result;
3755c5f8030SJérôme Forissier 	} res = {
3765c5f8030SJérôme Forissier 		.result = {
3775c5f8030SJérôme Forissier 			.build_id = 0
3785c5f8030SJérôme Forissier 		}
3795c5f8030SJérôme Forissier 	};
3805c5f8030SJérôme Forissier 
3815c5f8030SJérôme Forissier 	invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
3825c5f8030SJérôme Forissier 		  &res.smccc);
3835c5f8030SJérôme Forissier 
3845c5f8030SJérôme Forissier 	if (res.result.build_id)
3855c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu (%08lx)", res.result.major,
3865c5f8030SJérôme Forissier 			res.result.minor, res.result.build_id);
3875c5f8030SJérôme Forissier 	else
3885c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu", res.result.major, res.result.minor);
3895c5f8030SJérôme Forissier }
3905c5f8030SJérôme Forissier 
3914fb0a5ebSJens Wiklander static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
3924fb0a5ebSJens Wiklander {
3934fb0a5ebSJens Wiklander 	union {
3944fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
3954fb0a5ebSJens Wiklander 		struct optee_smc_calls_revision_result result;
3964fb0a5ebSJens Wiklander 	} res;
3974fb0a5ebSJens Wiklander 
3984fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
3994fb0a5ebSJens Wiklander 
4004fb0a5ebSJens Wiklander 	if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
4014fb0a5ebSJens Wiklander 	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
4024fb0a5ebSJens Wiklander 		return true;
4034fb0a5ebSJens Wiklander 	return false;
4044fb0a5ebSJens Wiklander }
4054fb0a5ebSJens Wiklander 
4064fb0a5ebSJens Wiklander static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
4074fb0a5ebSJens Wiklander 					    u32 *sec_caps)
4084fb0a5ebSJens Wiklander {
4094fb0a5ebSJens Wiklander 	union {
4104fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4114fb0a5ebSJens Wiklander 		struct optee_smc_exchange_capabilities_result result;
4124fb0a5ebSJens Wiklander 	} res;
4134fb0a5ebSJens Wiklander 	u32 a1 = 0;
4144fb0a5ebSJens Wiklander 
4154fb0a5ebSJens Wiklander 	/*
4164fb0a5ebSJens Wiklander 	 * TODO This isn't enough to tell if it's UP system (from kernel
4174fb0a5ebSJens Wiklander 	 * point of view) or not, is_smp() returns the the information
4184fb0a5ebSJens Wiklander 	 * needed, but can't be called directly from here.
4194fb0a5ebSJens Wiklander 	 */
4204fb0a5ebSJens Wiklander 	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1)
4214fb0a5ebSJens Wiklander 		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
4224fb0a5ebSJens Wiklander 
4234fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0,
4244fb0a5ebSJens Wiklander 		  &res.smccc);
4254fb0a5ebSJens Wiklander 
4264fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK)
4274fb0a5ebSJens Wiklander 		return false;
4284fb0a5ebSJens Wiklander 
4294fb0a5ebSJens Wiklander 	*sec_caps = res.result.capabilities;
4304fb0a5ebSJens Wiklander 	return true;
4314fb0a5ebSJens Wiklander }
4324fb0a5ebSJens Wiklander 
4339733b072SVolodymyr Babchuk static struct tee_shm_pool *optee_config_dyn_shm(void)
4349733b072SVolodymyr Babchuk {
4359733b072SVolodymyr Babchuk 	struct tee_shm_pool_mgr *priv_mgr;
4369733b072SVolodymyr Babchuk 	struct tee_shm_pool_mgr *dmabuf_mgr;
4379733b072SVolodymyr Babchuk 	void *rc;
4389733b072SVolodymyr Babchuk 
4399733b072SVolodymyr Babchuk 	rc = optee_shm_pool_alloc_pages();
4409733b072SVolodymyr Babchuk 	if (IS_ERR(rc))
4419733b072SVolodymyr Babchuk 		return rc;
4429733b072SVolodymyr Babchuk 	priv_mgr = rc;
4439733b072SVolodymyr Babchuk 
4449733b072SVolodymyr Babchuk 	rc = optee_shm_pool_alloc_pages();
4459733b072SVolodymyr Babchuk 	if (IS_ERR(rc)) {
4469733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(priv_mgr);
4479733b072SVolodymyr Babchuk 		return rc;
4489733b072SVolodymyr Babchuk 	}
4499733b072SVolodymyr Babchuk 	dmabuf_mgr = rc;
4509733b072SVolodymyr Babchuk 
4519733b072SVolodymyr Babchuk 	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
4529733b072SVolodymyr Babchuk 	if (IS_ERR(rc)) {
4539733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(priv_mgr);
4549733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(dmabuf_mgr);
4559733b072SVolodymyr Babchuk 	}
4569733b072SVolodymyr Babchuk 
4579733b072SVolodymyr Babchuk 	return rc;
4589733b072SVolodymyr Babchuk }
4599733b072SVolodymyr Babchuk 
4604fb0a5ebSJens Wiklander static struct tee_shm_pool *
4619733b072SVolodymyr Babchuk optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
4624fb0a5ebSJens Wiklander {
4634fb0a5ebSJens Wiklander 	union {
4644fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4654fb0a5ebSJens Wiklander 		struct optee_smc_get_shm_config_result result;
4664fb0a5ebSJens Wiklander 	} res;
4674fb0a5ebSJens Wiklander 	unsigned long vaddr;
4684fb0a5ebSJens Wiklander 	phys_addr_t paddr;
4694fb0a5ebSJens Wiklander 	size_t size;
4704fb0a5ebSJens Wiklander 	phys_addr_t begin;
4714fb0a5ebSJens Wiklander 	phys_addr_t end;
4724fb0a5ebSJens Wiklander 	void *va;
473f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *priv_mgr;
474f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *dmabuf_mgr;
475f58e236cSVolodymyr Babchuk 	void *rc;
4769733b072SVolodymyr Babchuk 	const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
4774fb0a5ebSJens Wiklander 
4784fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
4794fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK) {
4809733b072SVolodymyr Babchuk 		pr_err("static shm service not available\n");
4814fb0a5ebSJens Wiklander 		return ERR_PTR(-ENOENT);
4824fb0a5ebSJens Wiklander 	}
4834fb0a5ebSJens Wiklander 
4844fb0a5ebSJens Wiklander 	if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
4854fb0a5ebSJens Wiklander 		pr_err("only normal cached shared memory supported\n");
4864fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4874fb0a5ebSJens Wiklander 	}
4884fb0a5ebSJens Wiklander 
4894fb0a5ebSJens Wiklander 	begin = roundup(res.result.start, PAGE_SIZE);
4904fb0a5ebSJens Wiklander 	end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
4914fb0a5ebSJens Wiklander 	paddr = begin;
4924fb0a5ebSJens Wiklander 	size = end - begin;
4934fb0a5ebSJens Wiklander 
4944fb0a5ebSJens Wiklander 	if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) {
4954fb0a5ebSJens Wiklander 		pr_err("too small shared memory area\n");
4964fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
4974fb0a5ebSJens Wiklander 	}
4984fb0a5ebSJens Wiklander 
4994fb0a5ebSJens Wiklander 	va = memremap(paddr, size, MEMREMAP_WB);
5004fb0a5ebSJens Wiklander 	if (!va) {
5014fb0a5ebSJens Wiklander 		pr_err("shared memory ioremap failed\n");
5024fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5034fb0a5ebSJens Wiklander 	}
5044fb0a5ebSJens Wiklander 	vaddr = (unsigned long)va;
5054fb0a5ebSJens Wiklander 
506f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
507f58e236cSVolodymyr Babchuk 					    3 /* 8 bytes aligned */);
508f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
509f58e236cSVolodymyr Babchuk 		goto err_memunmap;
510f58e236cSVolodymyr Babchuk 	priv_mgr = rc;
511f58e236cSVolodymyr Babchuk 
512f58e236cSVolodymyr Babchuk 	vaddr += sz;
513f58e236cSVolodymyr Babchuk 	paddr += sz;
514f58e236cSVolodymyr Babchuk 	size -= sz;
5154fb0a5ebSJens Wiklander 
516f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
517f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
518f58e236cSVolodymyr Babchuk 		goto err_free_priv_mgr;
519f58e236cSVolodymyr Babchuk 	dmabuf_mgr = rc;
520f58e236cSVolodymyr Babchuk 
521f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
522f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
523f58e236cSVolodymyr Babchuk 		goto err_free_dmabuf_mgr;
524f58e236cSVolodymyr Babchuk 
5254fb0a5ebSJens Wiklander 	*memremaped_shm = va;
526f58e236cSVolodymyr Babchuk 
527f58e236cSVolodymyr Babchuk 	return rc;
528f58e236cSVolodymyr Babchuk 
529f58e236cSVolodymyr Babchuk err_free_dmabuf_mgr:
530f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(dmabuf_mgr);
531f58e236cSVolodymyr Babchuk err_free_priv_mgr:
532f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(priv_mgr);
533f58e236cSVolodymyr Babchuk err_memunmap:
534f58e236cSVolodymyr Babchuk 	memunmap(va);
535f58e236cSVolodymyr Babchuk 	return rc;
5364fb0a5ebSJens Wiklander }
5374fb0a5ebSJens Wiklander 
5384fb0a5ebSJens Wiklander /* Simple wrapper functions to be able to use a function pointer */
5394fb0a5ebSJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1,
5404fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5414fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5424fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5434fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5444fb0a5ebSJens Wiklander {
5454fb0a5ebSJens Wiklander 	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5464fb0a5ebSJens Wiklander }
5474fb0a5ebSJens Wiklander 
5484fb0a5ebSJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
5494fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5504fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5514fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5524fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5534fb0a5ebSJens Wiklander {
5544fb0a5ebSJens Wiklander 	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5554fb0a5ebSJens Wiklander }
5564fb0a5ebSJens Wiklander 
557f349710eSArd Biesheuvel static optee_invoke_fn *get_invoke_func(struct device *dev)
5584fb0a5ebSJens Wiklander {
5594fb0a5ebSJens Wiklander 	const char *method;
5604fb0a5ebSJens Wiklander 
561f349710eSArd Biesheuvel 	pr_info("probing for conduit method.\n");
5624fb0a5ebSJens Wiklander 
563f349710eSArd Biesheuvel 	if (device_property_read_string(dev, "method", &method)) {
5644fb0a5ebSJens Wiklander 		pr_warn("missing \"method\" property\n");
5654fb0a5ebSJens Wiklander 		return ERR_PTR(-ENXIO);
5664fb0a5ebSJens Wiklander 	}
5674fb0a5ebSJens Wiklander 
5684fb0a5ebSJens Wiklander 	if (!strcmp("hvc", method))
5694fb0a5ebSJens Wiklander 		return optee_smccc_hvc;
5704fb0a5ebSJens Wiklander 	else if (!strcmp("smc", method))
5714fb0a5ebSJens Wiklander 		return optee_smccc_smc;
5724fb0a5ebSJens Wiklander 
5734fb0a5ebSJens Wiklander 	pr_warn("invalid \"method\" property: %s\n", method);
5744fb0a5ebSJens Wiklander 	return ERR_PTR(-EINVAL);
5754fb0a5ebSJens Wiklander }
5764fb0a5ebSJens Wiklander 
577f25889f9SAllen Pais /* optee_remove - Device Removal Routine
578f25889f9SAllen Pais  * @pdev: platform device information struct
579f25889f9SAllen Pais  *
580f25889f9SAllen Pais  * optee_remove is called by platform subsystem to alert the driver
581f25889f9SAllen Pais  * that it should release the device
582f25889f9SAllen Pais  */
583f25889f9SAllen Pais 
584f349710eSArd Biesheuvel static int optee_remove(struct platform_device *pdev)
585f349710eSArd Biesheuvel {
586f349710eSArd Biesheuvel 	struct optee *optee = platform_get_drvdata(pdev);
587f349710eSArd Biesheuvel 
588*7f565d0eSSumit Garg 	/* Unregister OP-TEE specific client devices on TEE bus */
589*7f565d0eSSumit Garg 	optee_unregister_devices();
590*7f565d0eSSumit Garg 
591f349710eSArd Biesheuvel 	/*
592f349710eSArd Biesheuvel 	 * Ask OP-TEE to free all cached shared memory objects to decrease
593f349710eSArd Biesheuvel 	 * reference counters and also avoid wild pointers in secure world
594f349710eSArd Biesheuvel 	 * into the old shared memory range.
595f349710eSArd Biesheuvel 	 */
596f349710eSArd Biesheuvel 	optee_disable_shm_cache(optee);
597f349710eSArd Biesheuvel 
598f349710eSArd Biesheuvel 	/*
599f349710eSArd Biesheuvel 	 * The two devices have to be unregistered before we can free the
600f349710eSArd Biesheuvel 	 * other resources.
601f349710eSArd Biesheuvel 	 */
602f349710eSArd Biesheuvel 	tee_device_unregister(optee->supp_teedev);
603f349710eSArd Biesheuvel 	tee_device_unregister(optee->teedev);
604f349710eSArd Biesheuvel 
605f349710eSArd Biesheuvel 	tee_shm_pool_free(optee->pool);
606f349710eSArd Biesheuvel 	if (optee->memremaped_shm)
607f349710eSArd Biesheuvel 		memunmap(optee->memremaped_shm);
608f349710eSArd Biesheuvel 	optee_wait_queue_exit(&optee->wait_queue);
609f349710eSArd Biesheuvel 	optee_supp_uninit(&optee->supp);
610f349710eSArd Biesheuvel 	mutex_destroy(&optee->call_queue.mutex);
611f349710eSArd Biesheuvel 
612f349710eSArd Biesheuvel 	kfree(optee);
613f349710eSArd Biesheuvel 
614f349710eSArd Biesheuvel 	return 0;
615f349710eSArd Biesheuvel }
616f349710eSArd Biesheuvel 
617f25889f9SAllen Pais /* optee_shutdown - Device Removal Routine
618f25889f9SAllen Pais  * @pdev: platform device information struct
619f25889f9SAllen Pais  *
620f25889f9SAllen Pais  * platform_shutdown is called by the platform subsystem to alert
621f25889f9SAllen Pais  * the driver that a shutdown, reboot, or kexec is happening and
622f25889f9SAllen Pais  * device must be disabled.
623f25889f9SAllen Pais  */
624f25889f9SAllen Pais static void optee_shutdown(struct platform_device *pdev)
625f25889f9SAllen Pais {
626f25889f9SAllen Pais 	optee_disable_shm_cache(platform_get_drvdata(pdev));
627f25889f9SAllen Pais }
628f25889f9SAllen Pais 
629f349710eSArd Biesheuvel static int optee_probe(struct platform_device *pdev)
6304fb0a5ebSJens Wiklander {
6314fb0a5ebSJens Wiklander 	optee_invoke_fn *invoke_fn;
6329733b072SVolodymyr Babchuk 	struct tee_shm_pool *pool = ERR_PTR(-EINVAL);
6334fb0a5ebSJens Wiklander 	struct optee *optee = NULL;
6344fb0a5ebSJens Wiklander 	void *memremaped_shm = NULL;
6354fb0a5ebSJens Wiklander 	struct tee_device *teedev;
6364fb0a5ebSJens Wiklander 	u32 sec_caps;
6374fb0a5ebSJens Wiklander 	int rc;
6384fb0a5ebSJens Wiklander 
639adf752afSTyler Hicks 	/*
640adf752afSTyler Hicks 	 * The kernel may have crashed at the same time that all available
641adf752afSTyler Hicks 	 * secure world threads were suspended and we cannot reschedule the
642adf752afSTyler Hicks 	 * suspended threads without access to the crashed kernel's wait_queue.
643adf752afSTyler Hicks 	 * Therefore, we cannot reliably initialize the OP-TEE driver in the
644adf752afSTyler Hicks 	 * kdump kernel.
645adf752afSTyler Hicks 	 */
646adf752afSTyler Hicks 	if (is_kdump_kernel())
647adf752afSTyler Hicks 		return -ENODEV;
648adf752afSTyler Hicks 
649f349710eSArd Biesheuvel 	invoke_fn = get_invoke_func(&pdev->dev);
6504fb0a5ebSJens Wiklander 	if (IS_ERR(invoke_fn))
651f349710eSArd Biesheuvel 		return PTR_ERR(invoke_fn);
6524fb0a5ebSJens Wiklander 
6534fb0a5ebSJens Wiklander 	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
6544fb0a5ebSJens Wiklander 		pr_warn("api uid mismatch\n");
655f349710eSArd Biesheuvel 		return -EINVAL;
6564fb0a5ebSJens Wiklander 	}
6574fb0a5ebSJens Wiklander 
6585c5f8030SJérôme Forissier 	optee_msg_get_os_revision(invoke_fn);
6595c5f8030SJérôme Forissier 
6604fb0a5ebSJens Wiklander 	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
6614fb0a5ebSJens Wiklander 		pr_warn("api revision mismatch\n");
662f349710eSArd Biesheuvel 		return -EINVAL;
6634fb0a5ebSJens Wiklander 	}
6644fb0a5ebSJens Wiklander 
6654fb0a5ebSJens Wiklander 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
6664fb0a5ebSJens Wiklander 		pr_warn("capabilities mismatch\n");
667f349710eSArd Biesheuvel 		return -EINVAL;
6684fb0a5ebSJens Wiklander 	}
6694fb0a5ebSJens Wiklander 
6704fb0a5ebSJens Wiklander 	/*
6719733b072SVolodymyr Babchuk 	 * Try to use dynamic shared memory if possible
6724fb0a5ebSJens Wiklander 	 */
6739733b072SVolodymyr Babchuk 	if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
6749733b072SVolodymyr Babchuk 		pool = optee_config_dyn_shm();
6754fb0a5ebSJens Wiklander 
6769733b072SVolodymyr Babchuk 	/*
6779733b072SVolodymyr Babchuk 	 * If dynamic shared memory is not available or failed - try static one
6789733b072SVolodymyr Babchuk 	 */
6799733b072SVolodymyr Babchuk 	if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
6809733b072SVolodymyr Babchuk 		pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
6819733b072SVolodymyr Babchuk 
6824fb0a5ebSJens Wiklander 	if (IS_ERR(pool))
683f349710eSArd Biesheuvel 		return PTR_ERR(pool);
6844fb0a5ebSJens Wiklander 
6854fb0a5ebSJens Wiklander 	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
6864fb0a5ebSJens Wiklander 	if (!optee) {
6874fb0a5ebSJens Wiklander 		rc = -ENOMEM;
6884fb0a5ebSJens Wiklander 		goto err;
6894fb0a5ebSJens Wiklander 	}
6904fb0a5ebSJens Wiklander 
6914fb0a5ebSJens Wiklander 	optee->invoke_fn = invoke_fn;
692d885cc5eSVolodymyr Babchuk 	optee->sec_caps = sec_caps;
6934fb0a5ebSJens Wiklander 
6944fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
6954fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
6964fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
6974fb0a5ebSJens Wiklander 		goto err;
6984fb0a5ebSJens Wiklander 	}
6994fb0a5ebSJens Wiklander 	optee->teedev = teedev;
7004fb0a5ebSJens Wiklander 
7014fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
7024fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
7034fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
7044fb0a5ebSJens Wiklander 		goto err;
7054fb0a5ebSJens Wiklander 	}
7064fb0a5ebSJens Wiklander 	optee->supp_teedev = teedev;
7074fb0a5ebSJens Wiklander 
7084fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->teedev);
7094fb0a5ebSJens Wiklander 	if (rc)
7104fb0a5ebSJens Wiklander 		goto err;
7114fb0a5ebSJens Wiklander 
7124fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->supp_teedev);
7134fb0a5ebSJens Wiklander 	if (rc)
7144fb0a5ebSJens Wiklander 		goto err;
7154fb0a5ebSJens Wiklander 
7164fb0a5ebSJens Wiklander 	mutex_init(&optee->call_queue.mutex);
7174fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&optee->call_queue.waiters);
7184fb0a5ebSJens Wiklander 	optee_wait_queue_init(&optee->wait_queue);
7194fb0a5ebSJens Wiklander 	optee_supp_init(&optee->supp);
7204fb0a5ebSJens Wiklander 	optee->memremaped_shm = memremaped_shm;
7214fb0a5ebSJens Wiklander 	optee->pool = pool;
7224fb0a5ebSJens Wiklander 
723b5c10dd0STyler Hicks 	/*
724b5c10dd0STyler Hicks 	 * Ensure that there are no pre-existing shm objects before enabling
725b5c10dd0STyler Hicks 	 * the shm cache so that there's no chance of receiving an invalid
726b5c10dd0STyler Hicks 	 * address during shutdown. This could occur, for example, if we're
727b5c10dd0STyler Hicks 	 * kexec booting from an older kernel that did not properly cleanup the
728b5c10dd0STyler Hicks 	 * shm cache.
729b5c10dd0STyler Hicks 	 */
730b5c10dd0STyler Hicks 	optee_disable_unmapped_shm_cache(optee);
731b5c10dd0STyler Hicks 
7324fb0a5ebSJens Wiklander 	optee_enable_shm_cache(optee);
7334fb0a5ebSJens Wiklander 
7343c15ddb9SVictor Chong 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
7353c15ddb9SVictor Chong 		pr_info("dynamic shared memory is enabled\n");
7363c15ddb9SVictor Chong 
737f349710eSArd Biesheuvel 	platform_set_drvdata(pdev, optee);
738f349710eSArd Biesheuvel 
7395f178bb7SMaxim Uvarov 	rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
740f349710eSArd Biesheuvel 	if (rc) {
741f349710eSArd Biesheuvel 		optee_remove(pdev);
742f349710eSArd Biesheuvel 		return rc;
743f349710eSArd Biesheuvel 	}
744f349710eSArd Biesheuvel 
745f349710eSArd Biesheuvel 	pr_info("initialized driver\n");
746f349710eSArd Biesheuvel 	return 0;
7474fb0a5ebSJens Wiklander err:
7484fb0a5ebSJens Wiklander 	if (optee) {
7494fb0a5ebSJens Wiklander 		/*
7504fb0a5ebSJens Wiklander 		 * tee_device_unregister() is safe to call even if the
7514fb0a5ebSJens Wiklander 		 * devices hasn't been registered with
7524fb0a5ebSJens Wiklander 		 * tee_device_register() yet.
7534fb0a5ebSJens Wiklander 		 */
7544fb0a5ebSJens Wiklander 		tee_device_unregister(optee->supp_teedev);
7554fb0a5ebSJens Wiklander 		tee_device_unregister(optee->teedev);
7564fb0a5ebSJens Wiklander 		kfree(optee);
7574fb0a5ebSJens Wiklander 	}
7584fb0a5ebSJens Wiklander 	if (pool)
7594fb0a5ebSJens Wiklander 		tee_shm_pool_free(pool);
7604fb0a5ebSJens Wiklander 	if (memremaped_shm)
7614fb0a5ebSJens Wiklander 		memunmap(memremaped_shm);
76203212e34SJens Wiklander 	return rc;
76303212e34SJens Wiklander }
76403212e34SJens Wiklander 
765f349710eSArd Biesheuvel static const struct of_device_id optee_dt_match[] = {
766f349710eSArd Biesheuvel 	{ .compatible = "linaro,optee-tz" },
767f349710eSArd Biesheuvel 	{},
768f349710eSArd Biesheuvel };
769f349710eSArd Biesheuvel MODULE_DEVICE_TABLE(of, optee_dt_match);
77003212e34SJens Wiklander 
771f349710eSArd Biesheuvel static struct platform_driver optee_driver = {
772f349710eSArd Biesheuvel 	.probe  = optee_probe,
773f349710eSArd Biesheuvel 	.remove = optee_remove,
774f25889f9SAllen Pais 	.shutdown = optee_shutdown,
775f349710eSArd Biesheuvel 	.driver = {
776f349710eSArd Biesheuvel 		.name = "optee",
777f349710eSArd Biesheuvel 		.of_match_table = optee_dt_match,
778f349710eSArd Biesheuvel 	},
779f349710eSArd Biesheuvel };
780f349710eSArd Biesheuvel module_platform_driver(optee_driver);
7814fb0a5ebSJens Wiklander 
7824fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro");
7834fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver");
7844fb0a5ebSJens Wiklander MODULE_VERSION("1.0");
7854fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2");
786f349710eSArd Biesheuvel MODULE_ALIAS("platform:optee");
787