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