18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015, Linaro Limited 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 98c2ecf20Sopenharmony_ci#include <linux/crash_dump.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <linux/tee_drv.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci#include "optee_private.h" 238c2ecf20Sopenharmony_ci#include "optee_smc.h" 248c2ecf20Sopenharmony_ci#include "shm_pool.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DRIVER_NAME "optee" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/** 318c2ecf20Sopenharmony_ci * optee_from_msg_param() - convert from OPTEE_MSG parameters to 328c2ecf20Sopenharmony_ci * struct tee_param 338c2ecf20Sopenharmony_ci * @params: subsystem internal parameter representation 348c2ecf20Sopenharmony_ci * @num_params: number of elements in the parameter arrays 358c2ecf20Sopenharmony_ci * @msg_params: OPTEE_MSG parameters 368c2ecf20Sopenharmony_ci * Returns 0 on success or <0 on failure 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ciint optee_from_msg_param(struct tee_param *params, size_t num_params, 398c2ecf20Sopenharmony_ci const struct optee_msg_param *msg_params) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int rc; 428c2ecf20Sopenharmony_ci size_t n; 438c2ecf20Sopenharmony_ci struct tee_shm *shm; 448c2ecf20Sopenharmony_ci phys_addr_t pa; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci for (n = 0; n < num_params; n++) { 478c2ecf20Sopenharmony_ci struct tee_param *p = params + n; 488c2ecf20Sopenharmony_ci const struct optee_msg_param *mp = msg_params + n; 498c2ecf20Sopenharmony_ci u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci switch (attr) { 528c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_NONE: 538c2ecf20Sopenharmony_ci p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 548c2ecf20Sopenharmony_ci memset(&p->u, 0, sizeof(p->u)); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: 578c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: 588c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: 598c2ecf20Sopenharmony_ci p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + 608c2ecf20Sopenharmony_ci attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; 618c2ecf20Sopenharmony_ci p->u.value.a = mp->u.value.a; 628c2ecf20Sopenharmony_ci p->u.value.b = mp->u.value.b; 638c2ecf20Sopenharmony_ci p->u.value.c = mp->u.value.c; 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: 668c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: 678c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: 688c2ecf20Sopenharmony_ci p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + 698c2ecf20Sopenharmony_ci attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; 708c2ecf20Sopenharmony_ci p->u.memref.size = mp->u.tmem.size; 718c2ecf20Sopenharmony_ci shm = (struct tee_shm *)(unsigned long) 728c2ecf20Sopenharmony_ci mp->u.tmem.shm_ref; 738c2ecf20Sopenharmony_ci if (!shm) { 748c2ecf20Sopenharmony_ci p->u.memref.shm_offs = 0; 758c2ecf20Sopenharmony_ci p->u.memref.shm = NULL; 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci rc = tee_shm_get_pa(shm, 0, &pa); 798c2ecf20Sopenharmony_ci if (rc) 808c2ecf20Sopenharmony_ci return rc; 818c2ecf20Sopenharmony_ci p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; 828c2ecf20Sopenharmony_ci p->u.memref.shm = shm; 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: 858c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: 868c2ecf20Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: 878c2ecf20Sopenharmony_ci p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + 888c2ecf20Sopenharmony_ci attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; 898c2ecf20Sopenharmony_ci p->u.memref.size = mp->u.rmem.size; 908c2ecf20Sopenharmony_ci shm = (struct tee_shm *)(unsigned long) 918c2ecf20Sopenharmony_ci mp->u.rmem.shm_ref; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!shm) { 948c2ecf20Sopenharmony_ci p->u.memref.shm_offs = 0; 958c2ecf20Sopenharmony_ci p->u.memref.shm = NULL; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci p->u.memref.shm_offs = mp->u.rmem.offs; 998c2ecf20Sopenharmony_ci p->u.memref.shm = shm; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci default: 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int to_msg_param_tmp_mem(struct optee_msg_param *mp, 1118c2ecf20Sopenharmony_ci const struct tee_param *p) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci int rc; 1148c2ecf20Sopenharmony_ci phys_addr_t pa; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - 1178c2ecf20Sopenharmony_ci TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; 1208c2ecf20Sopenharmony_ci mp->u.tmem.size = p->u.memref.size; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (!p->u.memref.shm) { 1238c2ecf20Sopenharmony_ci mp->u.tmem.buf_ptr = 0; 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa); 1288c2ecf20Sopenharmony_ci if (rc) 1298c2ecf20Sopenharmony_ci return rc; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci mp->u.tmem.buf_ptr = pa; 1328c2ecf20Sopenharmony_ci mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << 1338c2ecf20Sopenharmony_ci OPTEE_MSG_ATTR_CACHE_SHIFT; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int to_msg_param_reg_mem(struct optee_msg_param *mp, 1398c2ecf20Sopenharmony_ci const struct tee_param *p) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - 1428c2ecf20Sopenharmony_ci TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm; 1458c2ecf20Sopenharmony_ci mp->u.rmem.size = p->u.memref.size; 1468c2ecf20Sopenharmony_ci mp->u.rmem.offs = p->u.memref.shm_offs; 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters 1528c2ecf20Sopenharmony_ci * @msg_params: OPTEE_MSG parameters 1538c2ecf20Sopenharmony_ci * @num_params: number of elements in the parameter arrays 1548c2ecf20Sopenharmony_ci * @params: subsystem itnernal parameter representation 1558c2ecf20Sopenharmony_ci * Returns 0 on success or <0 on failure 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ciint optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, 1588c2ecf20Sopenharmony_ci const struct tee_param *params) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int rc; 1618c2ecf20Sopenharmony_ci size_t n; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci for (n = 0; n < num_params; n++) { 1648c2ecf20Sopenharmony_ci const struct tee_param *p = params + n; 1658c2ecf20Sopenharmony_ci struct optee_msg_param *mp = msg_params + n; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci switch (p->attr) { 1688c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: 1698c2ecf20Sopenharmony_ci mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 1708c2ecf20Sopenharmony_ci memset(&mp->u, 0, sizeof(mp->u)); 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: 1738c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: 1748c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 1758c2ecf20Sopenharmony_ci mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - 1768c2ecf20Sopenharmony_ci TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; 1778c2ecf20Sopenharmony_ci mp->u.value.a = p->u.value.a; 1788c2ecf20Sopenharmony_ci mp->u.value.b = p->u.value.b; 1798c2ecf20Sopenharmony_ci mp->u.value.c = p->u.value.c; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: 1828c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 1838c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 1848c2ecf20Sopenharmony_ci if (tee_shm_is_registered(p->u.memref.shm)) 1858c2ecf20Sopenharmony_ci rc = to_msg_param_reg_mem(mp, p); 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci rc = to_msg_param_tmp_mem(mp, p); 1888c2ecf20Sopenharmony_ci if (rc) 1898c2ecf20Sopenharmony_ci return rc; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci default: 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void optee_get_version(struct tee_device *teedev, 1998c2ecf20Sopenharmony_ci struct tee_ioctl_version_data *vers) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct tee_ioctl_version_data v = { 2028c2ecf20Sopenharmony_ci .impl_id = TEE_IMPL_ID_OPTEE, 2038c2ecf20Sopenharmony_ci .impl_caps = TEE_OPTEE_CAP_TZ, 2048c2ecf20Sopenharmony_ci .gen_caps = TEE_GEN_CAP_GP, 2058c2ecf20Sopenharmony_ci }; 2068c2ecf20Sopenharmony_ci struct optee *optee = tee_get_drvdata(teedev); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) 2098c2ecf20Sopenharmony_ci v.gen_caps |= TEE_GEN_CAP_REG_MEM; 2108c2ecf20Sopenharmony_ci if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) 2118c2ecf20Sopenharmony_ci v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; 2128c2ecf20Sopenharmony_ci *vers = v; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void optee_bus_scan(struct work_struct *work) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int optee_open(struct tee_context *ctx) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct optee_context_data *ctxdata; 2238c2ecf20Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 2248c2ecf20Sopenharmony_ci struct optee *optee = tee_get_drvdata(teedev); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); 2278c2ecf20Sopenharmony_ci if (!ctxdata) 2288c2ecf20Sopenharmony_ci return -ENOMEM; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (teedev == optee->supp_teedev) { 2318c2ecf20Sopenharmony_ci bool busy = true; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci mutex_lock(&optee->supp.mutex); 2348c2ecf20Sopenharmony_ci if (!optee->supp.ctx) { 2358c2ecf20Sopenharmony_ci busy = false; 2368c2ecf20Sopenharmony_ci optee->supp.ctx = ctx; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci mutex_unlock(&optee->supp.mutex); 2398c2ecf20Sopenharmony_ci if (busy) { 2408c2ecf20Sopenharmony_ci kfree(ctxdata); 2418c2ecf20Sopenharmony_ci return -EBUSY; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (!optee->scan_bus_done) { 2458c2ecf20Sopenharmony_ci INIT_WORK(&optee->scan_bus_work, optee_bus_scan); 2468c2ecf20Sopenharmony_ci optee->scan_bus_wq = create_workqueue("optee_bus_scan"); 2478c2ecf20Sopenharmony_ci if (!optee->scan_bus_wq) { 2488c2ecf20Sopenharmony_ci kfree(ctxdata); 2498c2ecf20Sopenharmony_ci return -ECHILD; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci queue_work(optee->scan_bus_wq, &optee->scan_bus_work); 2528c2ecf20Sopenharmony_ci optee->scan_bus_done = true; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci mutex_init(&ctxdata->mutex); 2568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ctxdata->sess_list); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) 2598c2ecf20Sopenharmony_ci ctx->cap_memref_null = true; 2608c2ecf20Sopenharmony_ci else 2618c2ecf20Sopenharmony_ci ctx->cap_memref_null = false; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ctx->data = ctxdata; 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void optee_release(struct tee_context *ctx) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct optee_context_data *ctxdata = ctx->data; 2708c2ecf20Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 2718c2ecf20Sopenharmony_ci struct optee *optee = tee_get_drvdata(teedev); 2728c2ecf20Sopenharmony_ci struct tee_shm *shm; 2738c2ecf20Sopenharmony_ci struct optee_msg_arg *arg = NULL; 2748c2ecf20Sopenharmony_ci phys_addr_t parg; 2758c2ecf20Sopenharmony_ci struct optee_session *sess; 2768c2ecf20Sopenharmony_ci struct optee_session *sess_tmp; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!ctxdata) 2798c2ecf20Sopenharmony_ci return; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), 2828c2ecf20Sopenharmony_ci TEE_SHM_MAPPED | TEE_SHM_PRIV); 2838c2ecf20Sopenharmony_ci if (!IS_ERR(shm)) { 2848c2ecf20Sopenharmony_ci arg = tee_shm_get_va(shm, 0); 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * If va2pa fails for some reason, we can't call into 2878c2ecf20Sopenharmony_ci * secure world, only free the memory. Secure OS will leak 2888c2ecf20Sopenharmony_ci * sessions and finally refuse more sessions, but we will 2898c2ecf20Sopenharmony_ci * at least let normal world reclaim its memory. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci if (!IS_ERR(arg)) 2928c2ecf20Sopenharmony_ci if (tee_shm_va2pa(shm, arg, &parg)) 2938c2ecf20Sopenharmony_ci arg = NULL; /* prevent usage of parg below */ 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, 2978c2ecf20Sopenharmony_ci list_node) { 2988c2ecf20Sopenharmony_ci list_del(&sess->list_node); 2998c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(arg)) { 3008c2ecf20Sopenharmony_ci memset(arg, 0, sizeof(*arg)); 3018c2ecf20Sopenharmony_ci arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; 3028c2ecf20Sopenharmony_ci arg->session = sess->session_id; 3038c2ecf20Sopenharmony_ci optee_do_call_with_arg(ctx, parg); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci kfree(sess); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci kfree(ctxdata); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!IS_ERR(shm)) 3108c2ecf20Sopenharmony_ci tee_shm_free(shm); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ctx->data = NULL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (teedev == optee->supp_teedev) { 3158c2ecf20Sopenharmony_ci if (optee->scan_bus_wq) { 3168c2ecf20Sopenharmony_ci destroy_workqueue(optee->scan_bus_wq); 3178c2ecf20Sopenharmony_ci optee->scan_bus_wq = NULL; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci optee_supp_release(&optee->supp); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic const struct tee_driver_ops optee_ops = { 3248c2ecf20Sopenharmony_ci .get_version = optee_get_version, 3258c2ecf20Sopenharmony_ci .open = optee_open, 3268c2ecf20Sopenharmony_ci .release = optee_release, 3278c2ecf20Sopenharmony_ci .open_session = optee_open_session, 3288c2ecf20Sopenharmony_ci .close_session = optee_close_session, 3298c2ecf20Sopenharmony_ci .invoke_func = optee_invoke_func, 3308c2ecf20Sopenharmony_ci .cancel_req = optee_cancel_req, 3318c2ecf20Sopenharmony_ci .shm_register = optee_shm_register, 3328c2ecf20Sopenharmony_ci .shm_unregister = optee_shm_unregister, 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic const struct tee_desc optee_desc = { 3368c2ecf20Sopenharmony_ci .name = DRIVER_NAME "-clnt", 3378c2ecf20Sopenharmony_ci .ops = &optee_ops, 3388c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic const struct tee_driver_ops optee_supp_ops = { 3428c2ecf20Sopenharmony_ci .get_version = optee_get_version, 3438c2ecf20Sopenharmony_ci .open = optee_open, 3448c2ecf20Sopenharmony_ci .release = optee_release, 3458c2ecf20Sopenharmony_ci .supp_recv = optee_supp_recv, 3468c2ecf20Sopenharmony_ci .supp_send = optee_supp_send, 3478c2ecf20Sopenharmony_ci .shm_register = optee_shm_register_supp, 3488c2ecf20Sopenharmony_ci .shm_unregister = optee_shm_unregister_supp, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic const struct tee_desc optee_supp_desc = { 3528c2ecf20Sopenharmony_ci .name = DRIVER_NAME "-supp", 3538c2ecf20Sopenharmony_ci .ops = &optee_supp_ops, 3548c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3558c2ecf20Sopenharmony_ci .flags = TEE_DESC_PRIVILEGED, 3568c2ecf20Sopenharmony_ci}; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct arm_smccc_res res; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && 3658c2ecf20Sopenharmony_ci res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) 3668c2ecf20Sopenharmony_ci return true; 3678c2ecf20Sopenharmony_ci return false; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci union { 3738c2ecf20Sopenharmony_ci struct arm_smccc_res smccc; 3748c2ecf20Sopenharmony_ci struct optee_smc_call_get_os_revision_result result; 3758c2ecf20Sopenharmony_ci } res = { 3768c2ecf20Sopenharmony_ci .result = { 3778c2ecf20Sopenharmony_ci .build_id = 0 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci }; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, 3828c2ecf20Sopenharmony_ci &res.smccc); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (res.result.build_id) 3858c2ecf20Sopenharmony_ci pr_info("revision %lu.%lu (%08lx)", res.result.major, 3868c2ecf20Sopenharmony_ci res.result.minor, res.result.build_id); 3878c2ecf20Sopenharmony_ci else 3888c2ecf20Sopenharmony_ci pr_info("revision %lu.%lu", res.result.major, res.result.minor); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci union { 3948c2ecf20Sopenharmony_ci struct arm_smccc_res smccc; 3958c2ecf20Sopenharmony_ci struct optee_smc_calls_revision_result result; 3968c2ecf20Sopenharmony_ci } res; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (res.result.major == OPTEE_MSG_REVISION_MAJOR && 4018c2ecf20Sopenharmony_ci (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) 4028c2ecf20Sopenharmony_ci return true; 4038c2ecf20Sopenharmony_ci return false; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, 4078c2ecf20Sopenharmony_ci u32 *sec_caps) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci union { 4108c2ecf20Sopenharmony_ci struct arm_smccc_res smccc; 4118c2ecf20Sopenharmony_ci struct optee_smc_exchange_capabilities_result result; 4128c2ecf20Sopenharmony_ci } res; 4138c2ecf20Sopenharmony_ci u32 a1 = 0; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * TODO This isn't enough to tell if it's UP system (from kernel 4178c2ecf20Sopenharmony_ci * point of view) or not, is_smp() returns the the information 4188c2ecf20Sopenharmony_ci * needed, but can't be called directly from here. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) 4218c2ecf20Sopenharmony_ci a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, 4248c2ecf20Sopenharmony_ci &res.smccc); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (res.result.status != OPTEE_SMC_RETURN_OK) 4278c2ecf20Sopenharmony_ci return false; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci *sec_caps = res.result.capabilities; 4308c2ecf20Sopenharmony_ci return true; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic struct tee_shm_pool *optee_config_dyn_shm(void) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct tee_shm_pool_mgr *priv_mgr; 4368c2ecf20Sopenharmony_ci struct tee_shm_pool_mgr *dmabuf_mgr; 4378c2ecf20Sopenharmony_ci void *rc; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci rc = optee_shm_pool_alloc_pages(); 4408c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 4418c2ecf20Sopenharmony_ci return rc; 4428c2ecf20Sopenharmony_ci priv_mgr = rc; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci rc = optee_shm_pool_alloc_pages(); 4458c2ecf20Sopenharmony_ci if (IS_ERR(rc)) { 4468c2ecf20Sopenharmony_ci tee_shm_pool_mgr_destroy(priv_mgr); 4478c2ecf20Sopenharmony_ci return rc; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci dmabuf_mgr = rc; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); 4528c2ecf20Sopenharmony_ci if (IS_ERR(rc)) { 4538c2ecf20Sopenharmony_ci tee_shm_pool_mgr_destroy(priv_mgr); 4548c2ecf20Sopenharmony_ci tee_shm_pool_mgr_destroy(dmabuf_mgr); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return rc; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic struct tee_shm_pool * 4618c2ecf20Sopenharmony_cioptee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci union { 4648c2ecf20Sopenharmony_ci struct arm_smccc_res smccc; 4658c2ecf20Sopenharmony_ci struct optee_smc_get_shm_config_result result; 4668c2ecf20Sopenharmony_ci } res; 4678c2ecf20Sopenharmony_ci unsigned long vaddr; 4688c2ecf20Sopenharmony_ci phys_addr_t paddr; 4698c2ecf20Sopenharmony_ci size_t size; 4708c2ecf20Sopenharmony_ci phys_addr_t begin; 4718c2ecf20Sopenharmony_ci phys_addr_t end; 4728c2ecf20Sopenharmony_ci void *va; 4738c2ecf20Sopenharmony_ci struct tee_shm_pool_mgr *priv_mgr; 4748c2ecf20Sopenharmony_ci struct tee_shm_pool_mgr *dmabuf_mgr; 4758c2ecf20Sopenharmony_ci void *rc; 4768c2ecf20Sopenharmony_ci const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 4798c2ecf20Sopenharmony_ci if (res.result.status != OPTEE_SMC_RETURN_OK) { 4808c2ecf20Sopenharmony_ci pr_err("static shm service not available\n"); 4818c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (res.result.settings != OPTEE_SMC_SHM_CACHED) { 4858c2ecf20Sopenharmony_ci pr_err("only normal cached shared memory supported\n"); 4868c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci begin = roundup(res.result.start, PAGE_SIZE); 4908c2ecf20Sopenharmony_ci end = rounddown(res.result.start + res.result.size, PAGE_SIZE); 4918c2ecf20Sopenharmony_ci paddr = begin; 4928c2ecf20Sopenharmony_ci size = end - begin; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { 4958c2ecf20Sopenharmony_ci pr_err("too small shared memory area\n"); 4968c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci va = memremap(paddr, size, MEMREMAP_WB); 5008c2ecf20Sopenharmony_ci if (!va) { 5018c2ecf20Sopenharmony_ci pr_err("shared memory ioremap failed\n"); 5028c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci vaddr = (unsigned long)va; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, 5078c2ecf20Sopenharmony_ci 3 /* 8 bytes aligned */); 5088c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 5098c2ecf20Sopenharmony_ci goto err_memunmap; 5108c2ecf20Sopenharmony_ci priv_mgr = rc; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci vaddr += sz; 5138c2ecf20Sopenharmony_ci paddr += sz; 5148c2ecf20Sopenharmony_ci size -= sz; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); 5178c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 5188c2ecf20Sopenharmony_ci goto err_free_priv_mgr; 5198c2ecf20Sopenharmony_ci dmabuf_mgr = rc; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); 5228c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 5238c2ecf20Sopenharmony_ci goto err_free_dmabuf_mgr; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci *memremaped_shm = va; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return rc; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cierr_free_dmabuf_mgr: 5308c2ecf20Sopenharmony_ci tee_shm_pool_mgr_destroy(dmabuf_mgr); 5318c2ecf20Sopenharmony_cierr_free_priv_mgr: 5328c2ecf20Sopenharmony_ci tee_shm_pool_mgr_destroy(priv_mgr); 5338c2ecf20Sopenharmony_cierr_memunmap: 5348c2ecf20Sopenharmony_ci memunmap(va); 5358c2ecf20Sopenharmony_ci return rc; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci/* Simple wrapper functions to be able to use a function pointer */ 5398c2ecf20Sopenharmony_cistatic void optee_smccc_smc(unsigned long a0, unsigned long a1, 5408c2ecf20Sopenharmony_ci unsigned long a2, unsigned long a3, 5418c2ecf20Sopenharmony_ci unsigned long a4, unsigned long a5, 5428c2ecf20Sopenharmony_ci unsigned long a6, unsigned long a7, 5438c2ecf20Sopenharmony_ci struct arm_smccc_res *res) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic void optee_smccc_hvc(unsigned long a0, unsigned long a1, 5498c2ecf20Sopenharmony_ci unsigned long a2, unsigned long a3, 5508c2ecf20Sopenharmony_ci unsigned long a4, unsigned long a5, 5518c2ecf20Sopenharmony_ci unsigned long a6, unsigned long a7, 5528c2ecf20Sopenharmony_ci struct arm_smccc_res *res) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic optee_invoke_fn *get_invoke_func(struct device *dev) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci const char *method; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci pr_info("probing for conduit method.\n"); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (device_property_read_string(dev, "method", &method)) { 5648c2ecf20Sopenharmony_ci pr_warn("missing \"method\" property\n"); 5658c2ecf20Sopenharmony_ci return ERR_PTR(-ENXIO); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (!strcmp("hvc", method)) 5698c2ecf20Sopenharmony_ci return optee_smccc_hvc; 5708c2ecf20Sopenharmony_ci else if (!strcmp("smc", method)) 5718c2ecf20Sopenharmony_ci return optee_smccc_smc; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci pr_warn("invalid \"method\" property: %s\n", method); 5748c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/* optee_remove - Device Removal Routine 5788c2ecf20Sopenharmony_ci * @pdev: platform device information struct 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * optee_remove is called by platform subsystem to alert the driver 5818c2ecf20Sopenharmony_ci * that it should release the device 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic int optee_remove(struct platform_device *pdev) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci struct optee *optee = platform_get_drvdata(pdev); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* Unregister OP-TEE specific client devices on TEE bus */ 5898c2ecf20Sopenharmony_ci optee_unregister_devices(); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci teedev_close_context(optee->ctx); 5928c2ecf20Sopenharmony_ci /* 5938c2ecf20Sopenharmony_ci * Ask OP-TEE to free all cached shared memory objects to decrease 5948c2ecf20Sopenharmony_ci * reference counters and also avoid wild pointers in secure world 5958c2ecf20Sopenharmony_ci * into the old shared memory range. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_ci optee_disable_shm_cache(optee); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* 6008c2ecf20Sopenharmony_ci * The two devices have to be unregistered before we can free the 6018c2ecf20Sopenharmony_ci * other resources. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ci tee_device_unregister(optee->supp_teedev); 6048c2ecf20Sopenharmony_ci tee_device_unregister(optee->teedev); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci tee_shm_pool_free(optee->pool); 6078c2ecf20Sopenharmony_ci if (optee->memremaped_shm) 6088c2ecf20Sopenharmony_ci memunmap(optee->memremaped_shm); 6098c2ecf20Sopenharmony_ci optee_wait_queue_exit(&optee->wait_queue); 6108c2ecf20Sopenharmony_ci optee_supp_uninit(&optee->supp); 6118c2ecf20Sopenharmony_ci mutex_destroy(&optee->call_queue.mutex); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci kfree(optee); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/* optee_shutdown - Device Removal Routine 6198c2ecf20Sopenharmony_ci * @pdev: platform device information struct 6208c2ecf20Sopenharmony_ci * 6218c2ecf20Sopenharmony_ci * platform_shutdown is called by the platform subsystem to alert 6228c2ecf20Sopenharmony_ci * the driver that a shutdown, reboot, or kexec is happening and 6238c2ecf20Sopenharmony_ci * device must be disabled. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_cistatic void optee_shutdown(struct platform_device *pdev) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci optee_disable_shm_cache(platform_get_drvdata(pdev)); 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int optee_probe(struct platform_device *pdev) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci optee_invoke_fn *invoke_fn; 6338c2ecf20Sopenharmony_ci struct tee_shm_pool *pool = ERR_PTR(-EINVAL); 6348c2ecf20Sopenharmony_ci struct optee *optee = NULL; 6358c2ecf20Sopenharmony_ci void *memremaped_shm = NULL; 6368c2ecf20Sopenharmony_ci struct tee_device *teedev; 6378c2ecf20Sopenharmony_ci struct tee_context *ctx; 6388c2ecf20Sopenharmony_ci u32 sec_caps; 6398c2ecf20Sopenharmony_ci int rc; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* 6428c2ecf20Sopenharmony_ci * The kernel may have crashed at the same time that all available 6438c2ecf20Sopenharmony_ci * secure world threads were suspended and we cannot reschedule the 6448c2ecf20Sopenharmony_ci * suspended threads without access to the crashed kernel's wait_queue. 6458c2ecf20Sopenharmony_ci * Therefore, we cannot reliably initialize the OP-TEE driver in the 6468c2ecf20Sopenharmony_ci * kdump kernel. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ci if (is_kdump_kernel()) 6498c2ecf20Sopenharmony_ci return -ENODEV; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci invoke_fn = get_invoke_func(&pdev->dev); 6528c2ecf20Sopenharmony_ci if (IS_ERR(invoke_fn)) 6538c2ecf20Sopenharmony_ci return PTR_ERR(invoke_fn); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { 6568c2ecf20Sopenharmony_ci pr_warn("api uid mismatch\n"); 6578c2ecf20Sopenharmony_ci return -EINVAL; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci optee_msg_get_os_revision(invoke_fn); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (!optee_msg_api_revision_is_compatible(invoke_fn)) { 6638c2ecf20Sopenharmony_ci pr_warn("api revision mismatch\n"); 6648c2ecf20Sopenharmony_ci return -EINVAL; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { 6688c2ecf20Sopenharmony_ci pr_warn("capabilities mismatch\n"); 6698c2ecf20Sopenharmony_ci return -EINVAL; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* 6738c2ecf20Sopenharmony_ci * Try to use dynamic shared memory if possible 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_ci if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) 6768c2ecf20Sopenharmony_ci pool = optee_config_dyn_shm(); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* 6798c2ecf20Sopenharmony_ci * If dynamic shared memory is not available or failed - try static one 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) 6828c2ecf20Sopenharmony_ci pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (IS_ERR(pool)) 6858c2ecf20Sopenharmony_ci return PTR_ERR(pool); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci optee = kzalloc(sizeof(*optee), GFP_KERNEL); 6888c2ecf20Sopenharmony_ci if (!optee) { 6898c2ecf20Sopenharmony_ci rc = -ENOMEM; 6908c2ecf20Sopenharmony_ci goto err; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci optee->invoke_fn = invoke_fn; 6948c2ecf20Sopenharmony_ci optee->sec_caps = sec_caps; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); 6978c2ecf20Sopenharmony_ci if (IS_ERR(teedev)) { 6988c2ecf20Sopenharmony_ci rc = PTR_ERR(teedev); 6998c2ecf20Sopenharmony_ci goto err; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci optee->teedev = teedev; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); 7048c2ecf20Sopenharmony_ci if (IS_ERR(teedev)) { 7058c2ecf20Sopenharmony_ci rc = PTR_ERR(teedev); 7068c2ecf20Sopenharmony_ci goto err; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci optee->supp_teedev = teedev; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci rc = tee_device_register(optee->teedev); 7118c2ecf20Sopenharmony_ci if (rc) 7128c2ecf20Sopenharmony_ci goto err; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci rc = tee_device_register(optee->supp_teedev); 7158c2ecf20Sopenharmony_ci if (rc) 7168c2ecf20Sopenharmony_ci goto err; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci mutex_init(&optee->call_queue.mutex); 7198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&optee->call_queue.waiters); 7208c2ecf20Sopenharmony_ci optee_wait_queue_init(&optee->wait_queue); 7218c2ecf20Sopenharmony_ci optee_supp_init(&optee->supp); 7228c2ecf20Sopenharmony_ci optee->memremaped_shm = memremaped_shm; 7238c2ecf20Sopenharmony_ci optee->pool = pool; 7248c2ecf20Sopenharmony_ci ctx = teedev_open(optee->teedev); 7258c2ecf20Sopenharmony_ci if (IS_ERR(ctx)) { 7268c2ecf20Sopenharmony_ci rc = PTR_ERR(ctx); 7278c2ecf20Sopenharmony_ci goto err; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci optee->ctx = ctx; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * Ensure that there are no pre-existing shm objects before enabling 7338c2ecf20Sopenharmony_ci * the shm cache so that there's no chance of receiving an invalid 7348c2ecf20Sopenharmony_ci * address during shutdown. This could occur, for example, if we're 7358c2ecf20Sopenharmony_ci * kexec booting from an older kernel that did not properly cleanup the 7368c2ecf20Sopenharmony_ci * shm cache. 7378c2ecf20Sopenharmony_ci */ 7388c2ecf20Sopenharmony_ci optee_disable_unmapped_shm_cache(optee); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci optee_enable_shm_cache(optee); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) 7438c2ecf20Sopenharmony_ci pr_info("dynamic shared memory is enabled\n"); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, optee); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); 7488c2ecf20Sopenharmony_ci if (rc) { 7498c2ecf20Sopenharmony_ci optee_remove(pdev); 7508c2ecf20Sopenharmony_ci return rc; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci pr_info("initialized driver\n"); 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_cierr: 7568c2ecf20Sopenharmony_ci if (optee) { 7578c2ecf20Sopenharmony_ci /* 7588c2ecf20Sopenharmony_ci * tee_device_unregister() is safe to call even if the 7598c2ecf20Sopenharmony_ci * devices hasn't been registered with 7608c2ecf20Sopenharmony_ci * tee_device_register() yet. 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_ci tee_device_unregister(optee->supp_teedev); 7638c2ecf20Sopenharmony_ci tee_device_unregister(optee->teedev); 7648c2ecf20Sopenharmony_ci kfree(optee); 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci if (pool) 7678c2ecf20Sopenharmony_ci tee_shm_pool_free(pool); 7688c2ecf20Sopenharmony_ci if (memremaped_shm) 7698c2ecf20Sopenharmony_ci memunmap(memremaped_shm); 7708c2ecf20Sopenharmony_ci return rc; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic const struct of_device_id optee_dt_match[] = { 7748c2ecf20Sopenharmony_ci { .compatible = "linaro,optee-tz" }, 7758c2ecf20Sopenharmony_ci {}, 7768c2ecf20Sopenharmony_ci}; 7778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, optee_dt_match); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic struct platform_driver optee_driver = { 7808c2ecf20Sopenharmony_ci .probe = optee_probe, 7818c2ecf20Sopenharmony_ci .remove = optee_remove, 7828c2ecf20Sopenharmony_ci .shutdown = optee_shutdown, 7838c2ecf20Sopenharmony_ci .driver = { 7848c2ecf20Sopenharmony_ci .name = "optee", 7858c2ecf20Sopenharmony_ci .of_match_table = optee_dt_match, 7868c2ecf20Sopenharmony_ci }, 7878c2ecf20Sopenharmony_ci}; 7888c2ecf20Sopenharmony_cimodule_platform_driver(optee_driver); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linaro"); 7918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OP-TEE driver"); 7928c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE(""); 7938c2ecf20Sopenharmony_ciMODULE_VERSION("1.0"); 7948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7958c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:optee"); 796