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