162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015-2021, Linaro Limited 462306a36Sopenharmony_ci * Copyright (c) 2016, EPAM Systems 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/arm-smccc.h> 1062306a36Sopenharmony_ci#include <linux/cpuhotplug.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/firmware.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/irqdomain.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/mm.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/sched.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/string.h> 2662306a36Sopenharmony_ci#include <linux/tee_drv.h> 2762306a36Sopenharmony_ci#include <linux/types.h> 2862306a36Sopenharmony_ci#include <linux/workqueue.h> 2962306a36Sopenharmony_ci#include "optee_private.h" 3062306a36Sopenharmony_ci#include "optee_smc.h" 3162306a36Sopenharmony_ci#include "optee_rpc_cmd.h" 3262306a36Sopenharmony_ci#include <linux/kmemleak.h> 3362306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 3462306a36Sopenharmony_ci#include "optee_trace.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * This file implement the SMC ABI used when communicating with secure world 3862306a36Sopenharmony_ci * OP-TEE OS via raw SMCs. 3962306a36Sopenharmony_ci * This file is divided into the following sections: 4062306a36Sopenharmony_ci * 1. Convert between struct tee_param and struct optee_msg_param 4162306a36Sopenharmony_ci * 2. Low level support functions to register shared memory in secure world 4262306a36Sopenharmony_ci * 3. Dynamic shared memory pool based on alloc_pages() 4362306a36Sopenharmony_ci * 4. Do a normal scheduled call into secure world 4462306a36Sopenharmony_ci * 5. Asynchronous notification 4562306a36Sopenharmony_ci * 6. Driver initialization. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * A typical OP-TEE private shm allocation is 224 bytes (argument struct 5062306a36Sopenharmony_ci * with 6 parameters, needed for open session). So with an alignment of 512 5162306a36Sopenharmony_ci * we'll waste a bit more than 50%. However, it's only expected that we'll 5262306a36Sopenharmony_ci * have a handful of these structs allocated at a time. Most memory will 5362306a36Sopenharmony_ci * be allocated aligned to the page size, So all in all this should scale 5462306a36Sopenharmony_ci * up and down quite well. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci#define OPTEE_MIN_STATIC_POOL_ALIGN 9 /* 512 bytes aligned */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* SMC ABI considers at most a single TEE firmware */ 5962306a36Sopenharmony_cistatic unsigned int pcpu_irq_num; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int optee_cpuhp_enable_pcpu_irq(unsigned int cpu) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci enable_percpu_irq(pcpu_irq_num, IRQ_TYPE_NONE); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int optee_cpuhp_disable_pcpu_irq(unsigned int cpu) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci disable_percpu_irq(pcpu_irq_num); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * 1. Convert between struct tee_param and struct optee_msg_param 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * optee_from_msg_param() and optee_to_msg_param() are the main 7962306a36Sopenharmony_ci * functions. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int from_msg_param_tmp_mem(struct tee_param *p, u32 attr, 8362306a36Sopenharmony_ci const struct optee_msg_param *mp) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct tee_shm *shm; 8662306a36Sopenharmony_ci phys_addr_t pa; 8762306a36Sopenharmony_ci int rc; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + 9062306a36Sopenharmony_ci attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; 9162306a36Sopenharmony_ci p->u.memref.size = mp->u.tmem.size; 9262306a36Sopenharmony_ci shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref; 9362306a36Sopenharmony_ci if (!shm) { 9462306a36Sopenharmony_ci p->u.memref.shm_offs = 0; 9562306a36Sopenharmony_ci p->u.memref.shm = NULL; 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci rc = tee_shm_get_pa(shm, 0, &pa); 10062306a36Sopenharmony_ci if (rc) 10162306a36Sopenharmony_ci return rc; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; 10462306a36Sopenharmony_ci p->u.memref.shm = shm; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void from_msg_param_reg_mem(struct tee_param *p, u32 attr, 11062306a36Sopenharmony_ci const struct optee_msg_param *mp) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct tee_shm *shm; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + 11562306a36Sopenharmony_ci attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; 11662306a36Sopenharmony_ci p->u.memref.size = mp->u.rmem.size; 11762306a36Sopenharmony_ci shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (shm) { 12062306a36Sopenharmony_ci p->u.memref.shm_offs = mp->u.rmem.offs; 12162306a36Sopenharmony_ci p->u.memref.shm = shm; 12262306a36Sopenharmony_ci } else { 12362306a36Sopenharmony_ci p->u.memref.shm_offs = 0; 12462306a36Sopenharmony_ci p->u.memref.shm = NULL; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * optee_from_msg_param() - convert from OPTEE_MSG parameters to 13062306a36Sopenharmony_ci * struct tee_param 13162306a36Sopenharmony_ci * @optee: main service struct 13262306a36Sopenharmony_ci * @params: subsystem internal parameter representation 13362306a36Sopenharmony_ci * @num_params: number of elements in the parameter arrays 13462306a36Sopenharmony_ci * @msg_params: OPTEE_MSG parameters 13562306a36Sopenharmony_ci * Returns 0 on success or <0 on failure 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_cistatic int optee_from_msg_param(struct optee *optee, struct tee_param *params, 13862306a36Sopenharmony_ci size_t num_params, 13962306a36Sopenharmony_ci const struct optee_msg_param *msg_params) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci int rc; 14262306a36Sopenharmony_ci size_t n; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci for (n = 0; n < num_params; n++) { 14562306a36Sopenharmony_ci struct tee_param *p = params + n; 14662306a36Sopenharmony_ci const struct optee_msg_param *mp = msg_params + n; 14762306a36Sopenharmony_ci u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci switch (attr) { 15062306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_NONE: 15162306a36Sopenharmony_ci p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 15262306a36Sopenharmony_ci memset(&p->u, 0, sizeof(p->u)); 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: 15562306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: 15662306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: 15762306a36Sopenharmony_ci optee_from_msg_param_value(p, attr, mp); 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: 16062306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: 16162306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: 16262306a36Sopenharmony_ci rc = from_msg_param_tmp_mem(p, attr, mp); 16362306a36Sopenharmony_ci if (rc) 16462306a36Sopenharmony_ci return rc; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: 16762306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: 16862306a36Sopenharmony_ci case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: 16962306a36Sopenharmony_ci from_msg_param_reg_mem(p, attr, mp); 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci default: 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int to_msg_param_tmp_mem(struct optee_msg_param *mp, 18062306a36Sopenharmony_ci const struct tee_param *p) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int rc; 18362306a36Sopenharmony_ci phys_addr_t pa; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - 18662306a36Sopenharmony_ci TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; 18962306a36Sopenharmony_ci mp->u.tmem.size = p->u.memref.size; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (!p->u.memref.shm) { 19262306a36Sopenharmony_ci mp->u.tmem.buf_ptr = 0; 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa); 19762306a36Sopenharmony_ci if (rc) 19862306a36Sopenharmony_ci return rc; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mp->u.tmem.buf_ptr = pa; 20162306a36Sopenharmony_ci mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << 20262306a36Sopenharmony_ci OPTEE_MSG_ATTR_CACHE_SHIFT; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int to_msg_param_reg_mem(struct optee_msg_param *mp, 20862306a36Sopenharmony_ci const struct tee_param *p) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - 21162306a36Sopenharmony_ci TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm; 21462306a36Sopenharmony_ci mp->u.rmem.size = p->u.memref.size; 21562306a36Sopenharmony_ci mp->u.rmem.offs = p->u.memref.shm_offs; 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/** 22062306a36Sopenharmony_ci * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters 22162306a36Sopenharmony_ci * @optee: main service struct 22262306a36Sopenharmony_ci * @msg_params: OPTEE_MSG parameters 22362306a36Sopenharmony_ci * @num_params: number of elements in the parameter arrays 22462306a36Sopenharmony_ci * @params: subsystem itnernal parameter representation 22562306a36Sopenharmony_ci * Returns 0 on success or <0 on failure 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_cistatic int optee_to_msg_param(struct optee *optee, 22862306a36Sopenharmony_ci struct optee_msg_param *msg_params, 22962306a36Sopenharmony_ci size_t num_params, const struct tee_param *params) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int rc; 23262306a36Sopenharmony_ci size_t n; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci for (n = 0; n < num_params; n++) { 23562306a36Sopenharmony_ci const struct tee_param *p = params + n; 23662306a36Sopenharmony_ci struct optee_msg_param *mp = msg_params + n; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci switch (p->attr) { 23962306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: 24062306a36Sopenharmony_ci mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 24162306a36Sopenharmony_ci memset(&mp->u, 0, sizeof(mp->u)); 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: 24462306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: 24562306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 24662306a36Sopenharmony_ci optee_to_msg_param_value(mp, p); 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: 24962306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 25062306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 25162306a36Sopenharmony_ci if (tee_shm_is_dynamic(p->u.memref.shm)) 25262306a36Sopenharmony_ci rc = to_msg_param_reg_mem(mp, p); 25362306a36Sopenharmony_ci else 25462306a36Sopenharmony_ci rc = to_msg_param_tmp_mem(mp, p); 25562306a36Sopenharmony_ci if (rc) 25662306a36Sopenharmony_ci return rc; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci default: 25962306a36Sopenharmony_ci return -EINVAL; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * 2. Low level support functions to register shared memory in secure world 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * Functions to enable/disable shared memory caching in secure world, that 26962306a36Sopenharmony_ci * is, lazy freeing of previously allocated shared memory. Freeing is 27062306a36Sopenharmony_ci * performed when a request has been compled. 27162306a36Sopenharmony_ci * 27262306a36Sopenharmony_ci * Functions to register and unregister shared memory both for normal 27362306a36Sopenharmony_ci * clients and for tee-supplicant. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * optee_enable_shm_cache() - Enables caching of some shared memory allocation 27862306a36Sopenharmony_ci * in OP-TEE 27962306a36Sopenharmony_ci * @optee: main service struct 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistatic void optee_enable_shm_cache(struct optee *optee) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct optee_call_waiter w; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* We need to retry until secure world isn't busy. */ 28662306a36Sopenharmony_ci optee_cq_wait_init(&optee->call_queue, &w); 28762306a36Sopenharmony_ci while (true) { 28862306a36Sopenharmony_ci struct arm_smccc_res res; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci optee->smc.invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, 29162306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, &res); 29262306a36Sopenharmony_ci if (res.a0 == OPTEE_SMC_RETURN_OK) 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci optee_cq_wait_for_completion(&optee->call_queue, &w); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci optee_cq_wait_final(&optee->call_queue, &w); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/** 30062306a36Sopenharmony_ci * __optee_disable_shm_cache() - Disables caching of some shared memory 30162306a36Sopenharmony_ci * allocation in OP-TEE 30262306a36Sopenharmony_ci * @optee: main service struct 30362306a36Sopenharmony_ci * @is_mapped: true if the cached shared memory addresses were mapped by this 30462306a36Sopenharmony_ci * kernel, are safe to dereference, and should be freed 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_cistatic void __optee_disable_shm_cache(struct optee *optee, bool is_mapped) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct optee_call_waiter w; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* We need to retry until secure world isn't busy. */ 31162306a36Sopenharmony_ci optee_cq_wait_init(&optee->call_queue, &w); 31262306a36Sopenharmony_ci while (true) { 31362306a36Sopenharmony_ci union { 31462306a36Sopenharmony_ci struct arm_smccc_res smccc; 31562306a36Sopenharmony_ci struct optee_smc_disable_shm_cache_result result; 31662306a36Sopenharmony_ci } res; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci optee->smc.invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, 31962306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, &res.smccc); 32062306a36Sopenharmony_ci if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) 32162306a36Sopenharmony_ci break; /* All shm's freed */ 32262306a36Sopenharmony_ci if (res.result.status == OPTEE_SMC_RETURN_OK) { 32362306a36Sopenharmony_ci struct tee_shm *shm; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * Shared memory references that were not mapped by 32762306a36Sopenharmony_ci * this kernel must be ignored to prevent a crash. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci if (!is_mapped) 33062306a36Sopenharmony_ci continue; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci shm = reg_pair_to_ptr(res.result.shm_upper32, 33362306a36Sopenharmony_ci res.result.shm_lower32); 33462306a36Sopenharmony_ci tee_shm_free(shm); 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci optee_cq_wait_for_completion(&optee->call_queue, &w); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci optee_cq_wait_final(&optee->call_queue, &w); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/** 34362306a36Sopenharmony_ci * optee_disable_shm_cache() - Disables caching of mapped shared memory 34462306a36Sopenharmony_ci * allocations in OP-TEE 34562306a36Sopenharmony_ci * @optee: main service struct 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_cistatic void optee_disable_shm_cache(struct optee *optee) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci return __optee_disable_shm_cache(optee, true); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/** 35362306a36Sopenharmony_ci * optee_disable_unmapped_shm_cache() - Disables caching of shared memory 35462306a36Sopenharmony_ci * allocations in OP-TEE which are not 35562306a36Sopenharmony_ci * currently mapped 35662306a36Sopenharmony_ci * @optee: main service struct 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistatic void optee_disable_unmapped_shm_cache(struct optee *optee) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci return __optee_disable_shm_cache(optee, false); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci#define PAGELIST_ENTRIES_PER_PAGE \ 36462306a36Sopenharmony_ci ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* 36762306a36Sopenharmony_ci * The final entry in each pagelist page is a pointer to the next 36862306a36Sopenharmony_ci * pagelist page. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_cistatic size_t get_pages_list_size(size_t num_entries) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic u64 *optee_allocate_pages_list(size_t num_entries) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void optee_free_pages_list(void *list, size_t num_entries) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci free_pages_exact(list, get_pages_list_size(num_entries)); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/** 38862306a36Sopenharmony_ci * optee_fill_pages_list() - write list of user pages to given shared 38962306a36Sopenharmony_ci * buffer. 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * @dst: page-aligned buffer where list of pages will be stored 39262306a36Sopenharmony_ci * @pages: array of pages that represents shared buffer 39362306a36Sopenharmony_ci * @num_pages: number of entries in @pages 39462306a36Sopenharmony_ci * @page_offset: offset of user buffer from page start 39562306a36Sopenharmony_ci * 39662306a36Sopenharmony_ci * @dst should be big enough to hold list of user page addresses and 39762306a36Sopenharmony_ci * links to the next pages of buffer 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, 40062306a36Sopenharmony_ci size_t page_offset) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int n = 0; 40362306a36Sopenharmony_ci phys_addr_t optee_page; 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h 40662306a36Sopenharmony_ci * for details. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci struct { 40962306a36Sopenharmony_ci u64 pages_list[PAGELIST_ENTRIES_PER_PAGE]; 41062306a36Sopenharmony_ci u64 next_page_data; 41162306a36Sopenharmony_ci } *pages_data; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * Currently OP-TEE uses 4k page size and it does not looks 41562306a36Sopenharmony_ci * like this will change in the future. On other hand, there are 41662306a36Sopenharmony_ci * no know ARM architectures with page size < 4k. 41762306a36Sopenharmony_ci * Thus the next built assert looks redundant. But the following 41862306a36Sopenharmony_ci * code heavily relies on this assumption, so it is better be 41962306a36Sopenharmony_ci * safe than sorry. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci pages_data = (void *)dst; 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * If linux page is bigger than 4k, and user buffer offset is 42662306a36Sopenharmony_ci * larger than 4k/8k/12k/etc this will skip first 4k pages, 42762306a36Sopenharmony_ci * because they bear no value data for OP-TEE. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci optee_page = page_to_phys(*pages) + 43062306a36Sopenharmony_ci round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci while (true) { 43362306a36Sopenharmony_ci pages_data->pages_list[n++] = optee_page; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (n == PAGELIST_ENTRIES_PER_PAGE) { 43662306a36Sopenharmony_ci pages_data->next_page_data = 43762306a36Sopenharmony_ci virt_to_phys(pages_data + 1); 43862306a36Sopenharmony_ci pages_data++; 43962306a36Sopenharmony_ci n = 0; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE; 44362306a36Sopenharmony_ci if (!(optee_page & ~PAGE_MASK)) { 44462306a36Sopenharmony_ci if (!--num_pages) 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci pages++; 44762306a36Sopenharmony_ci optee_page = page_to_phys(*pages); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, 45362306a36Sopenharmony_ci struct page **pages, size_t num_pages, 45462306a36Sopenharmony_ci unsigned long start) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(ctx->teedev); 45762306a36Sopenharmony_ci struct optee_msg_arg *msg_arg; 45862306a36Sopenharmony_ci struct tee_shm *shm_arg; 45962306a36Sopenharmony_ci u64 *pages_list; 46062306a36Sopenharmony_ci size_t sz; 46162306a36Sopenharmony_ci int rc; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (!num_pages) 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci rc = optee_check_mem_type(start, num_pages); 46762306a36Sopenharmony_ci if (rc) 46862306a36Sopenharmony_ci return rc; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci pages_list = optee_allocate_pages_list(num_pages); 47162306a36Sopenharmony_ci if (!pages_list) 47262306a36Sopenharmony_ci return -ENOMEM; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* 47562306a36Sopenharmony_ci * We're about to register shared memory we can't register shared 47662306a36Sopenharmony_ci * memory for this request or there's a catch-22. 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * So in this we'll have to do the good old temporary private 47962306a36Sopenharmony_ci * allocation instead of using optee_get_msg_arg(). 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_ci sz = optee_msg_arg_size(optee->rpc_param_count); 48262306a36Sopenharmony_ci shm_arg = tee_shm_alloc_priv_buf(ctx, sz); 48362306a36Sopenharmony_ci if (IS_ERR(shm_arg)) { 48462306a36Sopenharmony_ci rc = PTR_ERR(shm_arg); 48562306a36Sopenharmony_ci goto out; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci msg_arg = tee_shm_get_va(shm_arg, 0); 48862306a36Sopenharmony_ci if (IS_ERR(msg_arg)) { 48962306a36Sopenharmony_ci rc = PTR_ERR(msg_arg); 49062306a36Sopenharmony_ci goto out; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci optee_fill_pages_list(pages_list, pages, num_pages, 49462306a36Sopenharmony_ci tee_shm_get_page_offset(shm)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci memset(msg_arg, 0, OPTEE_MSG_GET_ARG_SIZE(1)); 49762306a36Sopenharmony_ci msg_arg->num_params = 1; 49862306a36Sopenharmony_ci msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; 49962306a36Sopenharmony_ci msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | 50062306a36Sopenharmony_ci OPTEE_MSG_ATTR_NONCONTIG; 50162306a36Sopenharmony_ci msg_arg->params->u.tmem.shm_ref = (unsigned long)shm; 50262306a36Sopenharmony_ci msg_arg->params->u.tmem.size = tee_shm_get_size(shm); 50362306a36Sopenharmony_ci /* 50462306a36Sopenharmony_ci * In the least bits of msg_arg->params->u.tmem.buf_ptr we 50562306a36Sopenharmony_ci * store buffer offset from 4k page, as described in OP-TEE ABI. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | 50862306a36Sopenharmony_ci (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (optee->ops->do_call_with_arg(ctx, shm_arg, 0) || 51162306a36Sopenharmony_ci msg_arg->ret != TEEC_SUCCESS) 51262306a36Sopenharmony_ci rc = -EINVAL; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci tee_shm_free(shm_arg); 51562306a36Sopenharmony_ciout: 51662306a36Sopenharmony_ci optee_free_pages_list(pages_list, num_pages); 51762306a36Sopenharmony_ci return rc; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(ctx->teedev); 52362306a36Sopenharmony_ci struct optee_msg_arg *msg_arg; 52462306a36Sopenharmony_ci struct tee_shm *shm_arg; 52562306a36Sopenharmony_ci int rc = 0; 52662306a36Sopenharmony_ci size_t sz; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* 52962306a36Sopenharmony_ci * We're about to unregister shared memory and we may not be able 53062306a36Sopenharmony_ci * register shared memory for this request in case we're called 53162306a36Sopenharmony_ci * from optee_shm_arg_cache_uninit(). 53262306a36Sopenharmony_ci * 53362306a36Sopenharmony_ci * So in order to keep things simple in this function just as in 53462306a36Sopenharmony_ci * optee_shm_register() we'll use temporary private allocation 53562306a36Sopenharmony_ci * instead of using optee_get_msg_arg(). 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci sz = optee_msg_arg_size(optee->rpc_param_count); 53862306a36Sopenharmony_ci shm_arg = tee_shm_alloc_priv_buf(ctx, sz); 53962306a36Sopenharmony_ci if (IS_ERR(shm_arg)) 54062306a36Sopenharmony_ci return PTR_ERR(shm_arg); 54162306a36Sopenharmony_ci msg_arg = tee_shm_get_va(shm_arg, 0); 54262306a36Sopenharmony_ci if (IS_ERR(msg_arg)) { 54362306a36Sopenharmony_ci rc = PTR_ERR(msg_arg); 54462306a36Sopenharmony_ci goto out; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci memset(msg_arg, 0, sz); 54862306a36Sopenharmony_ci msg_arg->num_params = 1; 54962306a36Sopenharmony_ci msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; 55062306a36Sopenharmony_ci msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; 55162306a36Sopenharmony_ci msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (optee->ops->do_call_with_arg(ctx, shm_arg, 0) || 55462306a36Sopenharmony_ci msg_arg->ret != TEEC_SUCCESS) 55562306a36Sopenharmony_ci rc = -EINVAL; 55662306a36Sopenharmony_ciout: 55762306a36Sopenharmony_ci tee_shm_free(shm_arg); 55862306a36Sopenharmony_ci return rc; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, 56262306a36Sopenharmony_ci struct page **pages, size_t num_pages, 56362306a36Sopenharmony_ci unsigned long start) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci /* 56662306a36Sopenharmony_ci * We don't want to register supplicant memory in OP-TEE. 56762306a36Sopenharmony_ci * Instead information about it will be passed in RPC code. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci return optee_check_mem_type(start, num_pages); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int optee_shm_unregister_supp(struct tee_context *ctx, 57362306a36Sopenharmony_ci struct tee_shm *shm) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* 57962306a36Sopenharmony_ci * 3. Dynamic shared memory pool based on alloc_pages() 58062306a36Sopenharmony_ci * 58162306a36Sopenharmony_ci * Implements an OP-TEE specific shared memory pool which is used 58262306a36Sopenharmony_ci * when dynamic shared memory is supported by secure world. 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * The main function is optee_shm_pool_alloc_pages(). 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int pool_op_alloc(struct tee_shm_pool *pool, 58862306a36Sopenharmony_ci struct tee_shm *shm, size_t size, size_t align) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci /* 59162306a36Sopenharmony_ci * Shared memory private to the OP-TEE driver doesn't need 59262306a36Sopenharmony_ci * to be registered with OP-TEE. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci if (shm->flags & TEE_SHM_PRIV) 59562306a36Sopenharmony_ci return optee_pool_op_alloc_helper(pool, shm, size, align, NULL); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return optee_pool_op_alloc_helper(pool, shm, size, align, 59862306a36Sopenharmony_ci optee_shm_register); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void pool_op_free(struct tee_shm_pool *pool, 60262306a36Sopenharmony_ci struct tee_shm *shm) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci if (!(shm->flags & TEE_SHM_PRIV)) 60562306a36Sopenharmony_ci optee_pool_op_free_helper(pool, shm, optee_shm_unregister); 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci optee_pool_op_free_helper(pool, shm, NULL); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void pool_op_destroy_pool(struct tee_shm_pool *pool) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci kfree(pool); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic const struct tee_shm_pool_ops pool_ops = { 61662306a36Sopenharmony_ci .alloc = pool_op_alloc, 61762306a36Sopenharmony_ci .free = pool_op_free, 61862306a36Sopenharmony_ci .destroy_pool = pool_op_destroy_pool, 61962306a36Sopenharmony_ci}; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/** 62262306a36Sopenharmony_ci * optee_shm_pool_alloc_pages() - create page-based allocator pool 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * This pool is used when OP-TEE supports dymanic SHM. In this case 62562306a36Sopenharmony_ci * command buffers and such are allocated from kernel's own memory. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_cistatic struct tee_shm_pool *optee_shm_pool_alloc_pages(void) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct tee_shm_pool *pool = kzalloc(sizeof(*pool), GFP_KERNEL); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (!pool) 63262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci pool->ops = &pool_ops; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return pool; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/* 64062306a36Sopenharmony_ci * 4. Do a normal scheduled call into secure world 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * The function optee_smc_do_call_with_arg() performs a normal scheduled 64362306a36Sopenharmony_ci * call into secure world. During this call may normal world request help 64462306a36Sopenharmony_ci * from normal world using RPCs, Remote Procedure Calls. This includes 64562306a36Sopenharmony_ci * delivery of non-secure interrupts to for instance allow rescheduling of 64662306a36Sopenharmony_ci * the current task. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, 65062306a36Sopenharmony_ci struct optee_msg_arg *arg) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct tee_shm *shm; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci arg->ret_origin = TEEC_ORIGIN_COMMS; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (arg->num_params != 1 || 65762306a36Sopenharmony_ci arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { 65862306a36Sopenharmony_ci arg->ret = TEEC_ERROR_BAD_PARAMETERS; 65962306a36Sopenharmony_ci return; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; 66362306a36Sopenharmony_ci switch (arg->params[0].u.value.a) { 66462306a36Sopenharmony_ci case OPTEE_RPC_SHM_TYPE_APPL: 66562306a36Sopenharmony_ci optee_rpc_cmd_free_suppl(ctx, shm); 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci case OPTEE_RPC_SHM_TYPE_KERNEL: 66862306a36Sopenharmony_ci tee_shm_free(shm); 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci default: 67162306a36Sopenharmony_ci arg->ret = TEEC_ERROR_BAD_PARAMETERS; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci arg->ret = TEEC_SUCCESS; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, 67762306a36Sopenharmony_ci struct optee *optee, 67862306a36Sopenharmony_ci struct optee_msg_arg *arg, 67962306a36Sopenharmony_ci struct optee_call_ctx *call_ctx) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci phys_addr_t pa; 68262306a36Sopenharmony_ci struct tee_shm *shm; 68362306a36Sopenharmony_ci size_t sz; 68462306a36Sopenharmony_ci size_t n; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci arg->ret_origin = TEEC_ORIGIN_COMMS; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (!arg->num_params || 68962306a36Sopenharmony_ci arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { 69062306a36Sopenharmony_ci arg->ret = TEEC_ERROR_BAD_PARAMETERS; 69162306a36Sopenharmony_ci return; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci for (n = 1; n < arg->num_params; n++) { 69562306a36Sopenharmony_ci if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) { 69662306a36Sopenharmony_ci arg->ret = TEEC_ERROR_BAD_PARAMETERS; 69762306a36Sopenharmony_ci return; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci sz = arg->params[0].u.value.b; 70262306a36Sopenharmony_ci switch (arg->params[0].u.value.a) { 70362306a36Sopenharmony_ci case OPTEE_RPC_SHM_TYPE_APPL: 70462306a36Sopenharmony_ci shm = optee_rpc_cmd_alloc_suppl(ctx, sz); 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case OPTEE_RPC_SHM_TYPE_KERNEL: 70762306a36Sopenharmony_ci shm = tee_shm_alloc_priv_buf(optee->ctx, sz); 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci default: 71062306a36Sopenharmony_ci arg->ret = TEEC_ERROR_BAD_PARAMETERS; 71162306a36Sopenharmony_ci return; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (IS_ERR(shm)) { 71562306a36Sopenharmony_ci arg->ret = TEEC_ERROR_OUT_OF_MEMORY; 71662306a36Sopenharmony_ci return; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (tee_shm_get_pa(shm, 0, &pa)) { 72062306a36Sopenharmony_ci arg->ret = TEEC_ERROR_BAD_PARAMETERS; 72162306a36Sopenharmony_ci goto bad; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci sz = tee_shm_get_size(shm); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (tee_shm_is_dynamic(shm)) { 72762306a36Sopenharmony_ci struct page **pages; 72862306a36Sopenharmony_ci u64 *pages_list; 72962306a36Sopenharmony_ci size_t page_num; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci pages = tee_shm_get_pages(shm, &page_num); 73262306a36Sopenharmony_ci if (!pages || !page_num) { 73362306a36Sopenharmony_ci arg->ret = TEEC_ERROR_OUT_OF_MEMORY; 73462306a36Sopenharmony_ci goto bad; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci pages_list = optee_allocate_pages_list(page_num); 73862306a36Sopenharmony_ci if (!pages_list) { 73962306a36Sopenharmony_ci arg->ret = TEEC_ERROR_OUT_OF_MEMORY; 74062306a36Sopenharmony_ci goto bad; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci call_ctx->pages_list = pages_list; 74462306a36Sopenharmony_ci call_ctx->num_entries = page_num; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | 74762306a36Sopenharmony_ci OPTEE_MSG_ATTR_NONCONTIG; 74862306a36Sopenharmony_ci /* 74962306a36Sopenharmony_ci * In the least bits of u.tmem.buf_ptr we store buffer offset 75062306a36Sopenharmony_ci * from 4k page, as described in OP-TEE ABI. 75162306a36Sopenharmony_ci */ 75262306a36Sopenharmony_ci arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) | 75362306a36Sopenharmony_ci (tee_shm_get_page_offset(shm) & 75462306a36Sopenharmony_ci (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); 75562306a36Sopenharmony_ci arg->params[0].u.tmem.size = tee_shm_get_size(shm); 75662306a36Sopenharmony_ci arg->params[0].u.tmem.shm_ref = (unsigned long)shm; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci optee_fill_pages_list(pages_list, pages, page_num, 75962306a36Sopenharmony_ci tee_shm_get_page_offset(shm)); 76062306a36Sopenharmony_ci } else { 76162306a36Sopenharmony_ci arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; 76262306a36Sopenharmony_ci arg->params[0].u.tmem.buf_ptr = pa; 76362306a36Sopenharmony_ci arg->params[0].u.tmem.size = sz; 76462306a36Sopenharmony_ci arg->params[0].u.tmem.shm_ref = (unsigned long)shm; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci arg->ret = TEEC_SUCCESS; 76862306a36Sopenharmony_ci return; 76962306a36Sopenharmony_cibad: 77062306a36Sopenharmony_ci tee_shm_free(shm); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic void free_pages_list(struct optee_call_ctx *call_ctx) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci if (call_ctx->pages_list) { 77662306a36Sopenharmony_ci optee_free_pages_list(call_ctx->pages_list, 77762306a36Sopenharmony_ci call_ctx->num_entries); 77862306a36Sopenharmony_ci call_ctx->pages_list = NULL; 77962306a36Sopenharmony_ci call_ctx->num_entries = 0; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci free_pages_list(call_ctx); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, 78962306a36Sopenharmony_ci struct optee_msg_arg *arg, 79062306a36Sopenharmony_ci struct optee_call_ctx *call_ctx) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci switch (arg->cmd) { 79462306a36Sopenharmony_ci case OPTEE_RPC_CMD_SHM_ALLOC: 79562306a36Sopenharmony_ci free_pages_list(call_ctx); 79662306a36Sopenharmony_ci handle_rpc_func_cmd_shm_alloc(ctx, optee, arg, call_ctx); 79762306a36Sopenharmony_ci break; 79862306a36Sopenharmony_ci case OPTEE_RPC_CMD_SHM_FREE: 79962306a36Sopenharmony_ci handle_rpc_func_cmd_shm_free(ctx, arg); 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci default: 80262306a36Sopenharmony_ci optee_rpc_cmd(ctx, optee, arg); 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/** 80762306a36Sopenharmony_ci * optee_handle_rpc() - handle RPC from secure world 80862306a36Sopenharmony_ci * @ctx: context doing the RPC 80962306a36Sopenharmony_ci * @param: value of registers for the RPC 81062306a36Sopenharmony_ci * @call_ctx: call context. Preserved during one OP-TEE invocation 81162306a36Sopenharmony_ci * 81262306a36Sopenharmony_ci * Result of RPC is written back into @param. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_cistatic void optee_handle_rpc(struct tee_context *ctx, 81562306a36Sopenharmony_ci struct optee_msg_arg *rpc_arg, 81662306a36Sopenharmony_ci struct optee_rpc_param *param, 81762306a36Sopenharmony_ci struct optee_call_ctx *call_ctx) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 82062306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(teedev); 82162306a36Sopenharmony_ci struct optee_msg_arg *arg; 82262306a36Sopenharmony_ci struct tee_shm *shm; 82362306a36Sopenharmony_ci phys_addr_t pa; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { 82662306a36Sopenharmony_ci case OPTEE_SMC_RPC_FUNC_ALLOC: 82762306a36Sopenharmony_ci shm = tee_shm_alloc_priv_buf(optee->ctx, param->a1); 82862306a36Sopenharmony_ci if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { 82962306a36Sopenharmony_ci reg_pair_from_64(¶m->a1, ¶m->a2, pa); 83062306a36Sopenharmony_ci reg_pair_from_64(¶m->a4, ¶m->a5, 83162306a36Sopenharmony_ci (unsigned long)shm); 83262306a36Sopenharmony_ci } else { 83362306a36Sopenharmony_ci param->a1 = 0; 83462306a36Sopenharmony_ci param->a2 = 0; 83562306a36Sopenharmony_ci param->a4 = 0; 83662306a36Sopenharmony_ci param->a5 = 0; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci kmemleak_not_leak(shm); 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci case OPTEE_SMC_RPC_FUNC_FREE: 84162306a36Sopenharmony_ci shm = reg_pair_to_ptr(param->a1, param->a2); 84262306a36Sopenharmony_ci tee_shm_free(shm); 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: 84562306a36Sopenharmony_ci /* 84662306a36Sopenharmony_ci * A foreign interrupt was raised while secure world was 84762306a36Sopenharmony_ci * executing, since they are handled in Linux a dummy RPC is 84862306a36Sopenharmony_ci * performed to let Linux take the interrupt through the normal 84962306a36Sopenharmony_ci * vector. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci case OPTEE_SMC_RPC_FUNC_CMD: 85362306a36Sopenharmony_ci if (rpc_arg) { 85462306a36Sopenharmony_ci arg = rpc_arg; 85562306a36Sopenharmony_ci } else { 85662306a36Sopenharmony_ci shm = reg_pair_to_ptr(param->a1, param->a2); 85762306a36Sopenharmony_ci arg = tee_shm_get_va(shm, 0); 85862306a36Sopenharmony_ci if (IS_ERR(arg)) { 85962306a36Sopenharmony_ci pr_err("%s: tee_shm_get_va %p failed\n", 86062306a36Sopenharmony_ci __func__, shm); 86162306a36Sopenharmony_ci break; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci handle_rpc_func_cmd(ctx, optee, arg, call_ctx); 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci default: 86862306a36Sopenharmony_ci pr_warn("Unknown RPC func 0x%x\n", 86962306a36Sopenharmony_ci (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); 87062306a36Sopenharmony_ci break; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci/** 87762306a36Sopenharmony_ci * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world 87862306a36Sopenharmony_ci * @ctx: calling context 87962306a36Sopenharmony_ci * @shm: shared memory holding the message to pass to secure world 88062306a36Sopenharmony_ci * @offs: offset of the message in @shm 88162306a36Sopenharmony_ci * 88262306a36Sopenharmony_ci * Does and SMC to OP-TEE in secure world and handles eventual resulting 88362306a36Sopenharmony_ci * Remote Procedure Calls (RPC) from OP-TEE. 88462306a36Sopenharmony_ci * 88562306a36Sopenharmony_ci * Returns return code from secure world, 0 is OK 88662306a36Sopenharmony_ci */ 88762306a36Sopenharmony_cistatic int optee_smc_do_call_with_arg(struct tee_context *ctx, 88862306a36Sopenharmony_ci struct tee_shm *shm, u_int offs) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(ctx->teedev); 89162306a36Sopenharmony_ci struct optee_call_waiter w; 89262306a36Sopenharmony_ci struct optee_rpc_param param = { }; 89362306a36Sopenharmony_ci struct optee_call_ctx call_ctx = { }; 89462306a36Sopenharmony_ci struct optee_msg_arg *rpc_arg = NULL; 89562306a36Sopenharmony_ci int rc; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (optee->rpc_param_count) { 89862306a36Sopenharmony_ci struct optee_msg_arg *arg; 89962306a36Sopenharmony_ci unsigned int rpc_arg_offs; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci arg = tee_shm_get_va(shm, offs); 90262306a36Sopenharmony_ci if (IS_ERR(arg)) 90362306a36Sopenharmony_ci return PTR_ERR(arg); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); 90662306a36Sopenharmony_ci rpc_arg = tee_shm_get_va(shm, offs + rpc_arg_offs); 90762306a36Sopenharmony_ci if (IS_ERR(rpc_arg)) 90862306a36Sopenharmony_ci return PTR_ERR(rpc_arg); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (rpc_arg && tee_shm_is_dynamic(shm)) { 91262306a36Sopenharmony_ci param.a0 = OPTEE_SMC_CALL_WITH_REGD_ARG; 91362306a36Sopenharmony_ci reg_pair_from_64(¶m.a1, ¶m.a2, (u_long)shm); 91462306a36Sopenharmony_ci param.a3 = offs; 91562306a36Sopenharmony_ci } else { 91662306a36Sopenharmony_ci phys_addr_t parg; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci rc = tee_shm_get_pa(shm, offs, &parg); 91962306a36Sopenharmony_ci if (rc) 92062306a36Sopenharmony_ci return rc; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (rpc_arg) 92362306a36Sopenharmony_ci param.a0 = OPTEE_SMC_CALL_WITH_RPC_ARG; 92462306a36Sopenharmony_ci else 92562306a36Sopenharmony_ci param.a0 = OPTEE_SMC_CALL_WITH_ARG; 92662306a36Sopenharmony_ci reg_pair_from_64(¶m.a1, ¶m.a2, parg); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci /* Initialize waiter */ 92962306a36Sopenharmony_ci optee_cq_wait_init(&optee->call_queue, &w); 93062306a36Sopenharmony_ci while (true) { 93162306a36Sopenharmony_ci struct arm_smccc_res res; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci trace_optee_invoke_fn_begin(¶m); 93462306a36Sopenharmony_ci optee->smc.invoke_fn(param.a0, param.a1, param.a2, param.a3, 93562306a36Sopenharmony_ci param.a4, param.a5, param.a6, param.a7, 93662306a36Sopenharmony_ci &res); 93762306a36Sopenharmony_ci trace_optee_invoke_fn_end(¶m, &res); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { 94062306a36Sopenharmony_ci /* 94162306a36Sopenharmony_ci * Out of threads in secure world, wait for a thread 94262306a36Sopenharmony_ci * become available. 94362306a36Sopenharmony_ci */ 94462306a36Sopenharmony_ci optee_cq_wait_for_completion(&optee->call_queue, &w); 94562306a36Sopenharmony_ci } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { 94662306a36Sopenharmony_ci cond_resched(); 94762306a36Sopenharmony_ci param.a0 = res.a0; 94862306a36Sopenharmony_ci param.a1 = res.a1; 94962306a36Sopenharmony_ci param.a2 = res.a2; 95062306a36Sopenharmony_ci param.a3 = res.a3; 95162306a36Sopenharmony_ci optee_handle_rpc(ctx, rpc_arg, ¶m, &call_ctx); 95262306a36Sopenharmony_ci } else { 95362306a36Sopenharmony_ci rc = res.a0; 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci optee_rpc_finalize_call(&call_ctx); 95962306a36Sopenharmony_ci /* 96062306a36Sopenharmony_ci * We're done with our thread in secure world, if there's any 96162306a36Sopenharmony_ci * thread waiters wake up one. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci optee_cq_wait_final(&optee->call_queue, &w); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return rc; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic int simple_call_with_arg(struct tee_context *ctx, u32 cmd) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct optee_shm_arg_entry *entry; 97162306a36Sopenharmony_ci struct optee_msg_arg *msg_arg; 97262306a36Sopenharmony_ci struct tee_shm *shm; 97362306a36Sopenharmony_ci u_int offs; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs); 97662306a36Sopenharmony_ci if (IS_ERR(msg_arg)) 97762306a36Sopenharmony_ci return PTR_ERR(msg_arg); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci msg_arg->cmd = cmd; 98062306a36Sopenharmony_ci optee_smc_do_call_with_arg(ctx, shm, offs); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci optee_free_msg_arg(ctx, entry, offs); 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic int optee_smc_do_bottom_half(struct tee_context *ctx) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci return simple_call_with_arg(ctx, OPTEE_MSG_CMD_DO_BOTTOM_HALF); 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic int optee_smc_stop_async_notif(struct tee_context *ctx) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci return simple_call_with_arg(ctx, OPTEE_MSG_CMD_STOP_ASYNC_NOTIF); 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci/* 99762306a36Sopenharmony_ci * 5. Asynchronous notification 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid, 100162306a36Sopenharmony_ci bool *value_pending) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct arm_smccc_res res; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_GET_ASYNC_NOTIF_VALUE, 0, 0, 0, 0, 0, 0, 0, &res); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (res.a0) { 100862306a36Sopenharmony_ci *value_valid = false; 100962306a36Sopenharmony_ci return 0; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci *value_valid = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID); 101262306a36Sopenharmony_ci *value_pending = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING); 101362306a36Sopenharmony_ci return res.a1; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic irqreturn_t irq_handler(struct optee *optee) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci bool do_bottom_half = false; 101962306a36Sopenharmony_ci bool value_valid; 102062306a36Sopenharmony_ci bool value_pending; 102162306a36Sopenharmony_ci u32 value; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci do { 102462306a36Sopenharmony_ci value = get_async_notif_value(optee->smc.invoke_fn, 102562306a36Sopenharmony_ci &value_valid, &value_pending); 102662306a36Sopenharmony_ci if (!value_valid) 102762306a36Sopenharmony_ci break; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (value == OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF) 103062306a36Sopenharmony_ci do_bottom_half = true; 103162306a36Sopenharmony_ci else 103262306a36Sopenharmony_ci optee_notif_send(optee, value); 103362306a36Sopenharmony_ci } while (value_pending); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (do_bottom_half) 103662306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 103762306a36Sopenharmony_ci return IRQ_HANDLED; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic irqreturn_t notif_irq_handler(int irq, void *dev_id) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct optee *optee = dev_id; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci return irq_handler(optee); 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic irqreturn_t notif_irq_thread_fn(int irq, void *dev_id) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct optee *optee = dev_id; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci optee_smc_do_bottom_half(optee->ctx); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci return IRQ_HANDLED; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic int init_irq(struct optee *optee, u_int irq) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci int rc; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci rc = request_threaded_irq(irq, notif_irq_handler, 106162306a36Sopenharmony_ci notif_irq_thread_fn, 106262306a36Sopenharmony_ci 0, "optee_notification", optee); 106362306a36Sopenharmony_ci if (rc) 106462306a36Sopenharmony_ci return rc; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci optee->smc.notif_irq = irq; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic irqreturn_t notif_pcpu_irq_handler(int irq, void *dev_id) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct optee_pcpu *pcpu = dev_id; 107462306a36Sopenharmony_ci struct optee *optee = pcpu->optee; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (irq_handler(optee) == IRQ_WAKE_THREAD) 107762306a36Sopenharmony_ci queue_work(optee->smc.notif_pcpu_wq, 107862306a36Sopenharmony_ci &optee->smc.notif_pcpu_work); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return IRQ_HANDLED; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic void notif_pcpu_irq_work_fn(struct work_struct *work) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci struct optee_smc *optee_smc = container_of(work, struct optee_smc, 108662306a36Sopenharmony_ci notif_pcpu_work); 108762306a36Sopenharmony_ci struct optee *optee = container_of(optee_smc, struct optee, smc); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci optee_smc_do_bottom_half(optee->ctx); 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic int init_pcpu_irq(struct optee *optee, u_int irq) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct optee_pcpu __percpu *optee_pcpu; 109562306a36Sopenharmony_ci int cpu, rc; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci optee_pcpu = alloc_percpu(struct optee_pcpu); 109862306a36Sopenharmony_ci if (!optee_pcpu) 109962306a36Sopenharmony_ci return -ENOMEM; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci for_each_present_cpu(cpu) 110262306a36Sopenharmony_ci per_cpu_ptr(optee_pcpu, cpu)->optee = optee; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci rc = request_percpu_irq(irq, notif_pcpu_irq_handler, 110562306a36Sopenharmony_ci "optee_pcpu_notification", optee_pcpu); 110662306a36Sopenharmony_ci if (rc) 110762306a36Sopenharmony_ci goto err_free_pcpu; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci INIT_WORK(&optee->smc.notif_pcpu_work, notif_pcpu_irq_work_fn); 111062306a36Sopenharmony_ci optee->smc.notif_pcpu_wq = create_workqueue("optee_pcpu_notification"); 111162306a36Sopenharmony_ci if (!optee->smc.notif_pcpu_wq) { 111262306a36Sopenharmony_ci rc = -EINVAL; 111362306a36Sopenharmony_ci goto err_free_pcpu_irq; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci optee->smc.optee_pcpu = optee_pcpu; 111762306a36Sopenharmony_ci optee->smc.notif_irq = irq; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci pcpu_irq_num = irq; 112062306a36Sopenharmony_ci rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "optee/pcpu-notif:starting", 112162306a36Sopenharmony_ci optee_cpuhp_enable_pcpu_irq, 112262306a36Sopenharmony_ci optee_cpuhp_disable_pcpu_irq); 112362306a36Sopenharmony_ci if (!rc) 112462306a36Sopenharmony_ci rc = -EINVAL; 112562306a36Sopenharmony_ci if (rc < 0) 112662306a36Sopenharmony_ci goto err_free_pcpu_irq; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci optee->smc.notif_cpuhp_state = rc; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return 0; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cierr_free_pcpu_irq: 113362306a36Sopenharmony_ci free_percpu_irq(irq, optee_pcpu); 113462306a36Sopenharmony_cierr_free_pcpu: 113562306a36Sopenharmony_ci free_percpu(optee_pcpu); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci return rc; 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cistatic int optee_smc_notif_init_irq(struct optee *optee, u_int irq) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci if (irq_is_percpu_devid(irq)) 114362306a36Sopenharmony_ci return init_pcpu_irq(optee, irq); 114462306a36Sopenharmony_ci else 114562306a36Sopenharmony_ci return init_irq(optee, irq); 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic void uninit_pcpu_irq(struct optee *optee) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci cpuhp_remove_state(optee->smc.notif_cpuhp_state); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci destroy_workqueue(optee->smc.notif_pcpu_wq); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci free_percpu_irq(optee->smc.notif_irq, optee->smc.optee_pcpu); 115562306a36Sopenharmony_ci free_percpu(optee->smc.optee_pcpu); 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic void optee_smc_notif_uninit_irq(struct optee *optee) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) { 116162306a36Sopenharmony_ci optee_smc_stop_async_notif(optee->ctx); 116262306a36Sopenharmony_ci if (optee->smc.notif_irq) { 116362306a36Sopenharmony_ci if (irq_is_percpu_devid(optee->smc.notif_irq)) 116462306a36Sopenharmony_ci uninit_pcpu_irq(optee); 116562306a36Sopenharmony_ci else 116662306a36Sopenharmony_ci free_irq(optee->smc.notif_irq, optee); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci irq_dispose_mapping(optee->smc.notif_irq); 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci/* 117462306a36Sopenharmony_ci * 6. Driver initialization 117562306a36Sopenharmony_ci * 117662306a36Sopenharmony_ci * During driver initialization is secure world probed to find out which 117762306a36Sopenharmony_ci * features it supports so the driver can be initialized with a matching 117862306a36Sopenharmony_ci * configuration. This involves for instance support for dynamic shared 117962306a36Sopenharmony_ci * memory instead of a static memory carvout. 118062306a36Sopenharmony_ci */ 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic void optee_get_version(struct tee_device *teedev, 118362306a36Sopenharmony_ci struct tee_ioctl_version_data *vers) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct tee_ioctl_version_data v = { 118662306a36Sopenharmony_ci .impl_id = TEE_IMPL_ID_OPTEE, 118762306a36Sopenharmony_ci .impl_caps = TEE_OPTEE_CAP_TZ, 118862306a36Sopenharmony_ci .gen_caps = TEE_GEN_CAP_GP, 118962306a36Sopenharmony_ci }; 119062306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(teedev); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) 119362306a36Sopenharmony_ci v.gen_caps |= TEE_GEN_CAP_REG_MEM; 119462306a36Sopenharmony_ci if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) 119562306a36Sopenharmony_ci v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; 119662306a36Sopenharmony_ci *vers = v; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cistatic int optee_smc_open(struct tee_context *ctx) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(ctx->teedev); 120262306a36Sopenharmony_ci u32 sec_caps = optee->smc.sec_caps; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL); 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic const struct tee_driver_ops optee_clnt_ops = { 120862306a36Sopenharmony_ci .get_version = optee_get_version, 120962306a36Sopenharmony_ci .open = optee_smc_open, 121062306a36Sopenharmony_ci .release = optee_release, 121162306a36Sopenharmony_ci .open_session = optee_open_session, 121262306a36Sopenharmony_ci .close_session = optee_close_session, 121362306a36Sopenharmony_ci .invoke_func = optee_invoke_func, 121462306a36Sopenharmony_ci .cancel_req = optee_cancel_req, 121562306a36Sopenharmony_ci .shm_register = optee_shm_register, 121662306a36Sopenharmony_ci .shm_unregister = optee_shm_unregister, 121762306a36Sopenharmony_ci}; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic const struct tee_desc optee_clnt_desc = { 122062306a36Sopenharmony_ci .name = DRIVER_NAME "-clnt", 122162306a36Sopenharmony_ci .ops = &optee_clnt_ops, 122262306a36Sopenharmony_ci .owner = THIS_MODULE, 122362306a36Sopenharmony_ci}; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic const struct tee_driver_ops optee_supp_ops = { 122662306a36Sopenharmony_ci .get_version = optee_get_version, 122762306a36Sopenharmony_ci .open = optee_smc_open, 122862306a36Sopenharmony_ci .release = optee_release_supp, 122962306a36Sopenharmony_ci .supp_recv = optee_supp_recv, 123062306a36Sopenharmony_ci .supp_send = optee_supp_send, 123162306a36Sopenharmony_ci .shm_register = optee_shm_register_supp, 123262306a36Sopenharmony_ci .shm_unregister = optee_shm_unregister_supp, 123362306a36Sopenharmony_ci}; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic const struct tee_desc optee_supp_desc = { 123662306a36Sopenharmony_ci .name = DRIVER_NAME "-supp", 123762306a36Sopenharmony_ci .ops = &optee_supp_ops, 123862306a36Sopenharmony_ci .owner = THIS_MODULE, 123962306a36Sopenharmony_ci .flags = TEE_DESC_PRIVILEGED, 124062306a36Sopenharmony_ci}; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic const struct optee_ops optee_ops = { 124362306a36Sopenharmony_ci .do_call_with_arg = optee_smc_do_call_with_arg, 124462306a36Sopenharmony_ci .to_msg_param = optee_to_msg_param, 124562306a36Sopenharmony_ci .from_msg_param = optee_from_msg_param, 124662306a36Sopenharmony_ci}; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic int enable_async_notif(optee_invoke_fn *invoke_fn) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct arm_smccc_res res; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_ENABLE_ASYNC_NOTIF, 0, 0, 0, 0, 0, 0, 0, &res); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (res.a0) 125562306a36Sopenharmony_ci return -EINVAL; 125662306a36Sopenharmony_ci return 0; 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci struct arm_smccc_res res; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && 126662306a36Sopenharmony_ci res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) 126762306a36Sopenharmony_ci return true; 126862306a36Sopenharmony_ci return false; 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci#ifdef CONFIG_OPTEE_INSECURE_LOAD_IMAGE 127262306a36Sopenharmony_cistatic bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct arm_smccc_res res; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (res.a0 == OPTEE_MSG_IMAGE_LOAD_UID_0 && 127962306a36Sopenharmony_ci res.a1 == OPTEE_MSG_IMAGE_LOAD_UID_1 && 128062306a36Sopenharmony_ci res.a2 == OPTEE_MSG_IMAGE_LOAD_UID_2 && 128162306a36Sopenharmony_ci res.a3 == OPTEE_MSG_IMAGE_LOAD_UID_3) 128262306a36Sopenharmony_ci return true; 128362306a36Sopenharmony_ci return false; 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci#endif 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci union { 129062306a36Sopenharmony_ci struct arm_smccc_res smccc; 129162306a36Sopenharmony_ci struct optee_smc_call_get_os_revision_result result; 129262306a36Sopenharmony_ci } res = { 129362306a36Sopenharmony_ci .result = { 129462306a36Sopenharmony_ci .build_id = 0 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci }; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, 129962306a36Sopenharmony_ci &res.smccc); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (res.result.build_id) 130262306a36Sopenharmony_ci pr_info("revision %lu.%lu (%08lx)", res.result.major, 130362306a36Sopenharmony_ci res.result.minor, res.result.build_id); 130462306a36Sopenharmony_ci else 130562306a36Sopenharmony_ci pr_info("revision %lu.%lu", res.result.major, res.result.minor); 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cistatic bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci union { 131162306a36Sopenharmony_ci struct arm_smccc_res smccc; 131262306a36Sopenharmony_ci struct optee_smc_calls_revision_result result; 131362306a36Sopenharmony_ci } res; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (res.result.major == OPTEE_MSG_REVISION_MAJOR && 131862306a36Sopenharmony_ci (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) 131962306a36Sopenharmony_ci return true; 132062306a36Sopenharmony_ci return false; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistatic bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, 132462306a36Sopenharmony_ci u32 *sec_caps, u32 *max_notif_value, 132562306a36Sopenharmony_ci unsigned int *rpc_param_count) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci union { 132862306a36Sopenharmony_ci struct arm_smccc_res smccc; 132962306a36Sopenharmony_ci struct optee_smc_exchange_capabilities_result result; 133062306a36Sopenharmony_ci } res; 133162306a36Sopenharmony_ci u32 a1 = 0; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci /* 133462306a36Sopenharmony_ci * TODO This isn't enough to tell if it's UP system (from kernel 133562306a36Sopenharmony_ci * point of view) or not, is_smp() returns the information 133662306a36Sopenharmony_ci * needed, but can't be called directly from here. 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) 133962306a36Sopenharmony_ci a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, 134262306a36Sopenharmony_ci &res.smccc); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci if (res.result.status != OPTEE_SMC_RETURN_OK) 134562306a36Sopenharmony_ci return false; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci *sec_caps = res.result.capabilities; 134862306a36Sopenharmony_ci if (*sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) 134962306a36Sopenharmony_ci *max_notif_value = res.result.max_notif_value; 135062306a36Sopenharmony_ci else 135162306a36Sopenharmony_ci *max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE; 135262306a36Sopenharmony_ci if (*sec_caps & OPTEE_SMC_SEC_CAP_RPC_ARG) 135362306a36Sopenharmony_ci *rpc_param_count = (u8)res.result.data; 135462306a36Sopenharmony_ci else 135562306a36Sopenharmony_ci *rpc_param_count = 0; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return true; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic struct tee_shm_pool * 136162306a36Sopenharmony_cioptee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci union { 136462306a36Sopenharmony_ci struct arm_smccc_res smccc; 136562306a36Sopenharmony_ci struct optee_smc_get_shm_config_result result; 136662306a36Sopenharmony_ci } res; 136762306a36Sopenharmony_ci unsigned long vaddr; 136862306a36Sopenharmony_ci phys_addr_t paddr; 136962306a36Sopenharmony_ci size_t size; 137062306a36Sopenharmony_ci phys_addr_t begin; 137162306a36Sopenharmony_ci phys_addr_t end; 137262306a36Sopenharmony_ci void *va; 137362306a36Sopenharmony_ci void *rc; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); 137662306a36Sopenharmony_ci if (res.result.status != OPTEE_SMC_RETURN_OK) { 137762306a36Sopenharmony_ci pr_err("static shm service not available\n"); 137862306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci if (res.result.settings != OPTEE_SMC_SHM_CACHED) { 138262306a36Sopenharmony_ci pr_err("only normal cached shared memory supported\n"); 138362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci begin = roundup(res.result.start, PAGE_SIZE); 138762306a36Sopenharmony_ci end = rounddown(res.result.start + res.result.size, PAGE_SIZE); 138862306a36Sopenharmony_ci paddr = begin; 138962306a36Sopenharmony_ci size = end - begin; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci va = memremap(paddr, size, MEMREMAP_WB); 139262306a36Sopenharmony_ci if (!va) { 139362306a36Sopenharmony_ci pr_err("shared memory ioremap failed\n"); 139462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci vaddr = (unsigned long)va; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci rc = tee_shm_pool_alloc_res_mem(vaddr, paddr, size, 139962306a36Sopenharmony_ci OPTEE_MIN_STATIC_POOL_ALIGN); 140062306a36Sopenharmony_ci if (IS_ERR(rc)) 140162306a36Sopenharmony_ci memunmap(va); 140262306a36Sopenharmony_ci else 140362306a36Sopenharmony_ci *memremaped_shm = va; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci return rc; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci/* Simple wrapper functions to be able to use a function pointer */ 140962306a36Sopenharmony_cistatic void optee_smccc_smc(unsigned long a0, unsigned long a1, 141062306a36Sopenharmony_ci unsigned long a2, unsigned long a3, 141162306a36Sopenharmony_ci unsigned long a4, unsigned long a5, 141262306a36Sopenharmony_ci unsigned long a6, unsigned long a7, 141362306a36Sopenharmony_ci struct arm_smccc_res *res) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic void optee_smccc_hvc(unsigned long a0, unsigned long a1, 141962306a36Sopenharmony_ci unsigned long a2, unsigned long a3, 142062306a36Sopenharmony_ci unsigned long a4, unsigned long a5, 142162306a36Sopenharmony_ci unsigned long a6, unsigned long a7, 142262306a36Sopenharmony_ci struct arm_smccc_res *res) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistatic optee_invoke_fn *get_invoke_func(struct device *dev) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci const char *method; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci pr_info("probing for conduit method.\n"); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci if (device_property_read_string(dev, "method", &method)) { 143462306a36Sopenharmony_ci pr_warn("missing \"method\" property\n"); 143562306a36Sopenharmony_ci return ERR_PTR(-ENXIO); 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (!strcmp("hvc", method)) 143962306a36Sopenharmony_ci return optee_smccc_hvc; 144062306a36Sopenharmony_ci else if (!strcmp("smc", method)) 144162306a36Sopenharmony_ci return optee_smccc_smc; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci pr_warn("invalid \"method\" property: %s\n", method); 144462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci/* optee_remove - Device Removal Routine 144862306a36Sopenharmony_ci * @pdev: platform device information struct 144962306a36Sopenharmony_ci * 145062306a36Sopenharmony_ci * optee_remove is called by platform subsystem to alert the driver 145162306a36Sopenharmony_ci * that it should release the device 145262306a36Sopenharmony_ci */ 145362306a36Sopenharmony_cistatic int optee_smc_remove(struct platform_device *pdev) 145462306a36Sopenharmony_ci{ 145562306a36Sopenharmony_ci struct optee *optee = platform_get_drvdata(pdev); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci /* 145862306a36Sopenharmony_ci * Ask OP-TEE to free all cached shared memory objects to decrease 145962306a36Sopenharmony_ci * reference counters and also avoid wild pointers in secure world 146062306a36Sopenharmony_ci * into the old shared memory range. 146162306a36Sopenharmony_ci */ 146262306a36Sopenharmony_ci if (!optee->rpc_param_count) 146362306a36Sopenharmony_ci optee_disable_shm_cache(optee); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci optee_smc_notif_uninit_irq(optee); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci optee_remove_common(optee); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci if (optee->smc.memremaped_shm) 147062306a36Sopenharmony_ci memunmap(optee->smc.memremaped_shm); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci kfree(optee); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci return 0; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci/* optee_shutdown - Device Removal Routine 147862306a36Sopenharmony_ci * @pdev: platform device information struct 147962306a36Sopenharmony_ci * 148062306a36Sopenharmony_ci * platform_shutdown is called by the platform subsystem to alert 148162306a36Sopenharmony_ci * the driver that a shutdown, reboot, or kexec is happening and 148262306a36Sopenharmony_ci * device must be disabled. 148362306a36Sopenharmony_ci */ 148462306a36Sopenharmony_cistatic void optee_shutdown(struct platform_device *pdev) 148562306a36Sopenharmony_ci{ 148662306a36Sopenharmony_ci struct optee *optee = platform_get_drvdata(pdev); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if (!optee->rpc_param_count) 148962306a36Sopenharmony_ci optee_disable_shm_cache(optee); 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci#ifdef CONFIG_OPTEE_INSECURE_LOAD_IMAGE 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci#define OPTEE_FW_IMAGE "optee/tee.bin" 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic optee_invoke_fn *cpuhp_invoke_fn; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_cistatic int optee_cpuhp_probe(unsigned int cpu) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci /* 150162306a36Sopenharmony_ci * Invoking a call on a CPU will cause OP-TEE to perform the required 150262306a36Sopenharmony_ci * setup for that CPU. Just invoke the call to get the UID since that 150362306a36Sopenharmony_ci * has no side effects. 150462306a36Sopenharmony_ci */ 150562306a36Sopenharmony_ci if (optee_msg_api_uid_is_optee_api(cpuhp_invoke_fn)) 150662306a36Sopenharmony_ci return 0; 150762306a36Sopenharmony_ci else 150862306a36Sopenharmony_ci return -EINVAL; 150962306a36Sopenharmony_ci} 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_cistatic int optee_load_fw(struct platform_device *pdev, 151262306a36Sopenharmony_ci optee_invoke_fn *invoke_fn) 151362306a36Sopenharmony_ci{ 151462306a36Sopenharmony_ci const struct firmware *fw = NULL; 151562306a36Sopenharmony_ci struct arm_smccc_res res; 151662306a36Sopenharmony_ci phys_addr_t data_pa; 151762306a36Sopenharmony_ci u8 *data_buf = NULL; 151862306a36Sopenharmony_ci u64 data_size; 151962306a36Sopenharmony_ci u32 data_pa_high, data_pa_low; 152062306a36Sopenharmony_ci u32 data_size_high, data_size_low; 152162306a36Sopenharmony_ci int rc; 152262306a36Sopenharmony_ci int hp_state; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci if (!optee_msg_api_uid_is_optee_image_load(invoke_fn)) 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci rc = request_firmware(&fw, OPTEE_FW_IMAGE, &pdev->dev); 152862306a36Sopenharmony_ci if (rc) { 152962306a36Sopenharmony_ci /* 153062306a36Sopenharmony_ci * The firmware in the rootfs will not be accessible until we 153162306a36Sopenharmony_ci * are in the SYSTEM_RUNNING state, so return EPROBE_DEFER until 153262306a36Sopenharmony_ci * that point. 153362306a36Sopenharmony_ci */ 153462306a36Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 153562306a36Sopenharmony_ci return -EPROBE_DEFER; 153662306a36Sopenharmony_ci goto fw_err; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci data_size = fw->size; 154062306a36Sopenharmony_ci /* 154162306a36Sopenharmony_ci * This uses the GFP_DMA flag to ensure we are allocated memory in the 154262306a36Sopenharmony_ci * 32-bit space since TF-A cannot map memory beyond the 32-bit boundary. 154362306a36Sopenharmony_ci */ 154462306a36Sopenharmony_ci data_buf = kmemdup(fw->data, fw->size, GFP_KERNEL | GFP_DMA); 154562306a36Sopenharmony_ci if (!data_buf) { 154662306a36Sopenharmony_ci rc = -ENOMEM; 154762306a36Sopenharmony_ci goto fw_err; 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci data_pa = virt_to_phys(data_buf); 155062306a36Sopenharmony_ci reg_pair_from_64(&data_pa_high, &data_pa_low, data_pa); 155162306a36Sopenharmony_ci reg_pair_from_64(&data_size_high, &data_size_low, data_size); 155262306a36Sopenharmony_ci goto fw_load; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cifw_err: 155562306a36Sopenharmony_ci pr_warn("image loading failed\n"); 155662306a36Sopenharmony_ci data_pa_high = 0; 155762306a36Sopenharmony_ci data_pa_low = 0; 155862306a36Sopenharmony_ci data_size_high = 0; 155962306a36Sopenharmony_ci data_size_low = 0; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_cifw_load: 156262306a36Sopenharmony_ci /* 156362306a36Sopenharmony_ci * Always invoke the SMC, even if loading the image fails, to indicate 156462306a36Sopenharmony_ci * to EL3 that we have passed the point where it should allow invoking 156562306a36Sopenharmony_ci * this SMC. 156662306a36Sopenharmony_ci */ 156762306a36Sopenharmony_ci pr_warn("OP-TEE image loaded from kernel, this can be insecure"); 156862306a36Sopenharmony_ci invoke_fn(OPTEE_SMC_CALL_LOAD_IMAGE, data_size_high, data_size_low, 156962306a36Sopenharmony_ci data_pa_high, data_pa_low, 0, 0, 0, &res); 157062306a36Sopenharmony_ci if (!rc) 157162306a36Sopenharmony_ci rc = res.a0; 157262306a36Sopenharmony_ci if (fw) 157362306a36Sopenharmony_ci release_firmware(fw); 157462306a36Sopenharmony_ci kfree(data_buf); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (!rc) { 157762306a36Sopenharmony_ci /* 157862306a36Sopenharmony_ci * We need to initialize OP-TEE on all other running cores as 157962306a36Sopenharmony_ci * well. Any cores that aren't running yet will get initialized 158062306a36Sopenharmony_ci * when they are brought up by the power management functions in 158162306a36Sopenharmony_ci * TF-A which are registered by the OP-TEE SPD. Due to that we 158262306a36Sopenharmony_ci * can un-register the callback right after registering it. 158362306a36Sopenharmony_ci */ 158462306a36Sopenharmony_ci cpuhp_invoke_fn = invoke_fn; 158562306a36Sopenharmony_ci hp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "optee:probe", 158662306a36Sopenharmony_ci optee_cpuhp_probe, NULL); 158762306a36Sopenharmony_ci if (hp_state < 0) { 158862306a36Sopenharmony_ci pr_warn("Failed with CPU hotplug setup for OP-TEE"); 158962306a36Sopenharmony_ci return -EINVAL; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci cpuhp_remove_state(hp_state); 159262306a36Sopenharmony_ci cpuhp_invoke_fn = NULL; 159362306a36Sopenharmony_ci } 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci return rc; 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci#else 159862306a36Sopenharmony_cistatic inline int optee_load_fw(struct platform_device *pdev, 159962306a36Sopenharmony_ci optee_invoke_fn *invoke_fn) 160062306a36Sopenharmony_ci{ 160162306a36Sopenharmony_ci return 0; 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci#endif 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic int optee_probe(struct platform_device *pdev) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci optee_invoke_fn *invoke_fn; 160862306a36Sopenharmony_ci struct tee_shm_pool *pool = ERR_PTR(-EINVAL); 160962306a36Sopenharmony_ci struct optee *optee = NULL; 161062306a36Sopenharmony_ci void *memremaped_shm = NULL; 161162306a36Sopenharmony_ci unsigned int rpc_param_count; 161262306a36Sopenharmony_ci struct tee_device *teedev; 161362306a36Sopenharmony_ci struct tee_context *ctx; 161462306a36Sopenharmony_ci u32 max_notif_value; 161562306a36Sopenharmony_ci u32 arg_cache_flags; 161662306a36Sopenharmony_ci u32 sec_caps; 161762306a36Sopenharmony_ci int rc; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci invoke_fn = get_invoke_func(&pdev->dev); 162062306a36Sopenharmony_ci if (IS_ERR(invoke_fn)) 162162306a36Sopenharmony_ci return PTR_ERR(invoke_fn); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci rc = optee_load_fw(pdev, invoke_fn); 162462306a36Sopenharmony_ci if (rc) 162562306a36Sopenharmony_ci return rc; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { 162862306a36Sopenharmony_ci pr_warn("api uid mismatch\n"); 162962306a36Sopenharmony_ci return -EINVAL; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci optee_msg_get_os_revision(invoke_fn); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci if (!optee_msg_api_revision_is_compatible(invoke_fn)) { 163562306a36Sopenharmony_ci pr_warn("api revision mismatch\n"); 163662306a36Sopenharmony_ci return -EINVAL; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps, 164062306a36Sopenharmony_ci &max_notif_value, 164162306a36Sopenharmony_ci &rpc_param_count)) { 164262306a36Sopenharmony_ci pr_warn("capabilities mismatch\n"); 164362306a36Sopenharmony_ci return -EINVAL; 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci /* 164762306a36Sopenharmony_ci * Try to use dynamic shared memory if possible 164862306a36Sopenharmony_ci */ 164962306a36Sopenharmony_ci if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) { 165062306a36Sopenharmony_ci /* 165162306a36Sopenharmony_ci * If we have OPTEE_SMC_SEC_CAP_RPC_ARG we can ask 165262306a36Sopenharmony_ci * optee_get_msg_arg() to pre-register (by having 165362306a36Sopenharmony_ci * OPTEE_SHM_ARG_ALLOC_PRIV cleared) the page used to pass 165462306a36Sopenharmony_ci * an argument struct. 165562306a36Sopenharmony_ci * 165662306a36Sopenharmony_ci * With the page is pre-registered we can use a non-zero 165762306a36Sopenharmony_ci * offset for argument struct, this is indicated with 165862306a36Sopenharmony_ci * OPTEE_SHM_ARG_SHARED. 165962306a36Sopenharmony_ci * 166062306a36Sopenharmony_ci * This means that optee_smc_do_call_with_arg() will use 166162306a36Sopenharmony_ci * OPTEE_SMC_CALL_WITH_REGD_ARG for pre-registered pages. 166262306a36Sopenharmony_ci */ 166362306a36Sopenharmony_ci if (sec_caps & OPTEE_SMC_SEC_CAP_RPC_ARG) 166462306a36Sopenharmony_ci arg_cache_flags = OPTEE_SHM_ARG_SHARED; 166562306a36Sopenharmony_ci else 166662306a36Sopenharmony_ci arg_cache_flags = OPTEE_SHM_ARG_ALLOC_PRIV; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci pool = optee_shm_pool_alloc_pages(); 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci /* 167262306a36Sopenharmony_ci * If dynamic shared memory is not available or failed - try static one 167362306a36Sopenharmony_ci */ 167462306a36Sopenharmony_ci if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) { 167562306a36Sopenharmony_ci /* 167662306a36Sopenharmony_ci * The static memory pool can use non-zero page offsets so 167762306a36Sopenharmony_ci * let optee_get_msg_arg() know that with OPTEE_SHM_ARG_SHARED. 167862306a36Sopenharmony_ci * 167962306a36Sopenharmony_ci * optee_get_msg_arg() should not pre-register the 168062306a36Sopenharmony_ci * allocated page used to pass an argument struct, this is 168162306a36Sopenharmony_ci * indicated with OPTEE_SHM_ARG_ALLOC_PRIV. 168262306a36Sopenharmony_ci * 168362306a36Sopenharmony_ci * This means that optee_smc_do_call_with_arg() will use 168462306a36Sopenharmony_ci * OPTEE_SMC_CALL_WITH_ARG if rpc_param_count is 0, else 168562306a36Sopenharmony_ci * OPTEE_SMC_CALL_WITH_RPC_ARG. 168662306a36Sopenharmony_ci */ 168762306a36Sopenharmony_ci arg_cache_flags = OPTEE_SHM_ARG_SHARED | 168862306a36Sopenharmony_ci OPTEE_SHM_ARG_ALLOC_PRIV; 168962306a36Sopenharmony_ci pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (IS_ERR(pool)) 169362306a36Sopenharmony_ci return PTR_ERR(pool); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci optee = kzalloc(sizeof(*optee), GFP_KERNEL); 169662306a36Sopenharmony_ci if (!optee) { 169762306a36Sopenharmony_ci rc = -ENOMEM; 169862306a36Sopenharmony_ci goto err_free_pool; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci optee->ops = &optee_ops; 170262306a36Sopenharmony_ci optee->smc.invoke_fn = invoke_fn; 170362306a36Sopenharmony_ci optee->smc.sec_caps = sec_caps; 170462306a36Sopenharmony_ci optee->rpc_param_count = rpc_param_count; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); 170762306a36Sopenharmony_ci if (IS_ERR(teedev)) { 170862306a36Sopenharmony_ci rc = PTR_ERR(teedev); 170962306a36Sopenharmony_ci goto err_free_optee; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci optee->teedev = teedev; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); 171462306a36Sopenharmony_ci if (IS_ERR(teedev)) { 171562306a36Sopenharmony_ci rc = PTR_ERR(teedev); 171662306a36Sopenharmony_ci goto err_unreg_teedev; 171762306a36Sopenharmony_ci } 171862306a36Sopenharmony_ci optee->supp_teedev = teedev; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci rc = tee_device_register(optee->teedev); 172162306a36Sopenharmony_ci if (rc) 172262306a36Sopenharmony_ci goto err_unreg_supp_teedev; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci rc = tee_device_register(optee->supp_teedev); 172562306a36Sopenharmony_ci if (rc) 172662306a36Sopenharmony_ci goto err_unreg_supp_teedev; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci mutex_init(&optee->call_queue.mutex); 172962306a36Sopenharmony_ci INIT_LIST_HEAD(&optee->call_queue.waiters); 173062306a36Sopenharmony_ci optee_supp_init(&optee->supp); 173162306a36Sopenharmony_ci optee->smc.memremaped_shm = memremaped_shm; 173262306a36Sopenharmony_ci optee->pool = pool; 173362306a36Sopenharmony_ci optee_shm_arg_cache_init(optee, arg_cache_flags); 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci platform_set_drvdata(pdev, optee); 173662306a36Sopenharmony_ci ctx = teedev_open(optee->teedev); 173762306a36Sopenharmony_ci if (IS_ERR(ctx)) { 173862306a36Sopenharmony_ci rc = PTR_ERR(ctx); 173962306a36Sopenharmony_ci goto err_supp_uninit; 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci optee->ctx = ctx; 174262306a36Sopenharmony_ci rc = optee_notif_init(optee, max_notif_value); 174362306a36Sopenharmony_ci if (rc) 174462306a36Sopenharmony_ci goto err_close_ctx; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci if (sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) { 174762306a36Sopenharmony_ci unsigned int irq; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci rc = platform_get_irq(pdev, 0); 175062306a36Sopenharmony_ci if (rc < 0) { 175162306a36Sopenharmony_ci pr_err("platform_get_irq: ret %d\n", rc); 175262306a36Sopenharmony_ci goto err_notif_uninit; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci irq = rc; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci rc = optee_smc_notif_init_irq(optee, irq); 175762306a36Sopenharmony_ci if (rc) { 175862306a36Sopenharmony_ci irq_dispose_mapping(irq); 175962306a36Sopenharmony_ci goto err_notif_uninit; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci enable_async_notif(optee->smc.invoke_fn); 176262306a36Sopenharmony_ci pr_info("Asynchronous notifications enabled\n"); 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci /* 176662306a36Sopenharmony_ci * Ensure that there are no pre-existing shm objects before enabling 176762306a36Sopenharmony_ci * the shm cache so that there's no chance of receiving an invalid 176862306a36Sopenharmony_ci * address during shutdown. This could occur, for example, if we're 176962306a36Sopenharmony_ci * kexec booting from an older kernel that did not properly cleanup the 177062306a36Sopenharmony_ci * shm cache. 177162306a36Sopenharmony_ci */ 177262306a36Sopenharmony_ci optee_disable_unmapped_shm_cache(optee); 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci /* 177562306a36Sopenharmony_ci * Only enable the shm cache in case we're not able to pass the RPC 177662306a36Sopenharmony_ci * arg struct right after the normal arg struct. 177762306a36Sopenharmony_ci */ 177862306a36Sopenharmony_ci if (!optee->rpc_param_count) 177962306a36Sopenharmony_ci optee_enable_shm_cache(optee); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) 178262306a36Sopenharmony_ci pr_info("dynamic shared memory is enabled\n"); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); 178562306a36Sopenharmony_ci if (rc) 178662306a36Sopenharmony_ci goto err_disable_shm_cache; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci pr_info("initialized driver\n"); 178962306a36Sopenharmony_ci return 0; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_cierr_disable_shm_cache: 179262306a36Sopenharmony_ci if (!optee->rpc_param_count) 179362306a36Sopenharmony_ci optee_disable_shm_cache(optee); 179462306a36Sopenharmony_ci optee_smc_notif_uninit_irq(optee); 179562306a36Sopenharmony_ci optee_unregister_devices(); 179662306a36Sopenharmony_cierr_notif_uninit: 179762306a36Sopenharmony_ci optee_notif_uninit(optee); 179862306a36Sopenharmony_cierr_close_ctx: 179962306a36Sopenharmony_ci teedev_close_context(ctx); 180062306a36Sopenharmony_cierr_supp_uninit: 180162306a36Sopenharmony_ci optee_shm_arg_cache_uninit(optee); 180262306a36Sopenharmony_ci optee_supp_uninit(&optee->supp); 180362306a36Sopenharmony_ci mutex_destroy(&optee->call_queue.mutex); 180462306a36Sopenharmony_cierr_unreg_supp_teedev: 180562306a36Sopenharmony_ci tee_device_unregister(optee->supp_teedev); 180662306a36Sopenharmony_cierr_unreg_teedev: 180762306a36Sopenharmony_ci tee_device_unregister(optee->teedev); 180862306a36Sopenharmony_cierr_free_optee: 180962306a36Sopenharmony_ci kfree(optee); 181062306a36Sopenharmony_cierr_free_pool: 181162306a36Sopenharmony_ci tee_shm_pool_free(pool); 181262306a36Sopenharmony_ci if (memremaped_shm) 181362306a36Sopenharmony_ci memunmap(memremaped_shm); 181462306a36Sopenharmony_ci return rc; 181562306a36Sopenharmony_ci} 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_cistatic const struct of_device_id optee_dt_match[] = { 181862306a36Sopenharmony_ci { .compatible = "linaro,optee-tz" }, 181962306a36Sopenharmony_ci {}, 182062306a36Sopenharmony_ci}; 182162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, optee_dt_match); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_cistatic struct platform_driver optee_driver = { 182462306a36Sopenharmony_ci .probe = optee_probe, 182562306a36Sopenharmony_ci .remove = optee_smc_remove, 182662306a36Sopenharmony_ci .shutdown = optee_shutdown, 182762306a36Sopenharmony_ci .driver = { 182862306a36Sopenharmony_ci .name = "optee", 182962306a36Sopenharmony_ci .of_match_table = optee_dt_match, 183062306a36Sopenharmony_ci }, 183162306a36Sopenharmony_ci}; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ciint optee_smc_abi_register(void) 183462306a36Sopenharmony_ci{ 183562306a36Sopenharmony_ci return platform_driver_register(&optee_driver); 183662306a36Sopenharmony_ci} 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_civoid optee_smc_abi_unregister(void) 183962306a36Sopenharmony_ci{ 184062306a36Sopenharmony_ci platform_driver_unregister(&optee_driver); 184162306a36Sopenharmony_ci} 1842