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 2814fb0a5ebSJens Wiklander shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED); 2824fb0a5ebSJens Wiklander if (!IS_ERR(shm)) { 2834fb0a5ebSJens Wiklander arg = tee_shm_get_va(shm, 0); 2844fb0a5ebSJens Wiklander /* 285efb14036SJens Wiklander * If va2pa fails for some reason, we can't call into 286efb14036SJens Wiklander * secure world, only free the memory. Secure OS will leak 287efb14036SJens Wiklander * sessions and finally refuse more sessions, but we will 288efb14036SJens Wiklander * at least let normal world reclaim its memory. 2894fb0a5ebSJens Wiklander */ 2904fb0a5ebSJens Wiklander if (!IS_ERR(arg)) 291efb14036SJens Wiklander if (tee_shm_va2pa(shm, arg, &parg)) 292efb14036SJens Wiklander arg = NULL; /* prevent usage of parg below */ 2934fb0a5ebSJens Wiklander } 2944fb0a5ebSJens Wiklander 2954fb0a5ebSJens Wiklander list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, 2964fb0a5ebSJens Wiklander list_node) { 2974fb0a5ebSJens Wiklander list_del(&sess->list_node); 2984fb0a5ebSJens Wiklander if (!IS_ERR_OR_NULL(arg)) { 2994fb0a5ebSJens Wiklander memset(arg, 0, sizeof(*arg)); 3004fb0a5ebSJens Wiklander arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; 3014fb0a5ebSJens Wiklander arg->session = sess->session_id; 3024fb0a5ebSJens Wiklander optee_do_call_with_arg(ctx, parg); 3034fb0a5ebSJens Wiklander } 3044fb0a5ebSJens Wiklander kfree(sess); 3054fb0a5ebSJens Wiklander } 3064fb0a5ebSJens Wiklander kfree(ctxdata); 3074fb0a5ebSJens Wiklander 3084fb0a5ebSJens Wiklander if (!IS_ERR(shm)) 3094fb0a5ebSJens Wiklander tee_shm_free(shm); 3104fb0a5ebSJens Wiklander 3114fb0a5ebSJens Wiklander ctx->data = NULL; 3124fb0a5ebSJens Wiklander 3135f178bb7SMaxim Uvarov if (teedev == optee->supp_teedev) { 3145f178bb7SMaxim Uvarov if (optee->scan_bus_wq) { 3155f178bb7SMaxim Uvarov destroy_workqueue(optee->scan_bus_wq); 3165f178bb7SMaxim Uvarov optee->scan_bus_wq = NULL; 3175f178bb7SMaxim Uvarov } 3181647a5acSJens Wiklander optee_supp_release(&optee->supp); 3194fb0a5ebSJens Wiklander } 3205f178bb7SMaxim Uvarov } 3214fb0a5ebSJens Wiklander 32296e72ddeSBhumika Goyal static const struct tee_driver_ops optee_ops = { 3234fb0a5ebSJens Wiklander .get_version = optee_get_version, 3244fb0a5ebSJens Wiklander .open = optee_open, 3254fb0a5ebSJens Wiklander .release = optee_release, 3264fb0a5ebSJens Wiklander .open_session = optee_open_session, 3274fb0a5ebSJens Wiklander .close_session = optee_close_session, 3284fb0a5ebSJens Wiklander .invoke_func = optee_invoke_func, 3294fb0a5ebSJens Wiklander .cancel_req = optee_cancel_req, 33006ca7917SVolodymyr Babchuk .shm_register = optee_shm_register, 33106ca7917SVolodymyr Babchuk .shm_unregister = optee_shm_unregister, 3324fb0a5ebSJens Wiklander }; 3334fb0a5ebSJens Wiklander 33496e72ddeSBhumika Goyal static const struct tee_desc optee_desc = { 3354fb0a5ebSJens Wiklander .name = DRIVER_NAME "-clnt", 3364fb0a5ebSJens Wiklander .ops = &optee_ops, 3374fb0a5ebSJens Wiklander .owner = THIS_MODULE, 3384fb0a5ebSJens Wiklander }; 3394fb0a5ebSJens Wiklander 34096e72ddeSBhumika Goyal static const struct tee_driver_ops optee_supp_ops = { 3414fb0a5ebSJens Wiklander .get_version = optee_get_version, 3424fb0a5ebSJens Wiklander .open = optee_open, 3434fb0a5ebSJens Wiklander .release = optee_release, 3444fb0a5ebSJens Wiklander .supp_recv = optee_supp_recv, 3454fb0a5ebSJens Wiklander .supp_send = optee_supp_send, 34653a107c8SVolodymyr Babchuk .shm_register = optee_shm_register_supp, 34753a107c8SVolodymyr Babchuk .shm_unregister = optee_shm_unregister_supp, 3484fb0a5ebSJens Wiklander }; 3494fb0a5ebSJens Wiklander 35096e72ddeSBhumika Goyal static const struct tee_desc optee_supp_desc = { 3514fb0a5ebSJens Wiklander .name = DRIVER_NAME "-supp", 3524fb0a5ebSJens Wiklander .ops = &optee_supp_ops, 3534fb0a5ebSJens Wiklander .owner = THIS_MODULE, 3544fb0a5ebSJens Wiklander .flags = TEE_DESC_PRIVILEGED, 3554fb0a5ebSJens Wiklander }; 3564fb0a5ebSJens Wiklander 3574fb0a5ebSJens Wiklander static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) 3584fb0a5ebSJens Wiklander { 3594fb0a5ebSJens Wiklander struct arm_smccc_res res; 3604fb0a5ebSJens Wiklander 3614fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); 3624fb0a5ebSJens Wiklander 3634fb0a5ebSJens Wiklander if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && 3644fb0a5ebSJens Wiklander res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) 3654fb0a5ebSJens Wiklander return true; 3664fb0a5ebSJens Wiklander return false; 3674fb0a5ebSJens Wiklander } 3684fb0a5ebSJens Wiklander 3695c5f8030SJérôme Forissier static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) 3705c5f8030SJérôme Forissier { 3715c5f8030SJérôme Forissier union { 3725c5f8030SJérôme Forissier struct arm_smccc_res smccc; 3735c5f8030SJérôme Forissier struct optee_smc_call_get_os_revision_result result; 3745c5f8030SJérôme Forissier } res = { 3755c5f8030SJérôme Forissier .result = { 3765c5f8030SJérôme Forissier .build_id = 0 3775c5f8030SJérôme Forissier } 3785c5f8030SJérôme Forissier }; 3795c5f8030SJérôme Forissier 3805c5f8030SJérôme Forissier invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, 3815c5f8030SJérôme Forissier &res.smccc); 3825c5f8030SJérôme Forissier 3835c5f8030SJérôme Forissier if (res.result.build_id) 3845c5f8030SJérôme Forissier pr_info("revision %lu.%lu (%08lx)", res.result.major, 3855c5f8030SJérôme Forissier res.result.minor, res.result.build_id); 3865c5f8030SJérôme Forissier else 3875c5f8030SJérôme Forissier pr_info("revision %lu.%lu", res.result.major, res.result.minor); 3885c5f8030SJérôme Forissier } 3895c5f8030SJérôme Forissier 3904fb0a5ebSJens Wiklander static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) 3914fb0a5ebSJens Wiklander { 3924fb0a5ebSJens Wiklander union { 3934fb0a5ebSJens Wiklander struct arm_smccc_res smccc; 3944fb0a5ebSJens Wiklander struct optee_smc_calls_revision_result result; 3954fb0a5ebSJens Wiklander } res; 3964fb0a5ebSJens Wiklander 3974fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 3984fb0a5ebSJens Wiklander 3994fb0a5ebSJens Wiklander if (res.result.major == OPTEE_MSG_REVISION_MAJOR && 4004fb0a5ebSJens Wiklander (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) 4014fb0a5ebSJens Wiklander return true; 4024fb0a5ebSJens Wiklander return false; 4034fb0a5ebSJens Wiklander } 4044fb0a5ebSJens Wiklander 4054fb0a5ebSJens Wiklander static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, 4064fb0a5ebSJens Wiklander u32 *sec_caps) 4074fb0a5ebSJens Wiklander { 4084fb0a5ebSJens Wiklander union { 4094fb0a5ebSJens Wiklander struct arm_smccc_res smccc; 4104fb0a5ebSJens Wiklander struct optee_smc_exchange_capabilities_result result; 4114fb0a5ebSJens Wiklander } res; 4124fb0a5ebSJens Wiklander u32 a1 = 0; 4134fb0a5ebSJens Wiklander 4144fb0a5ebSJens Wiklander /* 4154fb0a5ebSJens Wiklander * TODO This isn't enough to tell if it's UP system (from kernel 4164fb0a5ebSJens Wiklander * point of view) or not, is_smp() returns the the information 4174fb0a5ebSJens Wiklander * needed, but can't be called directly from here. 4184fb0a5ebSJens Wiklander */ 4194fb0a5ebSJens Wiklander if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) 4204fb0a5ebSJens Wiklander a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; 4214fb0a5ebSJens Wiklander 4224fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, 4234fb0a5ebSJens Wiklander &res.smccc); 4244fb0a5ebSJens Wiklander 4254fb0a5ebSJens Wiklander if (res.result.status != OPTEE_SMC_RETURN_OK) 4264fb0a5ebSJens Wiklander return false; 4274fb0a5ebSJens Wiklander 4284fb0a5ebSJens Wiklander *sec_caps = res.result.capabilities; 4294fb0a5ebSJens Wiklander return true; 4304fb0a5ebSJens Wiklander } 4314fb0a5ebSJens Wiklander 4329733b072SVolodymyr Babchuk static struct tee_shm_pool *optee_config_dyn_shm(void) 4339733b072SVolodymyr Babchuk { 4349733b072SVolodymyr Babchuk struct tee_shm_pool_mgr *priv_mgr; 4359733b072SVolodymyr Babchuk struct tee_shm_pool_mgr *dmabuf_mgr; 4369733b072SVolodymyr Babchuk void *rc; 4379733b072SVolodymyr Babchuk 4389733b072SVolodymyr Babchuk rc = optee_shm_pool_alloc_pages(); 4399733b072SVolodymyr Babchuk if (IS_ERR(rc)) 4409733b072SVolodymyr Babchuk return rc; 4419733b072SVolodymyr Babchuk priv_mgr = rc; 4429733b072SVolodymyr Babchuk 4439733b072SVolodymyr Babchuk rc = optee_shm_pool_alloc_pages(); 4449733b072SVolodymyr Babchuk if (IS_ERR(rc)) { 4459733b072SVolodymyr Babchuk tee_shm_pool_mgr_destroy(priv_mgr); 4469733b072SVolodymyr Babchuk return rc; 4479733b072SVolodymyr Babchuk } 4489733b072SVolodymyr Babchuk dmabuf_mgr = rc; 4499733b072SVolodymyr Babchuk 4509733b072SVolodymyr Babchuk rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); 4519733b072SVolodymyr Babchuk if (IS_ERR(rc)) { 4529733b072SVolodymyr Babchuk tee_shm_pool_mgr_destroy(priv_mgr); 4539733b072SVolodymyr Babchuk tee_shm_pool_mgr_destroy(dmabuf_mgr); 4549733b072SVolodymyr Babchuk } 4559733b072SVolodymyr Babchuk 4569733b072SVolodymyr Babchuk return rc; 4579733b072SVolodymyr Babchuk } 4589733b072SVolodymyr Babchuk 4594fb0a5ebSJens Wiklander static struct tee_shm_pool * 4609733b072SVolodymyr Babchuk optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) 4614fb0a5ebSJens Wiklander { 4624fb0a5ebSJens Wiklander union { 4634fb0a5ebSJens Wiklander struct arm_smccc_res smccc; 4644fb0a5ebSJens Wiklander struct optee_smc_get_shm_config_result result; 4654fb0a5ebSJens Wiklander } res; 4664fb0a5ebSJens Wiklander unsigned long vaddr; 4674fb0a5ebSJens Wiklander phys_addr_t paddr; 4684fb0a5ebSJens Wiklander size_t size; 4694fb0a5ebSJens Wiklander phys_addr_t begin; 4704fb0a5ebSJens Wiklander phys_addr_t end; 4714fb0a5ebSJens Wiklander void *va; 472f58e236cSVolodymyr Babchuk struct tee_shm_pool_mgr *priv_mgr; 473f58e236cSVolodymyr Babchuk struct tee_shm_pool_mgr *dmabuf_mgr; 474f58e236cSVolodymyr Babchuk void *rc; 4759733b072SVolodymyr Babchuk const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; 4764fb0a5ebSJens Wiklander 4774fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 4784fb0a5ebSJens Wiklander if (res.result.status != OPTEE_SMC_RETURN_OK) { 4799733b072SVolodymyr Babchuk pr_err("static shm service not available\n"); 4804fb0a5ebSJens Wiklander return ERR_PTR(-ENOENT); 4814fb0a5ebSJens Wiklander } 4824fb0a5ebSJens Wiklander 4834fb0a5ebSJens Wiklander if (res.result.settings != OPTEE_SMC_SHM_CACHED) { 4844fb0a5ebSJens Wiklander pr_err("only normal cached shared memory supported\n"); 4854fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 4864fb0a5ebSJens Wiklander } 4874fb0a5ebSJens Wiklander 4884fb0a5ebSJens Wiklander begin = roundup(res.result.start, PAGE_SIZE); 4894fb0a5ebSJens Wiklander end = rounddown(res.result.start + res.result.size, PAGE_SIZE); 4904fb0a5ebSJens Wiklander paddr = begin; 4914fb0a5ebSJens Wiklander size = end - begin; 4924fb0a5ebSJens Wiklander 4934fb0a5ebSJens Wiklander if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { 4944fb0a5ebSJens Wiklander pr_err("too small shared memory area\n"); 4954fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 4964fb0a5ebSJens Wiklander } 4974fb0a5ebSJens Wiklander 4984fb0a5ebSJens Wiklander va = memremap(paddr, size, MEMREMAP_WB); 4994fb0a5ebSJens Wiklander if (!va) { 5004fb0a5ebSJens Wiklander pr_err("shared memory ioremap failed\n"); 5014fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 5024fb0a5ebSJens Wiklander } 5034fb0a5ebSJens Wiklander vaddr = (unsigned long)va; 5044fb0a5ebSJens Wiklander 505f58e236cSVolodymyr Babchuk rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, 506f58e236cSVolodymyr Babchuk 3 /* 8 bytes aligned */); 507f58e236cSVolodymyr Babchuk if (IS_ERR(rc)) 508f58e236cSVolodymyr Babchuk goto err_memunmap; 509f58e236cSVolodymyr Babchuk priv_mgr = rc; 510f58e236cSVolodymyr Babchuk 511f58e236cSVolodymyr Babchuk vaddr += sz; 512f58e236cSVolodymyr Babchuk paddr += sz; 513f58e236cSVolodymyr Babchuk size -= sz; 5144fb0a5ebSJens Wiklander 515f58e236cSVolodymyr Babchuk rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); 516f58e236cSVolodymyr Babchuk if (IS_ERR(rc)) 517f58e236cSVolodymyr Babchuk goto err_free_priv_mgr; 518f58e236cSVolodymyr Babchuk dmabuf_mgr = rc; 519f58e236cSVolodymyr Babchuk 520f58e236cSVolodymyr Babchuk rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); 521f58e236cSVolodymyr Babchuk if (IS_ERR(rc)) 522f58e236cSVolodymyr Babchuk goto err_free_dmabuf_mgr; 523f58e236cSVolodymyr Babchuk 5244fb0a5ebSJens Wiklander *memremaped_shm = va; 525f58e236cSVolodymyr Babchuk 526f58e236cSVolodymyr Babchuk return rc; 527f58e236cSVolodymyr Babchuk 528f58e236cSVolodymyr Babchuk err_free_dmabuf_mgr: 529f58e236cSVolodymyr Babchuk tee_shm_pool_mgr_destroy(dmabuf_mgr); 530f58e236cSVolodymyr Babchuk err_free_priv_mgr: 531f58e236cSVolodymyr Babchuk tee_shm_pool_mgr_destroy(priv_mgr); 532f58e236cSVolodymyr Babchuk err_memunmap: 533f58e236cSVolodymyr Babchuk memunmap(va); 534f58e236cSVolodymyr Babchuk return rc; 5354fb0a5ebSJens Wiklander } 5364fb0a5ebSJens Wiklander 5374fb0a5ebSJens Wiklander /* Simple wrapper functions to be able to use a function pointer */ 5384fb0a5ebSJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1, 5394fb0a5ebSJens Wiklander unsigned long a2, unsigned long a3, 5404fb0a5ebSJens Wiklander unsigned long a4, unsigned long a5, 5414fb0a5ebSJens Wiklander unsigned long a6, unsigned long a7, 5424fb0a5ebSJens Wiklander struct arm_smccc_res *res) 5434fb0a5ebSJens Wiklander { 5444fb0a5ebSJens Wiklander arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); 5454fb0a5ebSJens Wiklander } 5464fb0a5ebSJens Wiklander 5474fb0a5ebSJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1, 5484fb0a5ebSJens Wiklander unsigned long a2, unsigned long a3, 5494fb0a5ebSJens Wiklander unsigned long a4, unsigned long a5, 5504fb0a5ebSJens Wiklander unsigned long a6, unsigned long a7, 5514fb0a5ebSJens Wiklander struct arm_smccc_res *res) 5524fb0a5ebSJens Wiklander { 5534fb0a5ebSJens Wiklander arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); 5544fb0a5ebSJens Wiklander } 5554fb0a5ebSJens Wiklander 556f349710eSArd Biesheuvel static optee_invoke_fn *get_invoke_func(struct device *dev) 5574fb0a5ebSJens Wiklander { 5584fb0a5ebSJens Wiklander const char *method; 5594fb0a5ebSJens Wiklander 560f349710eSArd Biesheuvel pr_info("probing for conduit method.\n"); 5614fb0a5ebSJens Wiklander 562f349710eSArd Biesheuvel if (device_property_read_string(dev, "method", &method)) { 5634fb0a5ebSJens Wiklander pr_warn("missing \"method\" property\n"); 5644fb0a5ebSJens Wiklander return ERR_PTR(-ENXIO); 5654fb0a5ebSJens Wiklander } 5664fb0a5ebSJens Wiklander 5674fb0a5ebSJens Wiklander if (!strcmp("hvc", method)) 5684fb0a5ebSJens Wiklander return optee_smccc_hvc; 5694fb0a5ebSJens Wiklander else if (!strcmp("smc", method)) 5704fb0a5ebSJens Wiklander return optee_smccc_smc; 5714fb0a5ebSJens Wiklander 5724fb0a5ebSJens Wiklander pr_warn("invalid \"method\" property: %s\n", method); 5734fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 5744fb0a5ebSJens Wiklander } 5754fb0a5ebSJens Wiklander 576f25889f9SAllen Pais /* optee_remove - Device Removal Routine 577f25889f9SAllen Pais * @pdev: platform device information struct 578f25889f9SAllen Pais * 579f25889f9SAllen Pais * optee_remove is called by platform subsystem to alert the driver 580f25889f9SAllen Pais * that it should release the device 581f25889f9SAllen Pais */ 582f25889f9SAllen Pais 583f349710eSArd Biesheuvel static int optee_remove(struct platform_device *pdev) 584f349710eSArd Biesheuvel { 585f349710eSArd Biesheuvel struct optee *optee = platform_get_drvdata(pdev); 586f349710eSArd Biesheuvel 587f349710eSArd Biesheuvel /* 588f349710eSArd Biesheuvel * Ask OP-TEE to free all cached shared memory objects to decrease 589f349710eSArd Biesheuvel * reference counters and also avoid wild pointers in secure world 590f349710eSArd Biesheuvel * into the old shared memory range. 591f349710eSArd Biesheuvel */ 592f349710eSArd Biesheuvel optee_disable_shm_cache(optee); 593f349710eSArd Biesheuvel 594f349710eSArd Biesheuvel /* 595f349710eSArd Biesheuvel * The two devices have to be unregistered before we can free the 596f349710eSArd Biesheuvel * other resources. 597f349710eSArd Biesheuvel */ 598f349710eSArd Biesheuvel tee_device_unregister(optee->supp_teedev); 599f349710eSArd Biesheuvel tee_device_unregister(optee->teedev); 600f349710eSArd Biesheuvel 601f349710eSArd Biesheuvel tee_shm_pool_free(optee->pool); 602f349710eSArd Biesheuvel if (optee->memremaped_shm) 603f349710eSArd Biesheuvel memunmap(optee->memremaped_shm); 604f349710eSArd Biesheuvel optee_wait_queue_exit(&optee->wait_queue); 605f349710eSArd Biesheuvel optee_supp_uninit(&optee->supp); 606f349710eSArd Biesheuvel mutex_destroy(&optee->call_queue.mutex); 607f349710eSArd Biesheuvel 608f349710eSArd Biesheuvel kfree(optee); 609f349710eSArd Biesheuvel 610f349710eSArd Biesheuvel return 0; 611f349710eSArd Biesheuvel } 612f349710eSArd Biesheuvel 613f25889f9SAllen Pais /* optee_shutdown - Device Removal Routine 614f25889f9SAllen Pais * @pdev: platform device information struct 615f25889f9SAllen Pais * 616f25889f9SAllen Pais * platform_shutdown is called by the platform subsystem to alert 617f25889f9SAllen Pais * the driver that a shutdown, reboot, or kexec is happening and 618f25889f9SAllen Pais * device must be disabled. 619f25889f9SAllen Pais */ 620f25889f9SAllen Pais static void optee_shutdown(struct platform_device *pdev) 621f25889f9SAllen Pais { 622f25889f9SAllen Pais optee_disable_shm_cache(platform_get_drvdata(pdev)); 623f25889f9SAllen Pais } 624f25889f9SAllen Pais 625f349710eSArd Biesheuvel static int optee_probe(struct platform_device *pdev) 6264fb0a5ebSJens Wiklander { 6274fb0a5ebSJens Wiklander optee_invoke_fn *invoke_fn; 6289733b072SVolodymyr Babchuk struct tee_shm_pool *pool = ERR_PTR(-EINVAL); 6294fb0a5ebSJens Wiklander struct optee *optee = NULL; 6304fb0a5ebSJens Wiklander void *memremaped_shm = NULL; 6314fb0a5ebSJens Wiklander struct tee_device *teedev; 6324fb0a5ebSJens Wiklander u32 sec_caps; 6334fb0a5ebSJens Wiklander int rc; 6344fb0a5ebSJens Wiklander 635adf752afSTyler Hicks /* 636adf752afSTyler Hicks * The kernel may have crashed at the same time that all available 637adf752afSTyler Hicks * secure world threads were suspended and we cannot reschedule the 638adf752afSTyler Hicks * suspended threads without access to the crashed kernel's wait_queue. 639adf752afSTyler Hicks * Therefore, we cannot reliably initialize the OP-TEE driver in the 640adf752afSTyler Hicks * kdump kernel. 641adf752afSTyler Hicks */ 642adf752afSTyler Hicks if (is_kdump_kernel()) 643adf752afSTyler Hicks return -ENODEV; 644adf752afSTyler Hicks 645f349710eSArd Biesheuvel invoke_fn = get_invoke_func(&pdev->dev); 6464fb0a5ebSJens Wiklander if (IS_ERR(invoke_fn)) 647f349710eSArd Biesheuvel return PTR_ERR(invoke_fn); 6484fb0a5ebSJens Wiklander 6494fb0a5ebSJens Wiklander if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { 6504fb0a5ebSJens Wiklander pr_warn("api uid mismatch\n"); 651f349710eSArd Biesheuvel return -EINVAL; 6524fb0a5ebSJens Wiklander } 6534fb0a5ebSJens Wiklander 6545c5f8030SJérôme Forissier optee_msg_get_os_revision(invoke_fn); 6555c5f8030SJérôme Forissier 6564fb0a5ebSJens Wiklander if (!optee_msg_api_revision_is_compatible(invoke_fn)) { 6574fb0a5ebSJens Wiklander pr_warn("api revision mismatch\n"); 658f349710eSArd Biesheuvel return -EINVAL; 6594fb0a5ebSJens Wiklander } 6604fb0a5ebSJens Wiklander 6614fb0a5ebSJens Wiklander if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { 6624fb0a5ebSJens Wiklander pr_warn("capabilities mismatch\n"); 663f349710eSArd Biesheuvel return -EINVAL; 6644fb0a5ebSJens Wiklander } 6654fb0a5ebSJens Wiklander 6664fb0a5ebSJens Wiklander /* 6679733b072SVolodymyr Babchuk * Try to use dynamic shared memory if possible 6684fb0a5ebSJens Wiklander */ 6699733b072SVolodymyr Babchuk if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) 6709733b072SVolodymyr Babchuk pool = optee_config_dyn_shm(); 6714fb0a5ebSJens Wiklander 6729733b072SVolodymyr Babchuk /* 6739733b072SVolodymyr Babchuk * If dynamic shared memory is not available or failed - try static one 6749733b072SVolodymyr Babchuk */ 6759733b072SVolodymyr Babchuk if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) 6769733b072SVolodymyr Babchuk pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); 6779733b072SVolodymyr Babchuk 6784fb0a5ebSJens Wiklander if (IS_ERR(pool)) 679f349710eSArd Biesheuvel return PTR_ERR(pool); 6804fb0a5ebSJens Wiklander 6814fb0a5ebSJens Wiklander optee = kzalloc(sizeof(*optee), GFP_KERNEL); 6824fb0a5ebSJens Wiklander if (!optee) { 6834fb0a5ebSJens Wiklander rc = -ENOMEM; 6844fb0a5ebSJens Wiklander goto err; 6854fb0a5ebSJens Wiklander } 6864fb0a5ebSJens Wiklander 6874fb0a5ebSJens Wiklander optee->invoke_fn = invoke_fn; 688d885cc5eSVolodymyr Babchuk optee->sec_caps = sec_caps; 6894fb0a5ebSJens Wiklander 6904fb0a5ebSJens Wiklander teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); 6914fb0a5ebSJens Wiklander if (IS_ERR(teedev)) { 6924fb0a5ebSJens Wiklander rc = PTR_ERR(teedev); 6934fb0a5ebSJens Wiklander goto err; 6944fb0a5ebSJens Wiklander } 6954fb0a5ebSJens Wiklander optee->teedev = teedev; 6964fb0a5ebSJens Wiklander 6974fb0a5ebSJens Wiklander teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); 6984fb0a5ebSJens Wiklander if (IS_ERR(teedev)) { 6994fb0a5ebSJens Wiklander rc = PTR_ERR(teedev); 7004fb0a5ebSJens Wiklander goto err; 7014fb0a5ebSJens Wiklander } 7024fb0a5ebSJens Wiklander optee->supp_teedev = teedev; 7034fb0a5ebSJens Wiklander 7044fb0a5ebSJens Wiklander rc = tee_device_register(optee->teedev); 7054fb0a5ebSJens Wiklander if (rc) 7064fb0a5ebSJens Wiklander goto err; 7074fb0a5ebSJens Wiklander 7084fb0a5ebSJens Wiklander rc = tee_device_register(optee->supp_teedev); 7094fb0a5ebSJens Wiklander if (rc) 7104fb0a5ebSJens Wiklander goto err; 7114fb0a5ebSJens Wiklander 7124fb0a5ebSJens Wiklander mutex_init(&optee->call_queue.mutex); 7134fb0a5ebSJens Wiklander INIT_LIST_HEAD(&optee->call_queue.waiters); 7144fb0a5ebSJens Wiklander optee_wait_queue_init(&optee->wait_queue); 7154fb0a5ebSJens Wiklander optee_supp_init(&optee->supp); 7164fb0a5ebSJens Wiklander optee->memremaped_shm = memremaped_shm; 7174fb0a5ebSJens Wiklander optee->pool = pool; 7184fb0a5ebSJens Wiklander 719*b5c10dd0STyler Hicks /* 720*b5c10dd0STyler Hicks * Ensure that there are no pre-existing shm objects before enabling 721*b5c10dd0STyler Hicks * the shm cache so that there's no chance of receiving an invalid 722*b5c10dd0STyler Hicks * address during shutdown. This could occur, for example, if we're 723*b5c10dd0STyler Hicks * kexec booting from an older kernel that did not properly cleanup the 724*b5c10dd0STyler Hicks * shm cache. 725*b5c10dd0STyler Hicks */ 726*b5c10dd0STyler Hicks optee_disable_unmapped_shm_cache(optee); 727*b5c10dd0STyler Hicks 7284fb0a5ebSJens Wiklander optee_enable_shm_cache(optee); 7294fb0a5ebSJens Wiklander 7303c15ddb9SVictor Chong if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) 7313c15ddb9SVictor Chong pr_info("dynamic shared memory is enabled\n"); 7323c15ddb9SVictor Chong 733f349710eSArd Biesheuvel platform_set_drvdata(pdev, optee); 734f349710eSArd Biesheuvel 7355f178bb7SMaxim Uvarov rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); 736f349710eSArd Biesheuvel if (rc) { 737f349710eSArd Biesheuvel optee_remove(pdev); 738f349710eSArd Biesheuvel return rc; 739f349710eSArd Biesheuvel } 740f349710eSArd Biesheuvel 741f349710eSArd Biesheuvel pr_info("initialized driver\n"); 742f349710eSArd Biesheuvel return 0; 7434fb0a5ebSJens Wiklander err: 7444fb0a5ebSJens Wiklander if (optee) { 7454fb0a5ebSJens Wiklander /* 7464fb0a5ebSJens Wiklander * tee_device_unregister() is safe to call even if the 7474fb0a5ebSJens Wiklander * devices hasn't been registered with 7484fb0a5ebSJens Wiklander * tee_device_register() yet. 7494fb0a5ebSJens Wiklander */ 7504fb0a5ebSJens Wiklander tee_device_unregister(optee->supp_teedev); 7514fb0a5ebSJens Wiklander tee_device_unregister(optee->teedev); 7524fb0a5ebSJens Wiklander kfree(optee); 7534fb0a5ebSJens Wiklander } 7544fb0a5ebSJens Wiklander if (pool) 7554fb0a5ebSJens Wiklander tee_shm_pool_free(pool); 7564fb0a5ebSJens Wiklander if (memremaped_shm) 7574fb0a5ebSJens Wiklander memunmap(memremaped_shm); 75803212e34SJens Wiklander return rc; 75903212e34SJens Wiklander } 76003212e34SJens Wiklander 761f349710eSArd Biesheuvel static const struct of_device_id optee_dt_match[] = { 762f349710eSArd Biesheuvel { .compatible = "linaro,optee-tz" }, 763f349710eSArd Biesheuvel {}, 764f349710eSArd Biesheuvel }; 765f349710eSArd Biesheuvel MODULE_DEVICE_TABLE(of, optee_dt_match); 76603212e34SJens Wiklander 767f349710eSArd Biesheuvel static struct platform_driver optee_driver = { 768f349710eSArd Biesheuvel .probe = optee_probe, 769f349710eSArd Biesheuvel .remove = optee_remove, 770f25889f9SAllen Pais .shutdown = optee_shutdown, 771f349710eSArd Biesheuvel .driver = { 772f349710eSArd Biesheuvel .name = "optee", 773f349710eSArd Biesheuvel .of_match_table = optee_dt_match, 774f349710eSArd Biesheuvel }, 775f349710eSArd Biesheuvel }; 776f349710eSArd Biesheuvel module_platform_driver(optee_driver); 7774fb0a5ebSJens Wiklander 7784fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro"); 7794fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver"); 7804fb0a5ebSJens Wiklander MODULE_VERSION("1.0"); 7814fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2"); 782f349710eSArd Biesheuvel MODULE_ALIAS("platform:optee"); 783