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" 314fb0a5ebSJens Wiklander 324fb0a5ebSJens Wiklander #define DRIVER_NAME "optee" 334fb0a5ebSJens Wiklander 344fb0a5ebSJens Wiklander #define OPTEE_SHM_NUM_PRIV_PAGES 1 354fb0a5ebSJens Wiklander 364fb0a5ebSJens Wiklander /** 374fb0a5ebSJens Wiklander * optee_from_msg_param() - convert from OPTEE_MSG parameters to 384fb0a5ebSJens Wiklander * struct tee_param 394fb0a5ebSJens Wiklander * @params: subsystem internal parameter representation 404fb0a5ebSJens Wiklander * @num_params: number of elements in the parameter arrays 414fb0a5ebSJens Wiklander * @msg_params: OPTEE_MSG parameters 424fb0a5ebSJens Wiklander * Returns 0 on success or <0 on failure 434fb0a5ebSJens Wiklander */ 444fb0a5ebSJens Wiklander int optee_from_msg_param(struct tee_param *params, size_t num_params, 454fb0a5ebSJens Wiklander const struct optee_msg_param *msg_params) 464fb0a5ebSJens Wiklander { 474fb0a5ebSJens Wiklander int rc; 484fb0a5ebSJens Wiklander size_t n; 494fb0a5ebSJens Wiklander struct tee_shm *shm; 504fb0a5ebSJens Wiklander phys_addr_t pa; 514fb0a5ebSJens Wiklander 524fb0a5ebSJens Wiklander for (n = 0; n < num_params; n++) { 534fb0a5ebSJens Wiklander struct tee_param *p = params + n; 544fb0a5ebSJens Wiklander const struct optee_msg_param *mp = msg_params + n; 554fb0a5ebSJens Wiklander u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; 564fb0a5ebSJens Wiklander 574fb0a5ebSJens Wiklander switch (attr) { 584fb0a5ebSJens Wiklander case OPTEE_MSG_ATTR_TYPE_NONE: 594fb0a5ebSJens Wiklander p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 604fb0a5ebSJens Wiklander memset(&p->u, 0, sizeof(p->u)); 614fb0a5ebSJens Wiklander break; 624fb0a5ebSJens Wiklander case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: 634fb0a5ebSJens Wiklander case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: 644fb0a5ebSJens Wiklander case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: 654fb0a5ebSJens Wiklander p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + 664fb0a5ebSJens Wiklander attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; 674fb0a5ebSJens Wiklander p->u.value.a = mp->u.value.a; 684fb0a5ebSJens Wiklander p->u.value.b = mp->u.value.b; 694fb0a5ebSJens Wiklander p->u.value.c = mp->u.value.c; 704fb0a5ebSJens Wiklander break; 714fb0a5ebSJens Wiklander case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: 724fb0a5ebSJens Wiklander case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: 734fb0a5ebSJens Wiklander case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: 744fb0a5ebSJens Wiklander p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + 754fb0a5ebSJens Wiklander attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; 764fb0a5ebSJens Wiklander p->u.memref.size = mp->u.tmem.size; 774fb0a5ebSJens Wiklander shm = (struct tee_shm *)(unsigned long) 784fb0a5ebSJens Wiklander mp->u.tmem.shm_ref; 794fb0a5ebSJens Wiklander if (!shm) { 804fb0a5ebSJens Wiklander p->u.memref.shm_offs = 0; 814fb0a5ebSJens Wiklander p->u.memref.shm = NULL; 824fb0a5ebSJens Wiklander break; 834fb0a5ebSJens Wiklander } 844fb0a5ebSJens Wiklander rc = tee_shm_get_pa(shm, 0, &pa); 854fb0a5ebSJens Wiklander if (rc) 864fb0a5ebSJens Wiklander return rc; 874fb0a5ebSJens Wiklander p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; 884fb0a5ebSJens Wiklander p->u.memref.shm = shm; 894fb0a5ebSJens Wiklander 904fb0a5ebSJens Wiklander /* Check that the memref is covered by the shm object */ 914fb0a5ebSJens Wiklander if (p->u.memref.size) { 924fb0a5ebSJens Wiklander size_t o = p->u.memref.shm_offs + 934fb0a5ebSJens Wiklander p->u.memref.size - 1; 944fb0a5ebSJens Wiklander 954fb0a5ebSJens Wiklander rc = tee_shm_get_pa(shm, o, NULL); 964fb0a5ebSJens Wiklander if (rc) 974fb0a5ebSJens Wiklander return rc; 984fb0a5ebSJens Wiklander } 994fb0a5ebSJens Wiklander break; 1004fb0a5ebSJens Wiklander default: 1014fb0a5ebSJens Wiklander return -EINVAL; 1024fb0a5ebSJens Wiklander } 1034fb0a5ebSJens Wiklander } 1044fb0a5ebSJens Wiklander return 0; 1054fb0a5ebSJens Wiklander } 1064fb0a5ebSJens Wiklander 1074fb0a5ebSJens Wiklander /** 1084fb0a5ebSJens Wiklander * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters 1094fb0a5ebSJens Wiklander * @msg_params: OPTEE_MSG parameters 1104fb0a5ebSJens Wiklander * @num_params: number of elements in the parameter arrays 1114fb0a5ebSJens Wiklander * @params: subsystem itnernal parameter representation 1124fb0a5ebSJens Wiklander * Returns 0 on success or <0 on failure 1134fb0a5ebSJens Wiklander */ 1144fb0a5ebSJens Wiklander int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, 1154fb0a5ebSJens Wiklander const struct tee_param *params) 1164fb0a5ebSJens Wiklander { 1174fb0a5ebSJens Wiklander int rc; 1184fb0a5ebSJens Wiklander size_t n; 1194fb0a5ebSJens Wiklander phys_addr_t pa; 1204fb0a5ebSJens Wiklander 1214fb0a5ebSJens Wiklander for (n = 0; n < num_params; n++) { 1224fb0a5ebSJens Wiklander const struct tee_param *p = params + n; 1234fb0a5ebSJens Wiklander struct optee_msg_param *mp = msg_params + n; 1244fb0a5ebSJens Wiklander 1254fb0a5ebSJens Wiklander switch (p->attr) { 1264fb0a5ebSJens Wiklander case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: 1274fb0a5ebSJens Wiklander mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 1284fb0a5ebSJens Wiklander memset(&mp->u, 0, sizeof(mp->u)); 1294fb0a5ebSJens Wiklander break; 1304fb0a5ebSJens Wiklander case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: 1314fb0a5ebSJens Wiklander case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: 1324fb0a5ebSJens Wiklander case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 1334fb0a5ebSJens Wiklander mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - 1344fb0a5ebSJens Wiklander TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; 1354fb0a5ebSJens Wiklander mp->u.value.a = p->u.value.a; 1364fb0a5ebSJens Wiklander mp->u.value.b = p->u.value.b; 1374fb0a5ebSJens Wiklander mp->u.value.c = p->u.value.c; 1384fb0a5ebSJens Wiklander break; 1394fb0a5ebSJens Wiklander case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: 1404fb0a5ebSJens Wiklander case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 1414fb0a5ebSJens Wiklander case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 1424fb0a5ebSJens Wiklander mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + 1434fb0a5ebSJens Wiklander p->attr - 1444fb0a5ebSJens Wiklander TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; 1454fb0a5ebSJens Wiklander mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; 1464fb0a5ebSJens Wiklander mp->u.tmem.size = p->u.memref.size; 1474fb0a5ebSJens Wiklander if (!p->u.memref.shm) { 1484fb0a5ebSJens Wiklander mp->u.tmem.buf_ptr = 0; 1494fb0a5ebSJens Wiklander break; 1504fb0a5ebSJens Wiklander } 1514fb0a5ebSJens Wiklander rc = tee_shm_get_pa(p->u.memref.shm, 1524fb0a5ebSJens Wiklander p->u.memref.shm_offs, &pa); 1534fb0a5ebSJens Wiklander if (rc) 1544fb0a5ebSJens Wiklander return rc; 1554fb0a5ebSJens Wiklander mp->u.tmem.buf_ptr = pa; 1564fb0a5ebSJens Wiklander mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << 1574fb0a5ebSJens Wiklander OPTEE_MSG_ATTR_CACHE_SHIFT; 1584fb0a5ebSJens Wiklander break; 1594fb0a5ebSJens Wiklander default: 1604fb0a5ebSJens Wiklander return -EINVAL; 1614fb0a5ebSJens Wiklander } 1624fb0a5ebSJens Wiklander } 1634fb0a5ebSJens Wiklander return 0; 1644fb0a5ebSJens Wiklander } 1654fb0a5ebSJens Wiklander 1664fb0a5ebSJens Wiklander static void optee_get_version(struct tee_device *teedev, 1674fb0a5ebSJens Wiklander struct tee_ioctl_version_data *vers) 1684fb0a5ebSJens Wiklander { 1694fb0a5ebSJens Wiklander struct tee_ioctl_version_data v = { 1704fb0a5ebSJens Wiklander .impl_id = TEE_IMPL_ID_OPTEE, 1714fb0a5ebSJens Wiklander .impl_caps = TEE_OPTEE_CAP_TZ, 1724fb0a5ebSJens Wiklander .gen_caps = TEE_GEN_CAP_GP, 1734fb0a5ebSJens Wiklander }; 1744fb0a5ebSJens Wiklander *vers = v; 1754fb0a5ebSJens Wiklander } 1764fb0a5ebSJens Wiklander 1774fb0a5ebSJens Wiklander static int optee_open(struct tee_context *ctx) 1784fb0a5ebSJens Wiklander { 1794fb0a5ebSJens Wiklander struct optee_context_data *ctxdata; 1804fb0a5ebSJens Wiklander struct tee_device *teedev = ctx->teedev; 1814fb0a5ebSJens Wiklander struct optee *optee = tee_get_drvdata(teedev); 1824fb0a5ebSJens Wiklander 1834fb0a5ebSJens Wiklander ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); 1844fb0a5ebSJens Wiklander if (!ctxdata) 1854fb0a5ebSJens Wiklander return -ENOMEM; 1864fb0a5ebSJens Wiklander 1874fb0a5ebSJens Wiklander if (teedev == optee->supp_teedev) { 1884fb0a5ebSJens Wiklander bool busy = true; 1894fb0a5ebSJens Wiklander 1904fb0a5ebSJens Wiklander mutex_lock(&optee->supp.ctx_mutex); 1914fb0a5ebSJens Wiklander if (!optee->supp.ctx) { 1924fb0a5ebSJens Wiklander busy = false; 1934fb0a5ebSJens Wiklander optee->supp.ctx = ctx; 1944fb0a5ebSJens Wiklander } 1954fb0a5ebSJens Wiklander mutex_unlock(&optee->supp.ctx_mutex); 1964fb0a5ebSJens Wiklander if (busy) { 1974fb0a5ebSJens Wiklander kfree(ctxdata); 1984fb0a5ebSJens Wiklander return -EBUSY; 1994fb0a5ebSJens Wiklander } 2004fb0a5ebSJens Wiklander } 2014fb0a5ebSJens Wiklander 2024fb0a5ebSJens Wiklander mutex_init(&ctxdata->mutex); 2034fb0a5ebSJens Wiklander INIT_LIST_HEAD(&ctxdata->sess_list); 2044fb0a5ebSJens Wiklander 2054fb0a5ebSJens Wiklander ctx->data = ctxdata; 2064fb0a5ebSJens Wiklander return 0; 2074fb0a5ebSJens Wiklander } 2084fb0a5ebSJens Wiklander 2094fb0a5ebSJens Wiklander static void optee_release(struct tee_context *ctx) 2104fb0a5ebSJens Wiklander { 2114fb0a5ebSJens Wiklander struct optee_context_data *ctxdata = ctx->data; 2124fb0a5ebSJens Wiklander struct tee_device *teedev = ctx->teedev; 2134fb0a5ebSJens Wiklander struct optee *optee = tee_get_drvdata(teedev); 2144fb0a5ebSJens Wiklander struct tee_shm *shm; 2154fb0a5ebSJens Wiklander struct optee_msg_arg *arg = NULL; 2164fb0a5ebSJens Wiklander phys_addr_t parg; 2174fb0a5ebSJens Wiklander struct optee_session *sess; 2184fb0a5ebSJens Wiklander struct optee_session *sess_tmp; 2194fb0a5ebSJens Wiklander 2204fb0a5ebSJens Wiklander if (!ctxdata) 2214fb0a5ebSJens Wiklander return; 2224fb0a5ebSJens Wiklander 2234fb0a5ebSJens Wiklander shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED); 2244fb0a5ebSJens Wiklander if (!IS_ERR(shm)) { 2254fb0a5ebSJens Wiklander arg = tee_shm_get_va(shm, 0); 2264fb0a5ebSJens Wiklander /* 2274fb0a5ebSJens Wiklander * If va2pa fails for some reason, we can't call 2284fb0a5ebSJens Wiklander * optee_close_session(), only free the memory. Secure OS 2294fb0a5ebSJens Wiklander * will leak sessions and finally refuse more sessions, but 2304fb0a5ebSJens Wiklander * we will at least let normal world reclaim its memory. 2314fb0a5ebSJens Wiklander */ 2324fb0a5ebSJens Wiklander if (!IS_ERR(arg)) 2334fb0a5ebSJens Wiklander tee_shm_va2pa(shm, arg, &parg); 2344fb0a5ebSJens Wiklander } 2354fb0a5ebSJens Wiklander 2364fb0a5ebSJens Wiklander list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, 2374fb0a5ebSJens Wiklander list_node) { 2384fb0a5ebSJens Wiklander list_del(&sess->list_node); 2394fb0a5ebSJens Wiklander if (!IS_ERR_OR_NULL(arg)) { 2404fb0a5ebSJens Wiklander memset(arg, 0, sizeof(*arg)); 2414fb0a5ebSJens Wiklander arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; 2424fb0a5ebSJens Wiklander arg->session = sess->session_id; 2434fb0a5ebSJens Wiklander optee_do_call_with_arg(ctx, parg); 2444fb0a5ebSJens Wiklander } 2454fb0a5ebSJens Wiklander kfree(sess); 2464fb0a5ebSJens Wiklander } 2474fb0a5ebSJens Wiklander kfree(ctxdata); 2484fb0a5ebSJens Wiklander 2494fb0a5ebSJens Wiklander if (!IS_ERR(shm)) 2504fb0a5ebSJens Wiklander tee_shm_free(shm); 2514fb0a5ebSJens Wiklander 2524fb0a5ebSJens Wiklander ctx->data = NULL; 2534fb0a5ebSJens Wiklander 2544fb0a5ebSJens Wiklander if (teedev == optee->supp_teedev) { 2554fb0a5ebSJens Wiklander mutex_lock(&optee->supp.ctx_mutex); 2564fb0a5ebSJens Wiklander optee->supp.ctx = NULL; 2574fb0a5ebSJens Wiklander mutex_unlock(&optee->supp.ctx_mutex); 2584fb0a5ebSJens Wiklander } 2594fb0a5ebSJens Wiklander } 2604fb0a5ebSJens Wiklander 2614fb0a5ebSJens Wiklander static struct tee_driver_ops optee_ops = { 2624fb0a5ebSJens Wiklander .get_version = optee_get_version, 2634fb0a5ebSJens Wiklander .open = optee_open, 2644fb0a5ebSJens Wiklander .release = optee_release, 2654fb0a5ebSJens Wiklander .open_session = optee_open_session, 2664fb0a5ebSJens Wiklander .close_session = optee_close_session, 2674fb0a5ebSJens Wiklander .invoke_func = optee_invoke_func, 2684fb0a5ebSJens Wiklander .cancel_req = optee_cancel_req, 2694fb0a5ebSJens Wiklander }; 2704fb0a5ebSJens Wiklander 2714fb0a5ebSJens Wiklander static struct tee_desc optee_desc = { 2724fb0a5ebSJens Wiklander .name = DRIVER_NAME "-clnt", 2734fb0a5ebSJens Wiklander .ops = &optee_ops, 2744fb0a5ebSJens Wiklander .owner = THIS_MODULE, 2754fb0a5ebSJens Wiklander }; 2764fb0a5ebSJens Wiklander 2774fb0a5ebSJens Wiklander static struct tee_driver_ops optee_supp_ops = { 2784fb0a5ebSJens Wiklander .get_version = optee_get_version, 2794fb0a5ebSJens Wiklander .open = optee_open, 2804fb0a5ebSJens Wiklander .release = optee_release, 2814fb0a5ebSJens Wiklander .supp_recv = optee_supp_recv, 2824fb0a5ebSJens Wiklander .supp_send = optee_supp_send, 2834fb0a5ebSJens Wiklander }; 2844fb0a5ebSJens Wiklander 2854fb0a5ebSJens Wiklander static struct tee_desc optee_supp_desc = { 2864fb0a5ebSJens Wiklander .name = DRIVER_NAME "-supp", 2874fb0a5ebSJens Wiklander .ops = &optee_supp_ops, 2884fb0a5ebSJens Wiklander .owner = THIS_MODULE, 2894fb0a5ebSJens Wiklander .flags = TEE_DESC_PRIVILEGED, 2904fb0a5ebSJens Wiklander }; 2914fb0a5ebSJens Wiklander 2924fb0a5ebSJens Wiklander static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) 2934fb0a5ebSJens Wiklander { 2944fb0a5ebSJens Wiklander struct arm_smccc_res res; 2954fb0a5ebSJens Wiklander 2964fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); 2974fb0a5ebSJens Wiklander 2984fb0a5ebSJens Wiklander if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && 2994fb0a5ebSJens Wiklander res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) 3004fb0a5ebSJens Wiklander return true; 3014fb0a5ebSJens Wiklander return false; 3024fb0a5ebSJens Wiklander } 3034fb0a5ebSJens Wiklander 3044fb0a5ebSJens Wiklander static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) 3054fb0a5ebSJens Wiklander { 3064fb0a5ebSJens Wiklander union { 3074fb0a5ebSJens Wiklander struct arm_smccc_res smccc; 3084fb0a5ebSJens Wiklander struct optee_smc_calls_revision_result result; 3094fb0a5ebSJens Wiklander } res; 3104fb0a5ebSJens Wiklander 3114fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 3124fb0a5ebSJens Wiklander 3134fb0a5ebSJens Wiklander if (res.result.major == OPTEE_MSG_REVISION_MAJOR && 3144fb0a5ebSJens Wiklander (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) 3154fb0a5ebSJens Wiklander return true; 3164fb0a5ebSJens Wiklander return false; 3174fb0a5ebSJens Wiklander } 3184fb0a5ebSJens Wiklander 3194fb0a5ebSJens Wiklander static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, 3204fb0a5ebSJens Wiklander u32 *sec_caps) 3214fb0a5ebSJens Wiklander { 3224fb0a5ebSJens Wiklander union { 3234fb0a5ebSJens Wiklander struct arm_smccc_res smccc; 3244fb0a5ebSJens Wiklander struct optee_smc_exchange_capabilities_result result; 3254fb0a5ebSJens Wiklander } res; 3264fb0a5ebSJens Wiklander u32 a1 = 0; 3274fb0a5ebSJens Wiklander 3284fb0a5ebSJens Wiklander /* 3294fb0a5ebSJens Wiklander * TODO This isn't enough to tell if it's UP system (from kernel 3304fb0a5ebSJens Wiklander * point of view) or not, is_smp() returns the the information 3314fb0a5ebSJens Wiklander * needed, but can't be called directly from here. 3324fb0a5ebSJens Wiklander */ 3334fb0a5ebSJens Wiklander if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) 3344fb0a5ebSJens Wiklander a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; 3354fb0a5ebSJens Wiklander 3364fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, 3374fb0a5ebSJens Wiklander &res.smccc); 3384fb0a5ebSJens Wiklander 3394fb0a5ebSJens Wiklander if (res.result.status != OPTEE_SMC_RETURN_OK) 3404fb0a5ebSJens Wiklander return false; 3414fb0a5ebSJens Wiklander 3424fb0a5ebSJens Wiklander *sec_caps = res.result.capabilities; 3434fb0a5ebSJens Wiklander return true; 3444fb0a5ebSJens Wiklander } 3454fb0a5ebSJens Wiklander 3464fb0a5ebSJens Wiklander static struct tee_shm_pool * 3474fb0a5ebSJens Wiklander optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) 3484fb0a5ebSJens Wiklander { 3494fb0a5ebSJens Wiklander union { 3504fb0a5ebSJens Wiklander struct arm_smccc_res smccc; 3514fb0a5ebSJens Wiklander struct optee_smc_get_shm_config_result result; 3524fb0a5ebSJens Wiklander } res; 3534fb0a5ebSJens Wiklander struct tee_shm_pool *pool; 3544fb0a5ebSJens Wiklander unsigned long vaddr; 3554fb0a5ebSJens Wiklander phys_addr_t paddr; 3564fb0a5ebSJens Wiklander size_t size; 3574fb0a5ebSJens Wiklander phys_addr_t begin; 3584fb0a5ebSJens Wiklander phys_addr_t end; 3594fb0a5ebSJens Wiklander void *va; 3604fb0a5ebSJens Wiklander struct tee_shm_pool_mem_info priv_info; 3614fb0a5ebSJens Wiklander struct tee_shm_pool_mem_info dmabuf_info; 3624fb0a5ebSJens Wiklander 3634fb0a5ebSJens Wiklander invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 3644fb0a5ebSJens Wiklander if (res.result.status != OPTEE_SMC_RETURN_OK) { 3654fb0a5ebSJens Wiklander pr_info("shm service not available\n"); 3664fb0a5ebSJens Wiklander return ERR_PTR(-ENOENT); 3674fb0a5ebSJens Wiklander } 3684fb0a5ebSJens Wiklander 3694fb0a5ebSJens Wiklander if (res.result.settings != OPTEE_SMC_SHM_CACHED) { 3704fb0a5ebSJens Wiklander pr_err("only normal cached shared memory supported\n"); 3714fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 3724fb0a5ebSJens Wiklander } 3734fb0a5ebSJens Wiklander 3744fb0a5ebSJens Wiklander begin = roundup(res.result.start, PAGE_SIZE); 3754fb0a5ebSJens Wiklander end = rounddown(res.result.start + res.result.size, PAGE_SIZE); 3764fb0a5ebSJens Wiklander paddr = begin; 3774fb0a5ebSJens Wiklander size = end - begin; 3784fb0a5ebSJens Wiklander 3794fb0a5ebSJens Wiklander if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { 3804fb0a5ebSJens Wiklander pr_err("too small shared memory area\n"); 3814fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 3824fb0a5ebSJens Wiklander } 3834fb0a5ebSJens Wiklander 3844fb0a5ebSJens Wiklander va = memremap(paddr, size, MEMREMAP_WB); 3854fb0a5ebSJens Wiklander if (!va) { 3864fb0a5ebSJens Wiklander pr_err("shared memory ioremap failed\n"); 3874fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 3884fb0a5ebSJens Wiklander } 3894fb0a5ebSJens Wiklander vaddr = (unsigned long)va; 3904fb0a5ebSJens Wiklander 3914fb0a5ebSJens Wiklander priv_info.vaddr = vaddr; 3924fb0a5ebSJens Wiklander priv_info.paddr = paddr; 3934fb0a5ebSJens Wiklander priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; 3944fb0a5ebSJens Wiklander dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; 3954fb0a5ebSJens Wiklander dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; 3964fb0a5ebSJens Wiklander dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; 3974fb0a5ebSJens Wiklander 3984fb0a5ebSJens Wiklander pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info); 3994fb0a5ebSJens Wiklander if (IS_ERR(pool)) { 4004fb0a5ebSJens Wiklander memunmap(va); 4014fb0a5ebSJens Wiklander goto out; 4024fb0a5ebSJens Wiklander } 4034fb0a5ebSJens Wiklander 4044fb0a5ebSJens Wiklander *memremaped_shm = va; 4054fb0a5ebSJens Wiklander out: 4064fb0a5ebSJens Wiklander return pool; 4074fb0a5ebSJens Wiklander } 4084fb0a5ebSJens Wiklander 4094fb0a5ebSJens Wiklander /* Simple wrapper functions to be able to use a function pointer */ 4104fb0a5ebSJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1, 4114fb0a5ebSJens Wiklander unsigned long a2, unsigned long a3, 4124fb0a5ebSJens Wiklander unsigned long a4, unsigned long a5, 4134fb0a5ebSJens Wiklander unsigned long a6, unsigned long a7, 4144fb0a5ebSJens Wiklander struct arm_smccc_res *res) 4154fb0a5ebSJens Wiklander { 4164fb0a5ebSJens Wiklander arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); 4174fb0a5ebSJens Wiklander } 4184fb0a5ebSJens Wiklander 4194fb0a5ebSJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1, 4204fb0a5ebSJens Wiklander unsigned long a2, unsigned long a3, 4214fb0a5ebSJens Wiklander unsigned long a4, unsigned long a5, 4224fb0a5ebSJens Wiklander unsigned long a6, unsigned long a7, 4234fb0a5ebSJens Wiklander struct arm_smccc_res *res) 4244fb0a5ebSJens Wiklander { 4254fb0a5ebSJens Wiklander arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); 4264fb0a5ebSJens Wiklander } 4274fb0a5ebSJens Wiklander 4284fb0a5ebSJens Wiklander static optee_invoke_fn *get_invoke_func(struct device_node *np) 4294fb0a5ebSJens Wiklander { 4304fb0a5ebSJens Wiklander const char *method; 4314fb0a5ebSJens Wiklander 4324fb0a5ebSJens Wiklander pr_info("probing for conduit method from DT.\n"); 4334fb0a5ebSJens Wiklander 4344fb0a5ebSJens Wiklander if (of_property_read_string(np, "method", &method)) { 4354fb0a5ebSJens Wiklander pr_warn("missing \"method\" property\n"); 4364fb0a5ebSJens Wiklander return ERR_PTR(-ENXIO); 4374fb0a5ebSJens Wiklander } 4384fb0a5ebSJens Wiklander 4394fb0a5ebSJens Wiklander if (!strcmp("hvc", method)) 4404fb0a5ebSJens Wiklander return optee_smccc_hvc; 4414fb0a5ebSJens Wiklander else if (!strcmp("smc", method)) 4424fb0a5ebSJens Wiklander return optee_smccc_smc; 4434fb0a5ebSJens Wiklander 4444fb0a5ebSJens Wiklander pr_warn("invalid \"method\" property: %s\n", method); 4454fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 4464fb0a5ebSJens Wiklander } 4474fb0a5ebSJens Wiklander 4484fb0a5ebSJens Wiklander static struct optee *optee_probe(struct device_node *np) 4494fb0a5ebSJens Wiklander { 4504fb0a5ebSJens Wiklander optee_invoke_fn *invoke_fn; 4514fb0a5ebSJens Wiklander struct tee_shm_pool *pool; 4524fb0a5ebSJens Wiklander struct optee *optee = NULL; 4534fb0a5ebSJens Wiklander void *memremaped_shm = NULL; 4544fb0a5ebSJens Wiklander struct tee_device *teedev; 4554fb0a5ebSJens Wiklander u32 sec_caps; 4564fb0a5ebSJens Wiklander int rc; 4574fb0a5ebSJens Wiklander 4584fb0a5ebSJens Wiklander invoke_fn = get_invoke_func(np); 4594fb0a5ebSJens Wiklander if (IS_ERR(invoke_fn)) 4604fb0a5ebSJens Wiklander return (void *)invoke_fn; 4614fb0a5ebSJens Wiklander 4624fb0a5ebSJens Wiklander if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { 4634fb0a5ebSJens Wiklander pr_warn("api uid mismatch\n"); 4644fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 4654fb0a5ebSJens Wiklander } 4664fb0a5ebSJens Wiklander 4674fb0a5ebSJens Wiklander if (!optee_msg_api_revision_is_compatible(invoke_fn)) { 4684fb0a5ebSJens Wiklander pr_warn("api revision mismatch\n"); 4694fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 4704fb0a5ebSJens Wiklander } 4714fb0a5ebSJens Wiklander 4724fb0a5ebSJens Wiklander if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { 4734fb0a5ebSJens Wiklander pr_warn("capabilities mismatch\n"); 4744fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 4754fb0a5ebSJens Wiklander } 4764fb0a5ebSJens Wiklander 4774fb0a5ebSJens Wiklander /* 4784fb0a5ebSJens Wiklander * We have no other option for shared memory, if secure world 4794fb0a5ebSJens Wiklander * doesn't have any reserved memory we can use we can't continue. 4804fb0a5ebSJens Wiklander */ 4814fb0a5ebSJens Wiklander if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) 4824fb0a5ebSJens Wiklander return ERR_PTR(-EINVAL); 4834fb0a5ebSJens Wiklander 4844fb0a5ebSJens Wiklander pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); 4854fb0a5ebSJens Wiklander if (IS_ERR(pool)) 4864fb0a5ebSJens Wiklander return (void *)pool; 4874fb0a5ebSJens Wiklander 4884fb0a5ebSJens Wiklander optee = kzalloc(sizeof(*optee), GFP_KERNEL); 4894fb0a5ebSJens Wiklander if (!optee) { 4904fb0a5ebSJens Wiklander rc = -ENOMEM; 4914fb0a5ebSJens Wiklander goto err; 4924fb0a5ebSJens Wiklander } 4934fb0a5ebSJens Wiklander 4944fb0a5ebSJens Wiklander optee->invoke_fn = invoke_fn; 4954fb0a5ebSJens Wiklander 4964fb0a5ebSJens Wiklander teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); 4974fb0a5ebSJens Wiklander if (IS_ERR(teedev)) { 4984fb0a5ebSJens Wiklander rc = PTR_ERR(teedev); 4994fb0a5ebSJens Wiklander goto err; 5004fb0a5ebSJens Wiklander } 5014fb0a5ebSJens Wiklander optee->teedev = teedev; 5024fb0a5ebSJens Wiklander 5034fb0a5ebSJens Wiklander teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); 5044fb0a5ebSJens Wiklander if (IS_ERR(teedev)) { 5054fb0a5ebSJens Wiklander rc = PTR_ERR(teedev); 5064fb0a5ebSJens Wiklander goto err; 5074fb0a5ebSJens Wiklander } 5084fb0a5ebSJens Wiklander optee->supp_teedev = teedev; 5094fb0a5ebSJens Wiklander 5104fb0a5ebSJens Wiklander rc = tee_device_register(optee->teedev); 5114fb0a5ebSJens Wiklander if (rc) 5124fb0a5ebSJens Wiklander goto err; 5134fb0a5ebSJens Wiklander 5144fb0a5ebSJens Wiklander rc = tee_device_register(optee->supp_teedev); 5154fb0a5ebSJens Wiklander if (rc) 5164fb0a5ebSJens Wiklander goto err; 5174fb0a5ebSJens Wiklander 5184fb0a5ebSJens Wiklander mutex_init(&optee->call_queue.mutex); 5194fb0a5ebSJens Wiklander INIT_LIST_HEAD(&optee->call_queue.waiters); 5204fb0a5ebSJens Wiklander optee_wait_queue_init(&optee->wait_queue); 5214fb0a5ebSJens Wiklander optee_supp_init(&optee->supp); 5224fb0a5ebSJens Wiklander optee->memremaped_shm = memremaped_shm; 5234fb0a5ebSJens Wiklander optee->pool = pool; 5244fb0a5ebSJens Wiklander 5254fb0a5ebSJens Wiklander optee_enable_shm_cache(optee); 5264fb0a5ebSJens Wiklander 5274fb0a5ebSJens Wiklander pr_info("initialized driver\n"); 5284fb0a5ebSJens Wiklander return optee; 5294fb0a5ebSJens Wiklander err: 5304fb0a5ebSJens Wiklander if (optee) { 5314fb0a5ebSJens Wiklander /* 5324fb0a5ebSJens Wiklander * tee_device_unregister() is safe to call even if the 5334fb0a5ebSJens Wiklander * devices hasn't been registered with 5344fb0a5ebSJens Wiklander * tee_device_register() yet. 5354fb0a5ebSJens Wiklander */ 5364fb0a5ebSJens Wiklander tee_device_unregister(optee->supp_teedev); 5374fb0a5ebSJens Wiklander tee_device_unregister(optee->teedev); 5384fb0a5ebSJens Wiklander kfree(optee); 5394fb0a5ebSJens Wiklander } 5404fb0a5ebSJens Wiklander if (pool) 5414fb0a5ebSJens Wiklander tee_shm_pool_free(pool); 5424fb0a5ebSJens Wiklander if (memremaped_shm) 5434fb0a5ebSJens Wiklander memunmap(memremaped_shm); 5444fb0a5ebSJens Wiklander return ERR_PTR(rc); 5454fb0a5ebSJens Wiklander } 5464fb0a5ebSJens Wiklander 5474fb0a5ebSJens Wiklander static void optee_remove(struct optee *optee) 5484fb0a5ebSJens Wiklander { 5494fb0a5ebSJens Wiklander /* 5504fb0a5ebSJens Wiklander * Ask OP-TEE to free all cached shared memory objects to decrease 5514fb0a5ebSJens Wiklander * reference counters and also avoid wild pointers in secure world 5524fb0a5ebSJens Wiklander * into the old shared memory range. 5534fb0a5ebSJens Wiklander */ 5544fb0a5ebSJens Wiklander optee_disable_shm_cache(optee); 5554fb0a5ebSJens Wiklander 5564fb0a5ebSJens Wiklander /* 5574fb0a5ebSJens Wiklander * The two devices has to be unregistered before we can free the 5584fb0a5ebSJens Wiklander * other resources. 5594fb0a5ebSJens Wiklander */ 5604fb0a5ebSJens Wiklander tee_device_unregister(optee->supp_teedev); 5614fb0a5ebSJens Wiklander tee_device_unregister(optee->teedev); 5624fb0a5ebSJens Wiklander 5634fb0a5ebSJens Wiklander tee_shm_pool_free(optee->pool); 5644fb0a5ebSJens Wiklander if (optee->memremaped_shm) 5654fb0a5ebSJens Wiklander memunmap(optee->memremaped_shm); 5664fb0a5ebSJens Wiklander optee_wait_queue_exit(&optee->wait_queue); 5674fb0a5ebSJens Wiklander optee_supp_uninit(&optee->supp); 5684fb0a5ebSJens Wiklander mutex_destroy(&optee->call_queue.mutex); 5694fb0a5ebSJens Wiklander 5704fb0a5ebSJens Wiklander kfree(optee); 5714fb0a5ebSJens Wiklander } 5724fb0a5ebSJens Wiklander 5734fb0a5ebSJens Wiklander static const struct of_device_id optee_match[] = { 5744fb0a5ebSJens Wiklander { .compatible = "linaro,optee-tz" }, 5754fb0a5ebSJens Wiklander {}, 5764fb0a5ebSJens Wiklander }; 5774fb0a5ebSJens Wiklander 5784fb0a5ebSJens Wiklander static struct optee *optee_svc; 5794fb0a5ebSJens Wiklander 5804fb0a5ebSJens Wiklander static int __init optee_driver_init(void) 5814fb0a5ebSJens Wiklander { 5824fb0a5ebSJens Wiklander struct device_node *fw_np; 5834fb0a5ebSJens Wiklander struct device_node *np; 5844fb0a5ebSJens Wiklander struct optee *optee; 5854fb0a5ebSJens Wiklander 5864fb0a5ebSJens Wiklander /* Node is supposed to be below /firmware */ 5874fb0a5ebSJens Wiklander fw_np = of_find_node_by_name(NULL, "firmware"); 5884fb0a5ebSJens Wiklander if (!fw_np) 5894fb0a5ebSJens Wiklander return -ENODEV; 5904fb0a5ebSJens Wiklander 5914fb0a5ebSJens Wiklander np = of_find_matching_node(fw_np, optee_match); 5924fb0a5ebSJens Wiklander of_node_put(fw_np); 5934fb0a5ebSJens Wiklander if (!np) 5944fb0a5ebSJens Wiklander return -ENODEV; 5954fb0a5ebSJens Wiklander 5964fb0a5ebSJens Wiklander optee = optee_probe(np); 5974fb0a5ebSJens Wiklander of_node_put(np); 5984fb0a5ebSJens Wiklander 5994fb0a5ebSJens Wiklander if (IS_ERR(optee)) 6004fb0a5ebSJens Wiklander return PTR_ERR(optee); 6014fb0a5ebSJens Wiklander 6024fb0a5ebSJens Wiklander optee_svc = optee; 6034fb0a5ebSJens Wiklander 6044fb0a5ebSJens Wiklander return 0; 6054fb0a5ebSJens Wiklander } 6064fb0a5ebSJens Wiklander module_init(optee_driver_init); 6074fb0a5ebSJens Wiklander 6084fb0a5ebSJens Wiklander static void __exit optee_driver_exit(void) 6094fb0a5ebSJens Wiklander { 6104fb0a5ebSJens Wiklander struct optee *optee = optee_svc; 6114fb0a5ebSJens Wiklander 6124fb0a5ebSJens Wiklander optee_svc = NULL; 6134fb0a5ebSJens Wiklander if (optee) 6144fb0a5ebSJens Wiklander optee_remove(optee); 6154fb0a5ebSJens Wiklander } 6164fb0a5ebSJens Wiklander module_exit(optee_driver_exit); 6174fb0a5ebSJens Wiklander 6184fb0a5ebSJens Wiklander MODULE_AUTHOR("Linaro"); 6194fb0a5ebSJens Wiklander MODULE_DESCRIPTION("OP-TEE driver"); 6204fb0a5ebSJens Wiklander MODULE_SUPPORTED_DEVICE(""); 6214fb0a5ebSJens Wiklander MODULE_VERSION("1.0"); 6224fb0a5ebSJens Wiklander MODULE_LICENSE("GPL v2"); 623