162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015-2017, 2019-2021 Linaro Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/anon_inodes.h> 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/idr.h> 862306a36Sopenharmony_ci#include <linux/mm.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/tee_drv.h> 1262306a36Sopenharmony_ci#include <linux/uaccess.h> 1362306a36Sopenharmony_ci#include <linux/uio.h> 1462306a36Sopenharmony_ci#include <linux/highmem.h> 1562306a36Sopenharmony_ci#include "tee_private.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic void shm_put_kernel_pages(struct page **pages, size_t page_count) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci size_t n; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci for (n = 0; n < page_count; n++) 2262306a36Sopenharmony_ci put_page(pages[n]); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int shm_get_kernel_pages(unsigned long start, size_t page_count, 2662306a36Sopenharmony_ci struct page **pages) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct page *page; 2962306a36Sopenharmony_ci size_t n; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (WARN_ON_ONCE(is_vmalloc_addr((void *)start) || 3262306a36Sopenharmony_ci is_kmap_addr((void *)start))) 3362306a36Sopenharmony_ci return -EINVAL; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci page = virt_to_page((void *)start); 3662306a36Sopenharmony_ci for (n = 0; n < page_count; n++) { 3762306a36Sopenharmony_ci pages[n] = page + n; 3862306a36Sopenharmony_ci get_page(pages[n]); 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return page_count; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void release_registered_pages(struct tee_shm *shm) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci if (shm->pages) { 4762306a36Sopenharmony_ci if (shm->flags & TEE_SHM_USER_MAPPED) 4862306a36Sopenharmony_ci unpin_user_pages(shm->pages, shm->num_pages); 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci shm_put_kernel_pages(shm->pages, shm->num_pages); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci kfree(shm->pages); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci if (shm->flags & TEE_SHM_POOL) { 5962306a36Sopenharmony_ci teedev->pool->ops->free(teedev->pool, shm); 6062306a36Sopenharmony_ci } else if (shm->flags & TEE_SHM_DYNAMIC) { 6162306a36Sopenharmony_ci int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (rc) 6462306a36Sopenharmony_ci dev_err(teedev->dev.parent, 6562306a36Sopenharmony_ci "unregister shm %p failed: %d", shm, rc); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci release_registered_pages(shm); 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci teedev_ctx_put(shm->ctx); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci kfree(shm); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci tee_device_put(teedev); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct tee_shm *shm_alloc_helper(struct tee_context *ctx, size_t size, 7862306a36Sopenharmony_ci size_t align, u32 flags, int id) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 8162306a36Sopenharmony_ci struct tee_shm *shm; 8262306a36Sopenharmony_ci void *ret; 8362306a36Sopenharmony_ci int rc; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (!tee_device_get(teedev)) 8662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!teedev->pool) { 8962306a36Sopenharmony_ci /* teedev has been detached from driver */ 9062306a36Sopenharmony_ci ret = ERR_PTR(-EINVAL); 9162306a36Sopenharmony_ci goto err_dev_put; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci shm = kzalloc(sizeof(*shm), GFP_KERNEL); 9562306a36Sopenharmony_ci if (!shm) { 9662306a36Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 9762306a36Sopenharmony_ci goto err_dev_put; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci refcount_set(&shm->refcount, 1); 10162306a36Sopenharmony_ci shm->flags = flags; 10262306a36Sopenharmony_ci shm->id = id; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * We're assigning this as it is needed if the shm is to be 10662306a36Sopenharmony_ci * registered. If this function returns OK then the caller expected 10762306a36Sopenharmony_ci * to call teedev_ctx_get() or clear shm->ctx in case it's not 10862306a36Sopenharmony_ci * needed any longer. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci shm->ctx = ctx; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci rc = teedev->pool->ops->alloc(teedev->pool, shm, size, align); 11362306a36Sopenharmony_ci if (rc) { 11462306a36Sopenharmony_ci ret = ERR_PTR(rc); 11562306a36Sopenharmony_ci goto err_kfree; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci teedev_ctx_get(ctx); 11962306a36Sopenharmony_ci return shm; 12062306a36Sopenharmony_cierr_kfree: 12162306a36Sopenharmony_ci kfree(shm); 12262306a36Sopenharmony_cierr_dev_put: 12362306a36Sopenharmony_ci tee_device_put(teedev); 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * tee_shm_alloc_user_buf() - Allocate shared memory for user space 12962306a36Sopenharmony_ci * @ctx: Context that allocates the shared memory 13062306a36Sopenharmony_ci * @size: Requested size of shared memory 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * Memory allocated as user space shared memory is automatically freed when 13362306a36Sopenharmony_ci * the TEE file pointer is closed. The primary usage of this function is 13462306a36Sopenharmony_ci * when the TEE driver doesn't support registering ordinary user space 13562306a36Sopenharmony_ci * memory. 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * @returns a pointer to 'struct tee_shm' 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistruct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci u32 flags = TEE_SHM_DYNAMIC | TEE_SHM_POOL; 14262306a36Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 14362306a36Sopenharmony_ci struct tee_shm *shm; 14462306a36Sopenharmony_ci void *ret; 14562306a36Sopenharmony_ci int id; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 14862306a36Sopenharmony_ci id = idr_alloc(&teedev->idr, NULL, 1, 0, GFP_KERNEL); 14962306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 15062306a36Sopenharmony_ci if (id < 0) 15162306a36Sopenharmony_ci return ERR_PTR(id); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci shm = shm_alloc_helper(ctx, size, PAGE_SIZE, flags, id); 15462306a36Sopenharmony_ci if (IS_ERR(shm)) { 15562306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 15662306a36Sopenharmony_ci idr_remove(&teedev->idr, id); 15762306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 15862306a36Sopenharmony_ci return shm; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 16262306a36Sopenharmony_ci ret = idr_replace(&teedev->idr, shm, id); 16362306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 16462306a36Sopenharmony_ci if (IS_ERR(ret)) { 16562306a36Sopenharmony_ci tee_shm_free(shm); 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return shm; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/** 17362306a36Sopenharmony_ci * tee_shm_alloc_kernel_buf() - Allocate shared memory for kernel buffer 17462306a36Sopenharmony_ci * @ctx: Context that allocates the shared memory 17562306a36Sopenharmony_ci * @size: Requested size of shared memory 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * The returned memory registered in secure world and is suitable to be 17862306a36Sopenharmony_ci * passed as a memory buffer in parameter argument to 17962306a36Sopenharmony_ci * tee_client_invoke_func(). The memory allocated is later freed with a 18062306a36Sopenharmony_ci * call to tee_shm_free(). 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * @returns a pointer to 'struct tee_shm' 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistruct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 flags = TEE_SHM_DYNAMIC | TEE_SHM_POOL; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return shm_alloc_helper(ctx, size, PAGE_SIZE, flags, -1); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/** 19362306a36Sopenharmony_ci * tee_shm_alloc_priv_buf() - Allocate shared memory for a privately shared 19462306a36Sopenharmony_ci * kernel buffer 19562306a36Sopenharmony_ci * @ctx: Context that allocates the shared memory 19662306a36Sopenharmony_ci * @size: Requested size of shared memory 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * This function returns similar shared memory as 19962306a36Sopenharmony_ci * tee_shm_alloc_kernel_buf(), but with the difference that the memory 20062306a36Sopenharmony_ci * might not be registered in secure world in case the driver supports 20162306a36Sopenharmony_ci * passing memory not registered in advance. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * This function should normally only be used internally in the TEE 20462306a36Sopenharmony_ci * drivers. 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * @returns a pointer to 'struct tee_shm' 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_cistruct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci u32 flags = TEE_SHM_PRIV | TEE_SHM_POOL; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return shm_alloc_helper(ctx, size, sizeof(long) * 2, flags, -1); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_alloc_priv_buf); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic struct tee_shm * 21762306a36Sopenharmony_ciregister_shm_helper(struct tee_context *ctx, unsigned long addr, 21862306a36Sopenharmony_ci size_t length, u32 flags, int id) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 22162306a36Sopenharmony_ci struct tee_shm *shm; 22262306a36Sopenharmony_ci unsigned long start; 22362306a36Sopenharmony_ci size_t num_pages; 22462306a36Sopenharmony_ci void *ret; 22562306a36Sopenharmony_ci int rc; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!tee_device_get(teedev)) 22862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!teedev->desc->ops->shm_register || 23162306a36Sopenharmony_ci !teedev->desc->ops->shm_unregister) { 23262306a36Sopenharmony_ci ret = ERR_PTR(-ENOTSUPP); 23362306a36Sopenharmony_ci goto err_dev_put; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci teedev_ctx_get(ctx); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci shm = kzalloc(sizeof(*shm), GFP_KERNEL); 23962306a36Sopenharmony_ci if (!shm) { 24062306a36Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 24162306a36Sopenharmony_ci goto err_ctx_put; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci refcount_set(&shm->refcount, 1); 24562306a36Sopenharmony_ci shm->flags = flags; 24662306a36Sopenharmony_ci shm->ctx = ctx; 24762306a36Sopenharmony_ci shm->id = id; 24862306a36Sopenharmony_ci addr = untagged_addr(addr); 24962306a36Sopenharmony_ci start = rounddown(addr, PAGE_SIZE); 25062306a36Sopenharmony_ci shm->offset = addr - start; 25162306a36Sopenharmony_ci shm->size = length; 25262306a36Sopenharmony_ci num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE; 25362306a36Sopenharmony_ci shm->pages = kcalloc(num_pages, sizeof(*shm->pages), GFP_KERNEL); 25462306a36Sopenharmony_ci if (!shm->pages) { 25562306a36Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 25662306a36Sopenharmony_ci goto err_free_shm; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (flags & TEE_SHM_USER_MAPPED) 26062306a36Sopenharmony_ci rc = pin_user_pages_fast(start, num_pages, FOLL_WRITE, 26162306a36Sopenharmony_ci shm->pages); 26262306a36Sopenharmony_ci else 26362306a36Sopenharmony_ci rc = shm_get_kernel_pages(start, num_pages, shm->pages); 26462306a36Sopenharmony_ci if (rc > 0) 26562306a36Sopenharmony_ci shm->num_pages = rc; 26662306a36Sopenharmony_ci if (rc != num_pages) { 26762306a36Sopenharmony_ci if (rc >= 0) 26862306a36Sopenharmony_ci rc = -ENOMEM; 26962306a36Sopenharmony_ci ret = ERR_PTR(rc); 27062306a36Sopenharmony_ci goto err_put_shm_pages; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci rc = teedev->desc->ops->shm_register(ctx, shm, shm->pages, 27462306a36Sopenharmony_ci shm->num_pages, start); 27562306a36Sopenharmony_ci if (rc) { 27662306a36Sopenharmony_ci ret = ERR_PTR(rc); 27762306a36Sopenharmony_ci goto err_put_shm_pages; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return shm; 28162306a36Sopenharmony_cierr_put_shm_pages: 28262306a36Sopenharmony_ci if (flags & TEE_SHM_USER_MAPPED) 28362306a36Sopenharmony_ci unpin_user_pages(shm->pages, shm->num_pages); 28462306a36Sopenharmony_ci else 28562306a36Sopenharmony_ci shm_put_kernel_pages(shm->pages, shm->num_pages); 28662306a36Sopenharmony_ci kfree(shm->pages); 28762306a36Sopenharmony_cierr_free_shm: 28862306a36Sopenharmony_ci kfree(shm); 28962306a36Sopenharmony_cierr_ctx_put: 29062306a36Sopenharmony_ci teedev_ctx_put(ctx); 29162306a36Sopenharmony_cierr_dev_put: 29262306a36Sopenharmony_ci tee_device_put(teedev); 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/** 29762306a36Sopenharmony_ci * tee_shm_register_user_buf() - Register a userspace shared memory buffer 29862306a36Sopenharmony_ci * @ctx: Context that registers the shared memory 29962306a36Sopenharmony_ci * @addr: The userspace address of the shared buffer 30062306a36Sopenharmony_ci * @length: Length of the shared buffer 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * @returns a pointer to 'struct tee_shm' 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_cistruct tee_shm *tee_shm_register_user_buf(struct tee_context *ctx, 30562306a36Sopenharmony_ci unsigned long addr, size_t length) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci u32 flags = TEE_SHM_USER_MAPPED | TEE_SHM_DYNAMIC; 30862306a36Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 30962306a36Sopenharmony_ci struct tee_shm *shm; 31062306a36Sopenharmony_ci void *ret; 31162306a36Sopenharmony_ci int id; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!access_ok((void __user *)addr, length)) 31462306a36Sopenharmony_ci return ERR_PTR(-EFAULT); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 31762306a36Sopenharmony_ci id = idr_alloc(&teedev->idr, NULL, 1, 0, GFP_KERNEL); 31862306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 31962306a36Sopenharmony_ci if (id < 0) 32062306a36Sopenharmony_ci return ERR_PTR(id); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci shm = register_shm_helper(ctx, addr, length, flags, id); 32362306a36Sopenharmony_ci if (IS_ERR(shm)) { 32462306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 32562306a36Sopenharmony_ci idr_remove(&teedev->idr, id); 32662306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 32762306a36Sopenharmony_ci return shm; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 33162306a36Sopenharmony_ci ret = idr_replace(&teedev->idr, shm, id); 33262306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 33362306a36Sopenharmony_ci if (IS_ERR(ret)) { 33462306a36Sopenharmony_ci tee_shm_free(shm); 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return shm; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/** 34262306a36Sopenharmony_ci * tee_shm_register_kernel_buf() - Register kernel memory to be shared with 34362306a36Sopenharmony_ci * secure world 34462306a36Sopenharmony_ci * @ctx: Context that registers the shared memory 34562306a36Sopenharmony_ci * @addr: The buffer 34662306a36Sopenharmony_ci * @length: Length of the buffer 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * @returns a pointer to 'struct tee_shm' 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistruct tee_shm *tee_shm_register_kernel_buf(struct tee_context *ctx, 35262306a36Sopenharmony_ci void *addr, size_t length) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci u32 flags = TEE_SHM_DYNAMIC; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return register_shm_helper(ctx, (unsigned long)addr, length, flags, -1); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_register_kernel_buf); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int tee_shm_fop_release(struct inode *inode, struct file *filp) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci tee_shm_put(filp->private_data); 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int tee_shm_fop_mmap(struct file *filp, struct vm_area_struct *vma) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct tee_shm *shm = filp->private_data; 36962306a36Sopenharmony_ci size_t size = vma->vm_end - vma->vm_start; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Refuse sharing shared memory provided by application */ 37262306a36Sopenharmony_ci if (shm->flags & TEE_SHM_USER_MAPPED) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* check for overflowing the buffer's size */ 37662306a36Sopenharmony_ci if (vma->vm_pgoff + vma_pages(vma) > shm->size >> PAGE_SHIFT) 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, 38062306a36Sopenharmony_ci size, vma->vm_page_prot); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic const struct file_operations tee_shm_fops = { 38462306a36Sopenharmony_ci .owner = THIS_MODULE, 38562306a36Sopenharmony_ci .release = tee_shm_fop_release, 38662306a36Sopenharmony_ci .mmap = tee_shm_fop_mmap, 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/** 39062306a36Sopenharmony_ci * tee_shm_get_fd() - Increase reference count and return file descriptor 39162306a36Sopenharmony_ci * @shm: Shared memory handle 39262306a36Sopenharmony_ci * @returns user space file descriptor to shared memory 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ciint tee_shm_get_fd(struct tee_shm *shm) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int fd; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (shm->id < 0) 39962306a36Sopenharmony_ci return -EINVAL; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* matched by tee_shm_put() in tee_shm_op_release() */ 40262306a36Sopenharmony_ci refcount_inc(&shm->refcount); 40362306a36Sopenharmony_ci fd = anon_inode_getfd("tee_shm", &tee_shm_fops, shm, O_RDWR); 40462306a36Sopenharmony_ci if (fd < 0) 40562306a36Sopenharmony_ci tee_shm_put(shm); 40662306a36Sopenharmony_ci return fd; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/** 41062306a36Sopenharmony_ci * tee_shm_free() - Free shared memory 41162306a36Sopenharmony_ci * @shm: Handle to shared memory to free 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_civoid tee_shm_free(struct tee_shm *shm) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci tee_shm_put(shm); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_free); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * tee_shm_get_va() - Get virtual address of a shared memory plus an offset 42162306a36Sopenharmony_ci * @shm: Shared memory handle 42262306a36Sopenharmony_ci * @offs: Offset from start of this shared memory 42362306a36Sopenharmony_ci * @returns virtual address of the shared memory + offs if offs is within 42462306a36Sopenharmony_ci * the bounds of this shared memory, else an ERR_PTR 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_civoid *tee_shm_get_va(struct tee_shm *shm, size_t offs) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci if (!shm->kaddr) 42962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 43062306a36Sopenharmony_ci if (offs >= shm->size) 43162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 43262306a36Sopenharmony_ci return (char *)shm->kaddr + offs; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_get_va); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * tee_shm_get_pa() - Get physical address of a shared memory plus an offset 43862306a36Sopenharmony_ci * @shm: Shared memory handle 43962306a36Sopenharmony_ci * @offs: Offset from start of this shared memory 44062306a36Sopenharmony_ci * @pa: Physical address to return 44162306a36Sopenharmony_ci * @returns 0 if offs is within the bounds of this shared memory, else an 44262306a36Sopenharmony_ci * error code. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ciint tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci if (offs >= shm->size) 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci if (pa) 44962306a36Sopenharmony_ci *pa = shm->paddr + offs; 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_get_pa); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/** 45562306a36Sopenharmony_ci * tee_shm_get_from_id() - Find shared memory object and increase reference 45662306a36Sopenharmony_ci * count 45762306a36Sopenharmony_ci * @ctx: Context owning the shared memory 45862306a36Sopenharmony_ci * @id: Id of shared memory object 45962306a36Sopenharmony_ci * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_cistruct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct tee_device *teedev; 46462306a36Sopenharmony_ci struct tee_shm *shm; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!ctx) 46762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci teedev = ctx->teedev; 47062306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 47162306a36Sopenharmony_ci shm = idr_find(&teedev->idr, id); 47262306a36Sopenharmony_ci /* 47362306a36Sopenharmony_ci * If the tee_shm was found in the IDR it must have a refcount 47462306a36Sopenharmony_ci * larger than 0 due to the guarantee in tee_shm_put() below. So 47562306a36Sopenharmony_ci * it's safe to use refcount_inc(). 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci if (!shm || shm->ctx != ctx) 47862306a36Sopenharmony_ci shm = ERR_PTR(-EINVAL); 47962306a36Sopenharmony_ci else 48062306a36Sopenharmony_ci refcount_inc(&shm->refcount); 48162306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 48262306a36Sopenharmony_ci return shm; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_get_from_id); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * tee_shm_put() - Decrease reference count on a shared memory handle 48862306a36Sopenharmony_ci * @shm: Shared memory handle 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_civoid tee_shm_put(struct tee_shm *shm) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct tee_device *teedev = shm->ctx->teedev; 49362306a36Sopenharmony_ci bool do_release = false; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci mutex_lock(&teedev->mutex); 49662306a36Sopenharmony_ci if (refcount_dec_and_test(&shm->refcount)) { 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * refcount has reached 0, we must now remove it from the 49962306a36Sopenharmony_ci * IDR before releasing the mutex. This will guarantee that 50062306a36Sopenharmony_ci * the refcount_inc() in tee_shm_get_from_id() never starts 50162306a36Sopenharmony_ci * from 0. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci if (shm->id >= 0) 50462306a36Sopenharmony_ci idr_remove(&teedev->idr, shm->id); 50562306a36Sopenharmony_ci do_release = true; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci mutex_unlock(&teedev->mutex); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (do_release) 51062306a36Sopenharmony_ci tee_shm_release(teedev, shm); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_put); 513