xref: /openbmc/linux/drivers/tee/optee/core.c (revision 4602c584)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24fb0a5ebSJens Wiklander /*
3*4602c584SJens Wiklander  * Copyright (c) 2015-2021, 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 
30*4602c584SJens Wiklander static void from_msg_param_value(struct tee_param *p, u32 attr,
31*4602c584SJens Wiklander 				 const struct optee_msg_param *mp)
32*4602c584SJens Wiklander {
33*4602c584SJens Wiklander 	p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
34*4602c584SJens Wiklander 		  attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
35*4602c584SJens Wiklander 	p->u.value.a = mp->u.value.a;
36*4602c584SJens Wiklander 	p->u.value.b = mp->u.value.b;
37*4602c584SJens Wiklander 	p->u.value.c = mp->u.value.c;
38*4602c584SJens Wiklander }
39*4602c584SJens Wiklander 
40*4602c584SJens Wiklander static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr,
41*4602c584SJens Wiklander 				  const struct optee_msg_param *mp)
42*4602c584SJens Wiklander {
43*4602c584SJens Wiklander 	struct tee_shm *shm;
44*4602c584SJens Wiklander 	phys_addr_t pa;
45*4602c584SJens Wiklander 	int rc;
46*4602c584SJens Wiklander 
47*4602c584SJens Wiklander 	p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
48*4602c584SJens Wiklander 		  attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
49*4602c584SJens Wiklander 	p->u.memref.size = mp->u.tmem.size;
50*4602c584SJens Wiklander 	shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref;
51*4602c584SJens Wiklander 	if (!shm) {
52*4602c584SJens Wiklander 		p->u.memref.shm_offs = 0;
53*4602c584SJens Wiklander 		p->u.memref.shm = NULL;
54*4602c584SJens Wiklander 		return 0;
55*4602c584SJens Wiklander 	}
56*4602c584SJens Wiklander 
57*4602c584SJens Wiklander 	rc = tee_shm_get_pa(shm, 0, &pa);
58*4602c584SJens Wiklander 	if (rc)
59*4602c584SJens Wiklander 		return rc;
60*4602c584SJens Wiklander 
61*4602c584SJens Wiklander 	p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa;
62*4602c584SJens Wiklander 	p->u.memref.shm = shm;
63*4602c584SJens Wiklander 
64*4602c584SJens Wiklander 	/* Check that the memref is covered by the shm object */
65*4602c584SJens Wiklander 	if (p->u.memref.size) {
66*4602c584SJens Wiklander 		size_t o = p->u.memref.shm_offs +
67*4602c584SJens Wiklander 			   p->u.memref.size - 1;
68*4602c584SJens Wiklander 
69*4602c584SJens Wiklander 		rc = tee_shm_get_pa(shm, o, NULL);
70*4602c584SJens Wiklander 		if (rc)
71*4602c584SJens Wiklander 			return rc;
72*4602c584SJens Wiklander 	}
73*4602c584SJens Wiklander 
74*4602c584SJens Wiklander 	return 0;
75*4602c584SJens Wiklander }
76*4602c584SJens Wiklander 
77*4602c584SJens Wiklander static void from_msg_param_reg_mem(struct tee_param *p, u32 attr,
78*4602c584SJens Wiklander 				   const struct optee_msg_param *mp)
79*4602c584SJens Wiklander {
80*4602c584SJens Wiklander 	struct tee_shm *shm;
81*4602c584SJens Wiklander 
82*4602c584SJens Wiklander 	p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
83*4602c584SJens Wiklander 		  attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
84*4602c584SJens Wiklander 	p->u.memref.size = mp->u.rmem.size;
85*4602c584SJens Wiklander 	shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref;
86*4602c584SJens Wiklander 
87*4602c584SJens Wiklander 	if (shm) {
88*4602c584SJens Wiklander 		p->u.memref.shm_offs = mp->u.rmem.offs;
89*4602c584SJens Wiklander 		p->u.memref.shm = shm;
90*4602c584SJens Wiklander 	} else {
91*4602c584SJens Wiklander 		p->u.memref.shm_offs = 0;
92*4602c584SJens Wiklander 		p->u.memref.shm = NULL;
93*4602c584SJens Wiklander 	}
94*4602c584SJens Wiklander }
95*4602c584SJens Wiklander 
964fb0a5ebSJens Wiklander /**
974fb0a5ebSJens Wiklander  * optee_from_msg_param() - convert from OPTEE_MSG parameters to
984fb0a5ebSJens Wiklander  *			    struct tee_param
99*4602c584SJens Wiklander  * @optee:	main service struct
1004fb0a5ebSJens Wiklander  * @params:	subsystem internal parameter representation
1014fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
1024fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
1034fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
1044fb0a5ebSJens Wiklander  */
105*4602c584SJens Wiklander static int optee_from_msg_param(struct optee *optee, struct tee_param *params,
106*4602c584SJens Wiklander 				size_t num_params,
1074fb0a5ebSJens Wiklander 				const struct optee_msg_param *msg_params)
1084fb0a5ebSJens Wiklander {
1094fb0a5ebSJens Wiklander 	int rc;
1104fb0a5ebSJens Wiklander 	size_t n;
1114fb0a5ebSJens Wiklander 
1124fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
1134fb0a5ebSJens Wiklander 		struct tee_param *p = params + n;
1144fb0a5ebSJens Wiklander 		const struct optee_msg_param *mp = msg_params + n;
1154fb0a5ebSJens Wiklander 		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
1164fb0a5ebSJens Wiklander 
1174fb0a5ebSJens Wiklander 		switch (attr) {
1184fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_NONE:
1194fb0a5ebSJens Wiklander 			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
1204fb0a5ebSJens Wiklander 			memset(&p->u, 0, sizeof(p->u));
1214fb0a5ebSJens Wiklander 			break;
1224fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
1234fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
1244fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
125*4602c584SJens Wiklander 			from_msg_param_value(p, attr, mp);
1264fb0a5ebSJens Wiklander 			break;
1274fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
1284fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
1294fb0a5ebSJens Wiklander 		case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
130*4602c584SJens Wiklander 			rc = from_msg_param_tmp_mem(p, attr, mp);
1314fb0a5ebSJens Wiklander 			if (rc)
1324fb0a5ebSJens Wiklander 				return rc;
1334fb0a5ebSJens Wiklander 			break;
13464cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
13564cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
13664cf9d8aSVolodymyr Babchuk 		case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
137*4602c584SJens Wiklander 			from_msg_param_reg_mem(p, attr, mp);
13864cf9d8aSVolodymyr Babchuk 			break;
13964cf9d8aSVolodymyr Babchuk 
1404fb0a5ebSJens Wiklander 		default:
1414fb0a5ebSJens Wiklander 			return -EINVAL;
1424fb0a5ebSJens Wiklander 		}
1434fb0a5ebSJens Wiklander 	}
1444fb0a5ebSJens Wiklander 	return 0;
1454fb0a5ebSJens Wiklander }
1464fb0a5ebSJens Wiklander 
147*4602c584SJens Wiklander static void to_msg_param_value(struct optee_msg_param *mp,
148*4602c584SJens Wiklander 			       const struct tee_param *p)
149*4602c584SJens Wiklander {
150*4602c584SJens Wiklander 	mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
151*4602c584SJens Wiklander 		   TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
152*4602c584SJens Wiklander 	mp->u.value.a = p->u.value.a;
153*4602c584SJens Wiklander 	mp->u.value.b = p->u.value.b;
154*4602c584SJens Wiklander 	mp->u.value.c = p->u.value.c;
155*4602c584SJens Wiklander }
156*4602c584SJens Wiklander 
15764cf9d8aSVolodymyr Babchuk static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
15864cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
15964cf9d8aSVolodymyr Babchuk {
16064cf9d8aSVolodymyr Babchuk 	int rc;
16164cf9d8aSVolodymyr Babchuk 	phys_addr_t pa;
16264cf9d8aSVolodymyr Babchuk 
16364cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
16464cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
16564cf9d8aSVolodymyr Babchuk 
16664cf9d8aSVolodymyr Babchuk 	mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
16764cf9d8aSVolodymyr Babchuk 	mp->u.tmem.size = p->u.memref.size;
16864cf9d8aSVolodymyr Babchuk 
16964cf9d8aSVolodymyr Babchuk 	if (!p->u.memref.shm) {
17064cf9d8aSVolodymyr Babchuk 		mp->u.tmem.buf_ptr = 0;
17164cf9d8aSVolodymyr Babchuk 		return 0;
17264cf9d8aSVolodymyr Babchuk 	}
17364cf9d8aSVolodymyr Babchuk 
17464cf9d8aSVolodymyr Babchuk 	rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
17564cf9d8aSVolodymyr Babchuk 	if (rc)
17664cf9d8aSVolodymyr Babchuk 		return rc;
17764cf9d8aSVolodymyr Babchuk 
17864cf9d8aSVolodymyr Babchuk 	mp->u.tmem.buf_ptr = pa;
17964cf9d8aSVolodymyr Babchuk 	mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
18064cf9d8aSVolodymyr Babchuk 		    OPTEE_MSG_ATTR_CACHE_SHIFT;
18164cf9d8aSVolodymyr Babchuk 
18264cf9d8aSVolodymyr Babchuk 	return 0;
18364cf9d8aSVolodymyr Babchuk }
18464cf9d8aSVolodymyr Babchuk 
18564cf9d8aSVolodymyr Babchuk static int to_msg_param_reg_mem(struct optee_msg_param *mp,
18664cf9d8aSVolodymyr Babchuk 				const struct tee_param *p)
18764cf9d8aSVolodymyr Babchuk {
18864cf9d8aSVolodymyr Babchuk 	mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
18964cf9d8aSVolodymyr Babchuk 		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
19064cf9d8aSVolodymyr Babchuk 
19164cf9d8aSVolodymyr Babchuk 	mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
19264cf9d8aSVolodymyr Babchuk 	mp->u.rmem.size = p->u.memref.size;
19364cf9d8aSVolodymyr Babchuk 	mp->u.rmem.offs = p->u.memref.shm_offs;
19464cf9d8aSVolodymyr Babchuk 	return 0;
19564cf9d8aSVolodymyr Babchuk }
19664cf9d8aSVolodymyr Babchuk 
1974fb0a5ebSJens Wiklander /**
1984fb0a5ebSJens Wiklander  * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
199*4602c584SJens Wiklander  * @optee:	main service struct
2004fb0a5ebSJens Wiklander  * @msg_params:	OPTEE_MSG parameters
2014fb0a5ebSJens Wiklander  * @num_params:	number of elements in the parameter arrays
2024fb0a5ebSJens Wiklander  * @params:	subsystem itnernal parameter representation
2034fb0a5ebSJens Wiklander  * Returns 0 on success or <0 on failure
2044fb0a5ebSJens Wiklander  */
205*4602c584SJens Wiklander static int optee_to_msg_param(struct optee *optee,
206*4602c584SJens Wiklander 			      struct optee_msg_param *msg_params,
207*4602c584SJens Wiklander 			      size_t num_params, const struct tee_param *params)
2084fb0a5ebSJens Wiklander {
2094fb0a5ebSJens Wiklander 	int rc;
2104fb0a5ebSJens Wiklander 	size_t n;
2114fb0a5ebSJens Wiklander 
2124fb0a5ebSJens Wiklander 	for (n = 0; n < num_params; n++) {
2134fb0a5ebSJens Wiklander 		const struct tee_param *p = params + n;
2144fb0a5ebSJens Wiklander 		struct optee_msg_param *mp = msg_params + n;
2154fb0a5ebSJens Wiklander 
2164fb0a5ebSJens Wiklander 		switch (p->attr) {
2174fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
2184fb0a5ebSJens Wiklander 			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
2194fb0a5ebSJens Wiklander 			memset(&mp->u, 0, sizeof(mp->u));
2204fb0a5ebSJens Wiklander 			break;
2214fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
2224fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
2234fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
224*4602c584SJens Wiklander 			to_msg_param_value(mp, p);
2254fb0a5ebSJens Wiklander 			break;
2264fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
2274fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
2284fb0a5ebSJens Wiklander 		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
22964cf9d8aSVolodymyr Babchuk 			if (tee_shm_is_registered(p->u.memref.shm))
23064cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_reg_mem(mp, p);
23164cf9d8aSVolodymyr Babchuk 			else
23264cf9d8aSVolodymyr Babchuk 				rc = to_msg_param_tmp_mem(mp, p);
2334fb0a5ebSJens Wiklander 			if (rc)
2344fb0a5ebSJens Wiklander 				return rc;
2354fb0a5ebSJens Wiklander 			break;
2364fb0a5ebSJens Wiklander 		default:
2374fb0a5ebSJens Wiklander 			return -EINVAL;
2384fb0a5ebSJens Wiklander 		}
2394fb0a5ebSJens Wiklander 	}
2404fb0a5ebSJens Wiklander 	return 0;
2414fb0a5ebSJens Wiklander }
2424fb0a5ebSJens Wiklander 
2434fb0a5ebSJens Wiklander static void optee_get_version(struct tee_device *teedev,
2444fb0a5ebSJens Wiklander 			      struct tee_ioctl_version_data *vers)
2454fb0a5ebSJens Wiklander {
2464fb0a5ebSJens Wiklander 	struct tee_ioctl_version_data v = {
2474fb0a5ebSJens Wiklander 		.impl_id = TEE_IMPL_ID_OPTEE,
2484fb0a5ebSJens Wiklander 		.impl_caps = TEE_OPTEE_CAP_TZ,
2494fb0a5ebSJens Wiklander 		.gen_caps = TEE_GEN_CAP_GP,
2504fb0a5ebSJens Wiklander 	};
251f58e236cSVolodymyr Babchuk 	struct optee *optee = tee_get_drvdata(teedev);
252f58e236cSVolodymyr Babchuk 
253f58e236cSVolodymyr Babchuk 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
254f58e236cSVolodymyr Babchuk 		v.gen_caps |= TEE_GEN_CAP_REG_MEM;
255ba171d3fSCedric Neveux 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
256ba171d3fSCedric Neveux 		v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL;
2574fb0a5ebSJens Wiklander 	*vers = v;
2584fb0a5ebSJens Wiklander }
2594fb0a5ebSJens Wiklander 
2605f178bb7SMaxim Uvarov static void optee_bus_scan(struct work_struct *work)
2615f178bb7SMaxim Uvarov {
2625f178bb7SMaxim Uvarov 	WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
2635f178bb7SMaxim Uvarov }
2645f178bb7SMaxim Uvarov 
2654fb0a5ebSJens Wiklander static int optee_open(struct tee_context *ctx)
2664fb0a5ebSJens Wiklander {
2674fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata;
2684fb0a5ebSJens Wiklander 	struct tee_device *teedev = ctx->teedev;
2694fb0a5ebSJens Wiklander 	struct optee *optee = tee_get_drvdata(teedev);
2704fb0a5ebSJens Wiklander 
2714fb0a5ebSJens Wiklander 	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
2724fb0a5ebSJens Wiklander 	if (!ctxdata)
2734fb0a5ebSJens Wiklander 		return -ENOMEM;
2744fb0a5ebSJens Wiklander 
2754fb0a5ebSJens Wiklander 	if (teedev == optee->supp_teedev) {
2764fb0a5ebSJens Wiklander 		bool busy = true;
2774fb0a5ebSJens Wiklander 
2781647a5acSJens Wiklander 		mutex_lock(&optee->supp.mutex);
2794fb0a5ebSJens Wiklander 		if (!optee->supp.ctx) {
2804fb0a5ebSJens Wiklander 			busy = false;
2814fb0a5ebSJens Wiklander 			optee->supp.ctx = ctx;
2824fb0a5ebSJens Wiklander 		}
2831647a5acSJens Wiklander 		mutex_unlock(&optee->supp.mutex);
2844fb0a5ebSJens Wiklander 		if (busy) {
2854fb0a5ebSJens Wiklander 			kfree(ctxdata);
2864fb0a5ebSJens Wiklander 			return -EBUSY;
2874fb0a5ebSJens Wiklander 		}
2884fb0a5ebSJens Wiklander 
2895f178bb7SMaxim Uvarov 		if (!optee->scan_bus_done) {
2905f178bb7SMaxim Uvarov 			INIT_WORK(&optee->scan_bus_work, optee_bus_scan);
2915f178bb7SMaxim Uvarov 			optee->scan_bus_wq = create_workqueue("optee_bus_scan");
2925f178bb7SMaxim Uvarov 			if (!optee->scan_bus_wq) {
2935f178bb7SMaxim Uvarov 				kfree(ctxdata);
2945f178bb7SMaxim Uvarov 				return -ECHILD;
2955f178bb7SMaxim Uvarov 			}
2965f178bb7SMaxim Uvarov 			queue_work(optee->scan_bus_wq, &optee->scan_bus_work);
2975f178bb7SMaxim Uvarov 			optee->scan_bus_done = true;
2985f178bb7SMaxim Uvarov 		}
2995f178bb7SMaxim Uvarov 	}
3004fb0a5ebSJens Wiklander 	mutex_init(&ctxdata->mutex);
3014fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&ctxdata->sess_list);
3024fb0a5ebSJens Wiklander 
303ba171d3fSCedric Neveux 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL)
304ba171d3fSCedric Neveux 		ctx->cap_memref_null  = true;
305ba171d3fSCedric Neveux 	else
306ba171d3fSCedric Neveux 		ctx->cap_memref_null = false;
307ba171d3fSCedric Neveux 
3084fb0a5ebSJens Wiklander 	ctx->data = ctxdata;
3094fb0a5ebSJens Wiklander 	return 0;
3104fb0a5ebSJens Wiklander }
3114fb0a5ebSJens Wiklander 
312c0ab6db3SJens Wiklander static void optee_release_helper(struct tee_context *ctx,
313c0ab6db3SJens Wiklander 				 int (*close_session)(struct tee_context *ctx,
314c0ab6db3SJens Wiklander 						      u32 session))
3154fb0a5ebSJens Wiklander {
3164fb0a5ebSJens Wiklander 	struct optee_context_data *ctxdata = ctx->data;
3174fb0a5ebSJens Wiklander 	struct optee_session *sess;
3184fb0a5ebSJens Wiklander 	struct optee_session *sess_tmp;
3194fb0a5ebSJens Wiklander 
3204fb0a5ebSJens Wiklander 	if (!ctxdata)
3214fb0a5ebSJens Wiklander 		return;
3224fb0a5ebSJens Wiklander 
3234fb0a5ebSJens Wiklander 	list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
3244fb0a5ebSJens Wiklander 				 list_node) {
3254fb0a5ebSJens Wiklander 		list_del(&sess->list_node);
326c0ab6db3SJens Wiklander 		close_session(ctx, sess->session_id);
3274fb0a5ebSJens Wiklander 		kfree(sess);
3284fb0a5ebSJens Wiklander 	}
3294fb0a5ebSJens Wiklander 	kfree(ctxdata);
3304fb0a5ebSJens Wiklander 	ctx->data = NULL;
331c0ab6db3SJens Wiklander }
3324fb0a5ebSJens Wiklander 
333c0ab6db3SJens Wiklander static void optee_release(struct tee_context *ctx)
334c0ab6db3SJens Wiklander {
335c0ab6db3SJens Wiklander 	optee_release_helper(ctx, optee_close_session_helper);
336c0ab6db3SJens Wiklander }
337c0ab6db3SJens Wiklander 
338c0ab6db3SJens Wiklander static void optee_release_supp(struct tee_context *ctx)
339c0ab6db3SJens Wiklander {
340c0ab6db3SJens Wiklander 	struct optee *optee = tee_get_drvdata(ctx->teedev);
341c0ab6db3SJens Wiklander 
342c0ab6db3SJens Wiklander 	optee_release_helper(ctx, optee_close_session_helper);
3435f178bb7SMaxim Uvarov 	if (optee->scan_bus_wq) {
3445f178bb7SMaxim Uvarov 		destroy_workqueue(optee->scan_bus_wq);
3455f178bb7SMaxim Uvarov 		optee->scan_bus_wq = NULL;
3465f178bb7SMaxim Uvarov 	}
3471647a5acSJens Wiklander 	optee_supp_release(&optee->supp);
3484fb0a5ebSJens Wiklander }
3494fb0a5ebSJens Wiklander 
350*4602c584SJens Wiklander static const struct tee_driver_ops optee_clnt_ops = {
3514fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3524fb0a5ebSJens Wiklander 	.open = optee_open,
3534fb0a5ebSJens Wiklander 	.release = optee_release,
3544fb0a5ebSJens Wiklander 	.open_session = optee_open_session,
3554fb0a5ebSJens Wiklander 	.close_session = optee_close_session,
3564fb0a5ebSJens Wiklander 	.invoke_func = optee_invoke_func,
3574fb0a5ebSJens Wiklander 	.cancel_req = optee_cancel_req,
35806ca7917SVolodymyr Babchuk 	.shm_register = optee_shm_register,
35906ca7917SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister,
3604fb0a5ebSJens Wiklander };
3614fb0a5ebSJens Wiklander 
362*4602c584SJens Wiklander static const struct tee_desc optee_clnt_desc = {
3634fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-clnt",
364*4602c584SJens Wiklander 	.ops = &optee_clnt_ops,
3654fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3664fb0a5ebSJens Wiklander };
3674fb0a5ebSJens Wiklander 
36896e72ddeSBhumika Goyal static const struct tee_driver_ops optee_supp_ops = {
3694fb0a5ebSJens Wiklander 	.get_version = optee_get_version,
3704fb0a5ebSJens Wiklander 	.open = optee_open,
371c0ab6db3SJens Wiklander 	.release = optee_release_supp,
3724fb0a5ebSJens Wiklander 	.supp_recv = optee_supp_recv,
3734fb0a5ebSJens Wiklander 	.supp_send = optee_supp_send,
37453a107c8SVolodymyr Babchuk 	.shm_register = optee_shm_register_supp,
37553a107c8SVolodymyr Babchuk 	.shm_unregister = optee_shm_unregister_supp,
3764fb0a5ebSJens Wiklander };
3774fb0a5ebSJens Wiklander 
37896e72ddeSBhumika Goyal static const struct tee_desc optee_supp_desc = {
3794fb0a5ebSJens Wiklander 	.name = DRIVER_NAME "-supp",
3804fb0a5ebSJens Wiklander 	.ops = &optee_supp_ops,
3814fb0a5ebSJens Wiklander 	.owner = THIS_MODULE,
3824fb0a5ebSJens Wiklander 	.flags = TEE_DESC_PRIVILEGED,
3834fb0a5ebSJens Wiklander };
3844fb0a5ebSJens Wiklander 
385*4602c584SJens Wiklander static const struct optee_ops optee_ops = {
386*4602c584SJens Wiklander 	.do_call_with_arg = optee_do_call_with_arg,
387*4602c584SJens Wiklander 	.to_msg_param = optee_to_msg_param,
388*4602c584SJens Wiklander 	.from_msg_param = optee_from_msg_param,
389*4602c584SJens Wiklander };
390*4602c584SJens Wiklander 
3914fb0a5ebSJens Wiklander static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
3924fb0a5ebSJens Wiklander {
3934fb0a5ebSJens Wiklander 	struct arm_smccc_res res;
3944fb0a5ebSJens Wiklander 
3954fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
3964fb0a5ebSJens Wiklander 
3974fb0a5ebSJens Wiklander 	if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
3984fb0a5ebSJens Wiklander 	    res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3)
3994fb0a5ebSJens Wiklander 		return true;
4004fb0a5ebSJens Wiklander 	return false;
4014fb0a5ebSJens Wiklander }
4024fb0a5ebSJens Wiklander 
4035c5f8030SJérôme Forissier static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
4045c5f8030SJérôme Forissier {
4055c5f8030SJérôme Forissier 	union {
4065c5f8030SJérôme Forissier 		struct arm_smccc_res smccc;
4075c5f8030SJérôme Forissier 		struct optee_smc_call_get_os_revision_result result;
4085c5f8030SJérôme Forissier 	} res = {
4095c5f8030SJérôme Forissier 		.result = {
4105c5f8030SJérôme Forissier 			.build_id = 0
4115c5f8030SJérôme Forissier 		}
4125c5f8030SJérôme Forissier 	};
4135c5f8030SJérôme Forissier 
4145c5f8030SJérôme Forissier 	invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
4155c5f8030SJérôme Forissier 		  &res.smccc);
4165c5f8030SJérôme Forissier 
4175c5f8030SJérôme Forissier 	if (res.result.build_id)
4185c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu (%08lx)", res.result.major,
4195c5f8030SJérôme Forissier 			res.result.minor, res.result.build_id);
4205c5f8030SJérôme Forissier 	else
4215c5f8030SJérôme Forissier 		pr_info("revision %lu.%lu", res.result.major, res.result.minor);
4225c5f8030SJérôme Forissier }
4235c5f8030SJérôme Forissier 
4244fb0a5ebSJens Wiklander static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
4254fb0a5ebSJens Wiklander {
4264fb0a5ebSJens Wiklander 	union {
4274fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4284fb0a5ebSJens Wiklander 		struct optee_smc_calls_revision_result result;
4294fb0a5ebSJens Wiklander 	} res;
4304fb0a5ebSJens Wiklander 
4314fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
4324fb0a5ebSJens Wiklander 
4334fb0a5ebSJens Wiklander 	if (res.result.major == OPTEE_MSG_REVISION_MAJOR &&
4344fb0a5ebSJens Wiklander 	    (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR)
4354fb0a5ebSJens Wiklander 		return true;
4364fb0a5ebSJens Wiklander 	return false;
4374fb0a5ebSJens Wiklander }
4384fb0a5ebSJens Wiklander 
4394fb0a5ebSJens Wiklander static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
4404fb0a5ebSJens Wiklander 					    u32 *sec_caps)
4414fb0a5ebSJens Wiklander {
4424fb0a5ebSJens Wiklander 	union {
4434fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4444fb0a5ebSJens Wiklander 		struct optee_smc_exchange_capabilities_result result;
4454fb0a5ebSJens Wiklander 	} res;
4464fb0a5ebSJens Wiklander 	u32 a1 = 0;
4474fb0a5ebSJens Wiklander 
4484fb0a5ebSJens Wiklander 	/*
4494fb0a5ebSJens Wiklander 	 * TODO This isn't enough to tell if it's UP system (from kernel
4504fb0a5ebSJens Wiklander 	 * point of view) or not, is_smp() returns the the information
4514fb0a5ebSJens Wiklander 	 * needed, but can't be called directly from here.
4524fb0a5ebSJens Wiklander 	 */
4534fb0a5ebSJens Wiklander 	if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1)
4544fb0a5ebSJens Wiklander 		a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
4554fb0a5ebSJens Wiklander 
4564fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0,
4574fb0a5ebSJens Wiklander 		  &res.smccc);
4584fb0a5ebSJens Wiklander 
4594fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK)
4604fb0a5ebSJens Wiklander 		return false;
4614fb0a5ebSJens Wiklander 
4624fb0a5ebSJens Wiklander 	*sec_caps = res.result.capabilities;
4634fb0a5ebSJens Wiklander 	return true;
4644fb0a5ebSJens Wiklander }
4654fb0a5ebSJens Wiklander 
4669733b072SVolodymyr Babchuk static struct tee_shm_pool *optee_config_dyn_shm(void)
4679733b072SVolodymyr Babchuk {
4689733b072SVolodymyr Babchuk 	struct tee_shm_pool_mgr *priv_mgr;
4699733b072SVolodymyr Babchuk 	struct tee_shm_pool_mgr *dmabuf_mgr;
4709733b072SVolodymyr Babchuk 	void *rc;
4719733b072SVolodymyr Babchuk 
4729733b072SVolodymyr Babchuk 	rc = optee_shm_pool_alloc_pages();
4739733b072SVolodymyr Babchuk 	if (IS_ERR(rc))
4749733b072SVolodymyr Babchuk 		return rc;
4759733b072SVolodymyr Babchuk 	priv_mgr = rc;
4769733b072SVolodymyr Babchuk 
4779733b072SVolodymyr Babchuk 	rc = optee_shm_pool_alloc_pages();
4789733b072SVolodymyr Babchuk 	if (IS_ERR(rc)) {
4799733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(priv_mgr);
4809733b072SVolodymyr Babchuk 		return rc;
4819733b072SVolodymyr Babchuk 	}
4829733b072SVolodymyr Babchuk 	dmabuf_mgr = rc;
4839733b072SVolodymyr Babchuk 
4849733b072SVolodymyr Babchuk 	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
4859733b072SVolodymyr Babchuk 	if (IS_ERR(rc)) {
4869733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(priv_mgr);
4879733b072SVolodymyr Babchuk 		tee_shm_pool_mgr_destroy(dmabuf_mgr);
4889733b072SVolodymyr Babchuk 	}
4899733b072SVolodymyr Babchuk 
4909733b072SVolodymyr Babchuk 	return rc;
4919733b072SVolodymyr Babchuk }
4929733b072SVolodymyr Babchuk 
4934fb0a5ebSJens Wiklander static struct tee_shm_pool *
4949733b072SVolodymyr Babchuk optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
4954fb0a5ebSJens Wiklander {
4964fb0a5ebSJens Wiklander 	union {
4974fb0a5ebSJens Wiklander 		struct arm_smccc_res smccc;
4984fb0a5ebSJens Wiklander 		struct optee_smc_get_shm_config_result result;
4994fb0a5ebSJens Wiklander 	} res;
5004fb0a5ebSJens Wiklander 	unsigned long vaddr;
5014fb0a5ebSJens Wiklander 	phys_addr_t paddr;
5024fb0a5ebSJens Wiklander 	size_t size;
5034fb0a5ebSJens Wiklander 	phys_addr_t begin;
5044fb0a5ebSJens Wiklander 	phys_addr_t end;
5054fb0a5ebSJens Wiklander 	void *va;
506f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *priv_mgr;
507f58e236cSVolodymyr Babchuk 	struct tee_shm_pool_mgr *dmabuf_mgr;
508f58e236cSVolodymyr Babchuk 	void *rc;
5099733b072SVolodymyr Babchuk 	const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
5104fb0a5ebSJens Wiklander 
5114fb0a5ebSJens Wiklander 	invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
5124fb0a5ebSJens Wiklander 	if (res.result.status != OPTEE_SMC_RETURN_OK) {
5139733b072SVolodymyr Babchuk 		pr_err("static shm service not available\n");
5144fb0a5ebSJens Wiklander 		return ERR_PTR(-ENOENT);
5154fb0a5ebSJens Wiklander 	}
5164fb0a5ebSJens Wiklander 
5174fb0a5ebSJens Wiklander 	if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
5184fb0a5ebSJens Wiklander 		pr_err("only normal cached shared memory supported\n");
5194fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5204fb0a5ebSJens Wiklander 	}
5214fb0a5ebSJens Wiklander 
5224fb0a5ebSJens Wiklander 	begin = roundup(res.result.start, PAGE_SIZE);
5234fb0a5ebSJens Wiklander 	end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
5244fb0a5ebSJens Wiklander 	paddr = begin;
5254fb0a5ebSJens Wiklander 	size = end - begin;
5264fb0a5ebSJens Wiklander 
5274fb0a5ebSJens Wiklander 	if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) {
5284fb0a5ebSJens Wiklander 		pr_err("too small shared memory area\n");
5294fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5304fb0a5ebSJens Wiklander 	}
5314fb0a5ebSJens Wiklander 
5324fb0a5ebSJens Wiklander 	va = memremap(paddr, size, MEMREMAP_WB);
5334fb0a5ebSJens Wiklander 	if (!va) {
5344fb0a5ebSJens Wiklander 		pr_err("shared memory ioremap failed\n");
5354fb0a5ebSJens Wiklander 		return ERR_PTR(-EINVAL);
5364fb0a5ebSJens Wiklander 	}
5374fb0a5ebSJens Wiklander 	vaddr = (unsigned long)va;
5384fb0a5ebSJens Wiklander 
539f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
540f58e236cSVolodymyr Babchuk 					    3 /* 8 bytes aligned */);
541f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
542f58e236cSVolodymyr Babchuk 		goto err_memunmap;
543f58e236cSVolodymyr Babchuk 	priv_mgr = rc;
544f58e236cSVolodymyr Babchuk 
545f58e236cSVolodymyr Babchuk 	vaddr += sz;
546f58e236cSVolodymyr Babchuk 	paddr += sz;
547f58e236cSVolodymyr Babchuk 	size -= sz;
5484fb0a5ebSJens Wiklander 
549f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
550f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
551f58e236cSVolodymyr Babchuk 		goto err_free_priv_mgr;
552f58e236cSVolodymyr Babchuk 	dmabuf_mgr = rc;
553f58e236cSVolodymyr Babchuk 
554f58e236cSVolodymyr Babchuk 	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
555f58e236cSVolodymyr Babchuk 	if (IS_ERR(rc))
556f58e236cSVolodymyr Babchuk 		goto err_free_dmabuf_mgr;
557f58e236cSVolodymyr Babchuk 
5584fb0a5ebSJens Wiklander 	*memremaped_shm = va;
559f58e236cSVolodymyr Babchuk 
560f58e236cSVolodymyr Babchuk 	return rc;
561f58e236cSVolodymyr Babchuk 
562f58e236cSVolodymyr Babchuk err_free_dmabuf_mgr:
563f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(dmabuf_mgr);
564f58e236cSVolodymyr Babchuk err_free_priv_mgr:
565f58e236cSVolodymyr Babchuk 	tee_shm_pool_mgr_destroy(priv_mgr);
566f58e236cSVolodymyr Babchuk err_memunmap:
567f58e236cSVolodymyr Babchuk 	memunmap(va);
568f58e236cSVolodymyr Babchuk 	return rc;
5694fb0a5ebSJens Wiklander }
5704fb0a5ebSJens Wiklander 
5714fb0a5ebSJens Wiklander /* Simple wrapper functions to be able to use a function pointer */
5724fb0a5ebSJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1,
5734fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5744fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5754fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5764fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5774fb0a5ebSJens Wiklander {
5784fb0a5ebSJens Wiklander 	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5794fb0a5ebSJens Wiklander }
5804fb0a5ebSJens Wiklander 
5814fb0a5ebSJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
5824fb0a5ebSJens Wiklander 			    unsigned long a2, unsigned long a3,
5834fb0a5ebSJens Wiklander 			    unsigned long a4, unsigned long a5,
5844fb0a5ebSJens Wiklander 			    unsigned long a6, unsigned long a7,
5854fb0a5ebSJens Wiklander 			    struct arm_smccc_res *res)
5864fb0a5ebSJens Wiklander {
5874fb0a5ebSJens Wiklander 	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
5884fb0a5ebSJens Wiklander }
5894fb0a5ebSJens Wiklander 
590f349710eSArd Biesheuvel static optee_invoke_fn *get_invoke_func(struct device *dev)
5914fb0a5ebSJens Wiklander {
5924fb0a5ebSJens Wiklander 	const char *method;
5934fb0a5ebSJens Wiklander 
594f349710eSArd Biesheuvel 	pr_info("probing for conduit method.\n");
5954fb0a5ebSJens Wiklander 
596f349710eSArd Biesheuvel 	if (device_property_read_string(dev, "method", &method)) {
5974fb0a5ebSJens Wiklander 		pr_warn("missing \"method\" property\n");
5984fb0a5ebSJens Wiklander 		return ERR_PTR(-ENXIO);
5994fb0a5ebSJens Wiklander 	}
6004fb0a5ebSJens Wiklander 
6014fb0a5ebSJens Wiklander 	if (!strcmp("hvc", method))
6024fb0a5ebSJens Wiklander 		return optee_smccc_hvc;
6034fb0a5ebSJens Wiklander 	else if (!strcmp("smc", method))
6044fb0a5ebSJens Wiklander 		return optee_smccc_smc;
6054fb0a5ebSJens Wiklander 
6064fb0a5ebSJens Wiklander 	pr_warn("invalid \"method\" property: %s\n", method);
6074fb0a5ebSJens Wiklander 	return ERR_PTR(-EINVAL);
6084fb0a5ebSJens Wiklander }
6094fb0a5ebSJens Wiklander 
610f25889f9SAllen Pais /* optee_remove - Device Removal Routine
611f25889f9SAllen Pais  * @pdev: platform device information struct
612f25889f9SAllen Pais  *
613f25889f9SAllen Pais  * optee_remove is called by platform subsystem to alert the driver
614f25889f9SAllen Pais  * that it should release the device
615f25889f9SAllen Pais  */
616f25889f9SAllen Pais 
617f349710eSArd Biesheuvel static int optee_remove(struct platform_device *pdev)
618f349710eSArd Biesheuvel {
619f349710eSArd Biesheuvel 	struct optee *optee = platform_get_drvdata(pdev);
620f349710eSArd Biesheuvel 
6217f565d0eSSumit Garg 	/* Unregister OP-TEE specific client devices on TEE bus */
6227f565d0eSSumit Garg 	optee_unregister_devices();
6237f565d0eSSumit Garg 
624f349710eSArd Biesheuvel 	/*
625f349710eSArd Biesheuvel 	 * Ask OP-TEE to free all cached shared memory objects to decrease
626f349710eSArd Biesheuvel 	 * reference counters and also avoid wild pointers in secure world
627f349710eSArd Biesheuvel 	 * into the old shared memory range.
628f349710eSArd Biesheuvel 	 */
629f349710eSArd Biesheuvel 	optee_disable_shm_cache(optee);
630f349710eSArd Biesheuvel 
631f349710eSArd Biesheuvel 	/*
632f349710eSArd Biesheuvel 	 * The two devices have to be unregistered before we can free the
633f349710eSArd Biesheuvel 	 * other resources.
634f349710eSArd Biesheuvel 	 */
635f349710eSArd Biesheuvel 	tee_device_unregister(optee->supp_teedev);
636f349710eSArd Biesheuvel 	tee_device_unregister(optee->teedev);
637f349710eSArd Biesheuvel 
638f349710eSArd Biesheuvel 	tee_shm_pool_free(optee->pool);
639f349710eSArd Biesheuvel 	if (optee->memremaped_shm)
640f349710eSArd Biesheuvel 		memunmap(optee->memremaped_shm);
641f349710eSArd Biesheuvel 	optee_wait_queue_exit(&optee->wait_queue);
642f349710eSArd Biesheuvel 	optee_supp_uninit(&optee->supp);
643f349710eSArd Biesheuvel 	mutex_destroy(&optee->call_queue.mutex);
644f349710eSArd Biesheuvel 
645f349710eSArd Biesheuvel 	kfree(optee);
646f349710eSArd Biesheuvel 
647f349710eSArd Biesheuvel 	return 0;
648f349710eSArd Biesheuvel }
649f349710eSArd Biesheuvel 
650f25889f9SAllen Pais /* optee_shutdown - Device Removal Routine
651f25889f9SAllen Pais  * @pdev: platform device information struct
652f25889f9SAllen Pais  *
653f25889f9SAllen Pais  * platform_shutdown is called by the platform subsystem to alert
654f25889f9SAllen Pais  * the driver that a shutdown, reboot, or kexec is happening and
655f25889f9SAllen Pais  * device must be disabled.
656f25889f9SAllen Pais  */
657f25889f9SAllen Pais static void optee_shutdown(struct platform_device *pdev)
658f25889f9SAllen Pais {
659f25889f9SAllen Pais 	optee_disable_shm_cache(platform_get_drvdata(pdev));
660f25889f9SAllen Pais }
661f25889f9SAllen Pais 
662f349710eSArd Biesheuvel static int optee_probe(struct platform_device *pdev)
6634fb0a5ebSJens Wiklander {
6644fb0a5ebSJens Wiklander 	optee_invoke_fn *invoke_fn;
6659733b072SVolodymyr Babchuk 	struct tee_shm_pool *pool = ERR_PTR(-EINVAL);
6664fb0a5ebSJens Wiklander 	struct optee *optee = NULL;
6674fb0a5ebSJens Wiklander 	void *memremaped_shm = NULL;
6684fb0a5ebSJens Wiklander 	struct tee_device *teedev;
6694fb0a5ebSJens Wiklander 	u32 sec_caps;
6704fb0a5ebSJens Wiklander 	int rc;
6714fb0a5ebSJens Wiklander 
672adf752afSTyler Hicks 	/*
673adf752afSTyler Hicks 	 * The kernel may have crashed at the same time that all available
674adf752afSTyler Hicks 	 * secure world threads were suspended and we cannot reschedule the
675adf752afSTyler Hicks 	 * suspended threads without access to the crashed kernel's wait_queue.
676adf752afSTyler Hicks 	 * Therefore, we cannot reliably initialize the OP-TEE driver in the
677adf752afSTyler Hicks 	 * kdump kernel.
678adf752afSTyler Hicks 	 */
679adf752afSTyler Hicks 	if (is_kdump_kernel())
680adf752afSTyler Hicks 		return -ENODEV;
681adf752afSTyler Hicks 
682f349710eSArd Biesheuvel 	invoke_fn = get_invoke_func(&pdev->dev);
6834fb0a5ebSJens Wiklander 	if (IS_ERR(invoke_fn))
684f349710eSArd Biesheuvel 		return PTR_ERR(invoke_fn);
6854fb0a5ebSJens Wiklander 
6864fb0a5ebSJens Wiklander 	if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
6874fb0a5ebSJens Wiklander 		pr_warn("api uid mismatch\n");
688f349710eSArd Biesheuvel 		return -EINVAL;
6894fb0a5ebSJens Wiklander 	}
6904fb0a5ebSJens Wiklander 
6915c5f8030SJérôme Forissier 	optee_msg_get_os_revision(invoke_fn);
6925c5f8030SJérôme Forissier 
6934fb0a5ebSJens Wiklander 	if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
6944fb0a5ebSJens Wiklander 		pr_warn("api revision mismatch\n");
695f349710eSArd Biesheuvel 		return -EINVAL;
6964fb0a5ebSJens Wiklander 	}
6974fb0a5ebSJens Wiklander 
6984fb0a5ebSJens Wiklander 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
6994fb0a5ebSJens Wiklander 		pr_warn("capabilities mismatch\n");
700f349710eSArd Biesheuvel 		return -EINVAL;
7014fb0a5ebSJens Wiklander 	}
7024fb0a5ebSJens Wiklander 
7034fb0a5ebSJens Wiklander 	/*
7049733b072SVolodymyr Babchuk 	 * Try to use dynamic shared memory if possible
7054fb0a5ebSJens Wiklander 	 */
7069733b072SVolodymyr Babchuk 	if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
7079733b072SVolodymyr Babchuk 		pool = optee_config_dyn_shm();
7084fb0a5ebSJens Wiklander 
7099733b072SVolodymyr Babchuk 	/*
7109733b072SVolodymyr Babchuk 	 * If dynamic shared memory is not available or failed - try static one
7119733b072SVolodymyr Babchuk 	 */
7129733b072SVolodymyr Babchuk 	if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
7139733b072SVolodymyr Babchuk 		pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
7149733b072SVolodymyr Babchuk 
7154fb0a5ebSJens Wiklander 	if (IS_ERR(pool))
716f349710eSArd Biesheuvel 		return PTR_ERR(pool);
7174fb0a5ebSJens Wiklander 
7184fb0a5ebSJens Wiklander 	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
7194fb0a5ebSJens Wiklander 	if (!optee) {
7204fb0a5ebSJens Wiklander 		rc = -ENOMEM;
7214fb0a5ebSJens Wiklander 		goto err;
7224fb0a5ebSJens Wiklander 	}
7234fb0a5ebSJens Wiklander 
724*4602c584SJens Wiklander 	optee->ops = &optee_ops;
7254fb0a5ebSJens Wiklander 	optee->invoke_fn = invoke_fn;
726d885cc5eSVolodymyr Babchuk 	optee->sec_caps = sec_caps;
7274fb0a5ebSJens Wiklander 
728*4602c584SJens Wiklander 	teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee);
7294fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
7304fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
7314fb0a5ebSJens Wiklander 		goto err;
7324fb0a5ebSJens Wiklander 	}
7334fb0a5ebSJens Wiklander 	optee->teedev = teedev;
7344fb0a5ebSJens Wiklander 
7354fb0a5ebSJens Wiklander 	teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
7364fb0a5ebSJens Wiklander 	if (IS_ERR(teedev)) {
7374fb0a5ebSJens Wiklander 		rc = PTR_ERR(teedev);
7384fb0a5ebSJens Wiklander 		goto err;
7394fb0a5ebSJens Wiklander 	}
7404fb0a5ebSJens Wiklander 	optee->supp_teedev = teedev;
7414fb0a5ebSJens Wiklander 
7424fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->teedev);
7434fb0a5ebSJens Wiklander 	if (rc)
7444fb0a5ebSJens Wiklander 		goto err;
7454fb0a5ebSJens Wiklander 
7464fb0a5ebSJens Wiklander 	rc = tee_device_register(optee->supp_teedev);
7474fb0a5ebSJens Wiklander 	if (rc)
7484fb0a5ebSJens Wiklander 		goto err;
7494fb0a5ebSJens Wiklander 
7504fb0a5ebSJens Wiklander 	mutex_init(&optee->call_queue.mutex);
7514fb0a5ebSJens Wiklander 	INIT_LIST_HEAD(&optee->call_queue.waiters);
7524fb0a5ebSJens Wiklander 	optee_wait_queue_init(&optee->wait_queue);
7534fb0a5ebSJens Wiklander 	optee_supp_init(&optee->supp);
7544fb0a5ebSJens Wiklander 	optee->memremaped_shm = memremaped_shm;
7554fb0a5ebSJens Wiklander 	optee->pool = pool;
7564fb0a5ebSJens Wiklander 
757b5c10dd0STyler Hicks 	/*
758b5c10dd0STyler Hicks 	 * Ensure that there are no pre-existing shm objects before enabling
759b5c10dd0STyler Hicks 	 * the shm cache so that there's no chance of receiving an invalid
760b5c10dd0STyler Hicks 	 * address during shutdown. This could occur, for example, if we're
761b5c10dd0STyler Hicks 	 * kexec booting from an older kernel that did not properly cleanup the
762b5c10dd0STyler Hicks 	 * shm cache.
763b5c10dd0STyler Hicks 	 */
764b5c10dd0STyler Hicks 	optee_disable_unmapped_shm_cache(optee);
765b5c10dd0STyler Hicks 
7664fb0a5ebSJens Wiklander 	optee_enable_shm_cache(optee);
7674fb0a5ebSJens Wiklander 
7683c15ddb9SVictor Chong 	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
7693c15ddb9SVictor Chong 		pr_info("dynamic shared memory is enabled\n");
7703c15ddb9SVictor Chong 
771f349710eSArd Biesheuvel 	platform_set_drvdata(pdev, optee);
772f349710eSArd Biesheuvel 
7735f178bb7SMaxim Uvarov 	rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
774f349710eSArd Biesheuvel 	if (rc) {
775f349710eSArd Biesheuvel 		optee_remove(pdev);
776f349710eSArd Biesheuvel 		return rc;
777f349710eSArd Biesheuvel 	}
778f349710eSArd Biesheuvel 
779f349710eSArd Biesheuvel 	pr_info("initialized driver\n");
780f349710eSArd Biesheuvel 	return 0;
7814fb0a5ebSJens Wiklander err:
7824fb0a5ebSJens Wiklander 	if (optee) {
7834fb0a5ebSJens Wiklander 		/*
7844fb0a5ebSJens Wiklander 		 * tee_device_unregister() is safe to call even if the
7854fb0a5ebSJens Wiklander 		 * devices hasn't been registered with
7864fb0a5ebSJens Wiklander 		 * tee_device_register() yet.
7874fb0a5ebSJens Wiklander 		 */
7884fb0a5ebSJens Wiklander 		tee_device_unregister(optee->supp_teedev);
7894fb0a5ebSJens Wiklander 		tee_device_unregister(optee->teedev);
7904fb0a5ebSJens Wiklander 		kfree(optee);
7914fb0a5ebSJens Wiklander 	}
7924fb0a5ebSJens Wiklander 	if (pool)
7934fb0a5ebSJens Wiklander 		tee_shm_pool_free(pool);
7944fb0a5ebSJens Wiklander 	if (memremaped_shm)
7954fb0a5ebSJens Wiklander 		memunmap(memremaped_shm);
79603212e34SJens Wiklander 	return rc;
79703212e34SJens Wiklander }
79803212e34SJens Wiklander 
799f349710eSArd Biesheuvel static const struct of_device_id optee_dt_match[] = {
800f349710eSArd Biesheuvel 	{ .compatible = "linaro,optee-tz" },
801f349710eSArd Biesheuvel 	{},
802f349710eSArd Biesheuvel };
803f349710eSArd Biesheuvel MODULE_DEVICE_TABLE(of, optee_dt_match);
80403212e34SJens Wiklander 
805f349710eSArd Biesheuvel static struct platform_driver optee_driver = {
806f349710eSArd Biesheuvel 	.probe  = optee_probe,
807f349710eSArd Biesheuvel 	.remove = optee_remove,
808f25889f9SAllen Pais 	.shutdown = optee_shutdown,
809f349710eSArd Biesheuvel 	.driver = {
810f349710eSArd Biesheuvel 		.name = "optee",
811f349710eSArd Biesheuvel 		.of_match_table = optee_dt_match,
812f349710eSArd Biesheuvel 	},
813f349710eSArd Biesheuvel };
814f349710eSArd Biesheuvel module_platform_driver(optee_driver);
8154fb0a5ebSJens Wiklander 
8164fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro");
8174fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver");
8184fb0a5ebSJens Wiklander MODULE_VERSION("1.0");
8194fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2");
820f349710eSArd Biesheuvel MODULE_ALIAS("platform:optee");
821