18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015-2017, 2019-2021 Linaro Limited 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/anon_inodes.h> 68c2ecf20Sopenharmony_ci#include <linux/device.h> 78c2ecf20Sopenharmony_ci#include <linux/idr.h> 88c2ecf20Sopenharmony_ci#include <linux/mm.h> 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/tee_drv.h> 128c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 138c2ecf20Sopenharmony_ci#include <linux/uio.h> 148c2ecf20Sopenharmony_ci#include "tee_private.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic void release_registered_pages(struct tee_shm *shm) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci if (shm->pages) { 198c2ecf20Sopenharmony_ci if (shm->flags & TEE_SHM_USER_MAPPED) { 208c2ecf20Sopenharmony_ci unpin_user_pages(shm->pages, shm->num_pages); 218c2ecf20Sopenharmony_ci } else { 228c2ecf20Sopenharmony_ci size_t n; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci for (n = 0; n < shm->num_pages; n++) 258c2ecf20Sopenharmony_ci put_page(shm->pages[n]); 268c2ecf20Sopenharmony_ci } 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci kfree(shm->pages); 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci if (shm->flags & TEE_SHM_POOL) { 358c2ecf20Sopenharmony_ci struct tee_shm_pool_mgr *poolm; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (shm->flags & TEE_SHM_DMA_BUF) 388c2ecf20Sopenharmony_ci poolm = teedev->pool->dma_buf_mgr; 398c2ecf20Sopenharmony_ci else 408c2ecf20Sopenharmony_ci poolm = teedev->pool->private_mgr; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci poolm->ops->free(poolm, shm); 438c2ecf20Sopenharmony_ci } else if (shm->flags & TEE_SHM_REGISTER) { 448c2ecf20Sopenharmony_ci int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (rc) 478c2ecf20Sopenharmony_ci dev_err(teedev->dev.parent, 488c2ecf20Sopenharmony_ci "unregister shm %p failed: %d", shm, rc); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci release_registered_pages(shm); 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci teedev_ctx_put(shm->ctx); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci kfree(shm); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci tee_device_put(teedev); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 638c2ecf20Sopenharmony_ci struct tee_shm_pool_mgr *poolm = NULL; 648c2ecf20Sopenharmony_ci struct tee_shm *shm; 658c2ecf20Sopenharmony_ci void *ret; 668c2ecf20Sopenharmony_ci int rc; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!(flags & TEE_SHM_MAPPED)) { 698c2ecf20Sopenharmony_ci dev_err(teedev->dev.parent, 708c2ecf20Sopenharmony_ci "only mapped allocations supported\n"); 718c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF | TEE_SHM_PRIV))) { 758c2ecf20Sopenharmony_ci dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags); 768c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (!tee_device_get(teedev)) 808c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (!teedev->pool) { 838c2ecf20Sopenharmony_ci /* teedev has been detached from driver */ 848c2ecf20Sopenharmony_ci ret = ERR_PTR(-EINVAL); 858c2ecf20Sopenharmony_ci goto err_dev_put; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci shm = kzalloc(sizeof(*shm), GFP_KERNEL); 898c2ecf20Sopenharmony_ci if (!shm) { 908c2ecf20Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 918c2ecf20Sopenharmony_ci goto err_dev_put; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci refcount_set(&shm->refcount, 1); 958c2ecf20Sopenharmony_ci shm->flags = flags | TEE_SHM_POOL; 968c2ecf20Sopenharmony_ci shm->ctx = ctx; 978c2ecf20Sopenharmony_ci if (flags & TEE_SHM_DMA_BUF) 988c2ecf20Sopenharmony_ci poolm = teedev->pool->dma_buf_mgr; 998c2ecf20Sopenharmony_ci else 1008c2ecf20Sopenharmony_ci poolm = teedev->pool->private_mgr; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci rc = poolm->ops->alloc(poolm, shm, size); 1038c2ecf20Sopenharmony_ci if (rc) { 1048c2ecf20Sopenharmony_ci ret = ERR_PTR(rc); 1058c2ecf20Sopenharmony_ci goto err_kfree; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (flags & TEE_SHM_DMA_BUF) { 1098c2ecf20Sopenharmony_ci mutex_lock(&teedev->mutex); 1108c2ecf20Sopenharmony_ci shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); 1118c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 1128c2ecf20Sopenharmony_ci if (shm->id < 0) { 1138c2ecf20Sopenharmony_ci ret = ERR_PTR(shm->id); 1148c2ecf20Sopenharmony_ci goto err_pool_free; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci teedev_ctx_get(ctx); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return shm; 1218c2ecf20Sopenharmony_cierr_pool_free: 1228c2ecf20Sopenharmony_ci poolm->ops->free(poolm, shm); 1238c2ecf20Sopenharmony_cierr_kfree: 1248c2ecf20Sopenharmony_ci kfree(shm); 1258c2ecf20Sopenharmony_cierr_dev_put: 1268c2ecf20Sopenharmony_ci tee_device_put(teedev); 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_alloc); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/** 1328c2ecf20Sopenharmony_ci * tee_shm_alloc_kernel_buf() - Allocate shared memory for kernel buffer 1338c2ecf20Sopenharmony_ci * @ctx: Context that allocates the shared memory 1348c2ecf20Sopenharmony_ci * @size: Requested size of shared memory 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * The returned memory registered in secure world and is suitable to be 1378c2ecf20Sopenharmony_ci * passed as a memory buffer in parameter argument to 1388c2ecf20Sopenharmony_ci * tee_client_invoke_func(). The memory allocated is later freed with a 1398c2ecf20Sopenharmony_ci * call to tee_shm_free(). 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * @returns a pointer to 'struct tee_shm' 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistruct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistruct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, 1508c2ecf20Sopenharmony_ci size_t length, u32 flags) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 1538c2ecf20Sopenharmony_ci const u32 req_user_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED; 1548c2ecf20Sopenharmony_ci const u32 req_kernel_flags = TEE_SHM_DMA_BUF | TEE_SHM_KERNEL_MAPPED; 1558c2ecf20Sopenharmony_ci struct tee_shm *shm; 1568c2ecf20Sopenharmony_ci void *ret; 1578c2ecf20Sopenharmony_ci int rc; 1588c2ecf20Sopenharmony_ci int num_pages; 1598c2ecf20Sopenharmony_ci unsigned long start; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (flags != req_user_flags && flags != req_kernel_flags) 1628c2ecf20Sopenharmony_ci return ERR_PTR(-ENOTSUPP); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!tee_device_get(teedev)) 1658c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!teedev->desc->ops->shm_register || 1688c2ecf20Sopenharmony_ci !teedev->desc->ops->shm_unregister) { 1698c2ecf20Sopenharmony_ci tee_device_put(teedev); 1708c2ecf20Sopenharmony_ci return ERR_PTR(-ENOTSUPP); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci teedev_ctx_get(ctx); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci shm = kzalloc(sizeof(*shm), GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (!shm) { 1778c2ecf20Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 1788c2ecf20Sopenharmony_ci goto err; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci refcount_set(&shm->refcount, 1); 1828c2ecf20Sopenharmony_ci shm->flags = flags | TEE_SHM_REGISTER; 1838c2ecf20Sopenharmony_ci shm->ctx = ctx; 1848c2ecf20Sopenharmony_ci shm->id = -1; 1858c2ecf20Sopenharmony_ci addr = untagged_addr(addr); 1868c2ecf20Sopenharmony_ci start = rounddown(addr, PAGE_SIZE); 1878c2ecf20Sopenharmony_ci shm->offset = addr - start; 1888c2ecf20Sopenharmony_ci shm->size = length; 1898c2ecf20Sopenharmony_ci num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE; 1908c2ecf20Sopenharmony_ci shm->pages = kcalloc(num_pages, sizeof(*shm->pages), GFP_KERNEL); 1918c2ecf20Sopenharmony_ci if (!shm->pages) { 1928c2ecf20Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 1938c2ecf20Sopenharmony_ci goto err; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (flags & TEE_SHM_USER_MAPPED) { 1978c2ecf20Sopenharmony_ci rc = pin_user_pages_fast(start, num_pages, FOLL_WRITE, 1988c2ecf20Sopenharmony_ci shm->pages); 1998c2ecf20Sopenharmony_ci } else { 2008c2ecf20Sopenharmony_ci struct kvec *kiov; 2018c2ecf20Sopenharmony_ci int i; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci kiov = kcalloc(num_pages, sizeof(*kiov), GFP_KERNEL); 2048c2ecf20Sopenharmony_ci if (!kiov) { 2058c2ecf20Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 2068c2ecf20Sopenharmony_ci goto err; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci for (i = 0; i < num_pages; i++) { 2108c2ecf20Sopenharmony_ci kiov[i].iov_base = (void *)(start + i * PAGE_SIZE); 2118c2ecf20Sopenharmony_ci kiov[i].iov_len = PAGE_SIZE; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci rc = get_kernel_pages(kiov, num_pages, 0, shm->pages); 2158c2ecf20Sopenharmony_ci kfree(kiov); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci if (rc > 0) 2188c2ecf20Sopenharmony_ci shm->num_pages = rc; 2198c2ecf20Sopenharmony_ci if (rc != num_pages) { 2208c2ecf20Sopenharmony_ci if (rc >= 0) 2218c2ecf20Sopenharmony_ci rc = -ENOMEM; 2228c2ecf20Sopenharmony_ci ret = ERR_PTR(rc); 2238c2ecf20Sopenharmony_ci goto err; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci mutex_lock(&teedev->mutex); 2278c2ecf20Sopenharmony_ci shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); 2288c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (shm->id < 0) { 2318c2ecf20Sopenharmony_ci ret = ERR_PTR(shm->id); 2328c2ecf20Sopenharmony_ci goto err; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci rc = teedev->desc->ops->shm_register(ctx, shm, shm->pages, 2368c2ecf20Sopenharmony_ci shm->num_pages, start); 2378c2ecf20Sopenharmony_ci if (rc) { 2388c2ecf20Sopenharmony_ci ret = ERR_PTR(rc); 2398c2ecf20Sopenharmony_ci goto err; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return shm; 2438c2ecf20Sopenharmony_cierr: 2448c2ecf20Sopenharmony_ci if (shm) { 2458c2ecf20Sopenharmony_ci if (shm->id >= 0) { 2468c2ecf20Sopenharmony_ci mutex_lock(&teedev->mutex); 2478c2ecf20Sopenharmony_ci idr_remove(&teedev->idr, shm->id); 2488c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci release_registered_pages(shm); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci kfree(shm); 2538c2ecf20Sopenharmony_ci teedev_ctx_put(ctx); 2548c2ecf20Sopenharmony_ci tee_device_put(teedev); 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_register); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int tee_shm_fop_release(struct inode *inode, struct file *filp) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci tee_shm_put(filp->private_data); 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int tee_shm_fop_mmap(struct file *filp, struct vm_area_struct *vma) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct tee_shm *shm = filp->private_data; 2688c2ecf20Sopenharmony_ci size_t size = vma->vm_end - vma->vm_start; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* Refuse sharing shared memory provided by application */ 2718c2ecf20Sopenharmony_ci if (shm->flags & TEE_SHM_USER_MAPPED) 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* check for overflowing the buffer's size */ 2758c2ecf20Sopenharmony_ci if (vma->vm_pgoff + vma_pages(vma) > shm->size >> PAGE_SHIFT) 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, 2798c2ecf20Sopenharmony_ci size, vma->vm_page_prot); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic const struct file_operations tee_shm_fops = { 2838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2848c2ecf20Sopenharmony_ci .release = tee_shm_fop_release, 2858c2ecf20Sopenharmony_ci .mmap = tee_shm_fop_mmap, 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * tee_shm_get_fd() - Increase reference count and return file descriptor 2908c2ecf20Sopenharmony_ci * @shm: Shared memory handle 2918c2ecf20Sopenharmony_ci * @returns user space file descriptor to shared memory 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ciint tee_shm_get_fd(struct tee_shm *shm) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci int fd; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (!(shm->flags & TEE_SHM_DMA_BUF)) 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* matched by tee_shm_put() in tee_shm_op_release() */ 3018c2ecf20Sopenharmony_ci refcount_inc(&shm->refcount); 3028c2ecf20Sopenharmony_ci fd = anon_inode_getfd("tee_shm", &tee_shm_fops, shm, O_RDWR); 3038c2ecf20Sopenharmony_ci if (fd < 0) 3048c2ecf20Sopenharmony_ci tee_shm_put(shm); 3058c2ecf20Sopenharmony_ci return fd; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/** 3098c2ecf20Sopenharmony_ci * tee_shm_free() - Free shared memory 3108c2ecf20Sopenharmony_ci * @shm: Handle to shared memory to free 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_civoid tee_shm_free(struct tee_shm *shm) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci tee_shm_put(shm); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_free); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/** 3198c2ecf20Sopenharmony_ci * tee_shm_va2pa() - Get physical address of a virtual address 3208c2ecf20Sopenharmony_ci * @shm: Shared memory handle 3218c2ecf20Sopenharmony_ci * @va: Virtual address to tranlsate 3228c2ecf20Sopenharmony_ci * @pa: Returned physical address 3238c2ecf20Sopenharmony_ci * @returns 0 on success and < 0 on failure 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ciint tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci if (!(shm->flags & TEE_SHM_MAPPED)) 3288c2ecf20Sopenharmony_ci return -EINVAL; 3298c2ecf20Sopenharmony_ci /* Check that we're in the range of the shm */ 3308c2ecf20Sopenharmony_ci if ((char *)va < (char *)shm->kaddr) 3318c2ecf20Sopenharmony_ci return -EINVAL; 3328c2ecf20Sopenharmony_ci if ((char *)va >= ((char *)shm->kaddr + shm->size)) 3338c2ecf20Sopenharmony_ci return -EINVAL; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return tee_shm_get_pa( 3368c2ecf20Sopenharmony_ci shm, (unsigned long)va - (unsigned long)shm->kaddr, pa); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_va2pa); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/** 3418c2ecf20Sopenharmony_ci * tee_shm_pa2va() - Get virtual address of a physical address 3428c2ecf20Sopenharmony_ci * @shm: Shared memory handle 3438c2ecf20Sopenharmony_ci * @pa: Physical address to tranlsate 3448c2ecf20Sopenharmony_ci * @va: Returned virtual address 3458c2ecf20Sopenharmony_ci * @returns 0 on success and < 0 on failure 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ciint tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci if (!(shm->flags & TEE_SHM_MAPPED)) 3508c2ecf20Sopenharmony_ci return -EINVAL; 3518c2ecf20Sopenharmony_ci /* Check that we're in the range of the shm */ 3528c2ecf20Sopenharmony_ci if (pa < shm->paddr) 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci if (pa >= (shm->paddr + shm->size)) 3558c2ecf20Sopenharmony_ci return -EINVAL; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (va) { 3588c2ecf20Sopenharmony_ci void *v = tee_shm_get_va(shm, pa - shm->paddr); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (IS_ERR(v)) 3618c2ecf20Sopenharmony_ci return PTR_ERR(v); 3628c2ecf20Sopenharmony_ci *va = v; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_pa2va); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/** 3698c2ecf20Sopenharmony_ci * tee_shm_get_va() - Get virtual address of a shared memory plus an offset 3708c2ecf20Sopenharmony_ci * @shm: Shared memory handle 3718c2ecf20Sopenharmony_ci * @offs: Offset from start of this shared memory 3728c2ecf20Sopenharmony_ci * @returns virtual address of the shared memory + offs if offs is within 3738c2ecf20Sopenharmony_ci * the bounds of this shared memory, else an ERR_PTR 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_civoid *tee_shm_get_va(struct tee_shm *shm, size_t offs) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci if (!(shm->flags & TEE_SHM_MAPPED)) 3788c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3798c2ecf20Sopenharmony_ci if (offs >= shm->size) 3808c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3818c2ecf20Sopenharmony_ci return (char *)shm->kaddr + offs; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_get_va); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/** 3868c2ecf20Sopenharmony_ci * tee_shm_get_pa() - Get physical address of a shared memory plus an offset 3878c2ecf20Sopenharmony_ci * @shm: Shared memory handle 3888c2ecf20Sopenharmony_ci * @offs: Offset from start of this shared memory 3898c2ecf20Sopenharmony_ci * @pa: Physical address to return 3908c2ecf20Sopenharmony_ci * @returns 0 if offs is within the bounds of this shared memory, else an 3918c2ecf20Sopenharmony_ci * error code. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ciint tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci if (offs >= shm->size) 3968c2ecf20Sopenharmony_ci return -EINVAL; 3978c2ecf20Sopenharmony_ci if (pa) 3988c2ecf20Sopenharmony_ci *pa = shm->paddr + offs; 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_get_pa); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * tee_shm_get_from_id() - Find shared memory object and increase reference 4058c2ecf20Sopenharmony_ci * count 4068c2ecf20Sopenharmony_ci * @ctx: Context owning the shared memory 4078c2ecf20Sopenharmony_ci * @id: Id of shared memory object 4088c2ecf20Sopenharmony_ci * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_cistruct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct tee_device *teedev; 4138c2ecf20Sopenharmony_ci struct tee_shm *shm; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (!ctx) 4168c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci teedev = ctx->teedev; 4198c2ecf20Sopenharmony_ci mutex_lock(&teedev->mutex); 4208c2ecf20Sopenharmony_ci shm = idr_find(&teedev->idr, id); 4218c2ecf20Sopenharmony_ci /* 4228c2ecf20Sopenharmony_ci * If the tee_shm was found in the IDR it must have a refcount 4238c2ecf20Sopenharmony_ci * larger than 0 due to the guarantee in tee_shm_put() below. So 4248c2ecf20Sopenharmony_ci * it's safe to use refcount_inc(). 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci if (!shm || shm->ctx != ctx) 4278c2ecf20Sopenharmony_ci shm = ERR_PTR(-EINVAL); 4288c2ecf20Sopenharmony_ci else 4298c2ecf20Sopenharmony_ci refcount_inc(&shm->refcount); 4308c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 4318c2ecf20Sopenharmony_ci return shm; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_get_from_id); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * tee_shm_put() - Decrease reference count on a shared memory handle 4378c2ecf20Sopenharmony_ci * @shm: Shared memory handle 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_civoid tee_shm_put(struct tee_shm *shm) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct tee_device *teedev = shm->ctx->teedev; 4428c2ecf20Sopenharmony_ci bool do_release = false; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci mutex_lock(&teedev->mutex); 4458c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&shm->refcount)) { 4468c2ecf20Sopenharmony_ci /* 4478c2ecf20Sopenharmony_ci * refcount has reached 0, we must now remove it from the 4488c2ecf20Sopenharmony_ci * IDR before releasing the mutex. This will guarantee that 4498c2ecf20Sopenharmony_ci * the refcount_inc() in tee_shm_get_from_id() never starts 4508c2ecf20Sopenharmony_ci * from 0. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci if (shm->flags & TEE_SHM_DMA_BUF) 4538c2ecf20Sopenharmony_ci idr_remove(&teedev->idr, shm->id); 4548c2ecf20Sopenharmony_ci do_release = true; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (do_release) 4598c2ecf20Sopenharmony_ci tee_shm_release(teedev, shm); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_put); 462