162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015, 2017, 2022 Linaro Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/device.h>
662306a36Sopenharmony_ci#include <linux/dma-buf.h>
762306a36Sopenharmony_ci#include <linux/genalloc.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/tee_drv.h>
1062306a36Sopenharmony_ci#include "tee_private.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic int pool_op_gen_alloc(struct tee_shm_pool *pool, struct tee_shm *shm,
1362306a36Sopenharmony_ci			     size_t size, size_t align)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	unsigned long va;
1662306a36Sopenharmony_ci	struct gen_pool *genpool = pool->private_data;
1762306a36Sopenharmony_ci	size_t a = max_t(size_t, align, BIT(genpool->min_alloc_order));
1862306a36Sopenharmony_ci	struct genpool_data_align data = { .align = a };
1962306a36Sopenharmony_ci	size_t s = roundup(size, a);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	va = gen_pool_alloc_algo(genpool, s, gen_pool_first_fit_align, &data);
2262306a36Sopenharmony_ci	if (!va)
2362306a36Sopenharmony_ci		return -ENOMEM;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	memset((void *)va, 0, s);
2662306a36Sopenharmony_ci	shm->kaddr = (void *)va;
2762306a36Sopenharmony_ci	shm->paddr = gen_pool_virt_to_phys(genpool, va);
2862306a36Sopenharmony_ci	shm->size = s;
2962306a36Sopenharmony_ci	/*
3062306a36Sopenharmony_ci	 * This is from a static shared memory pool so no need to register
3162306a36Sopenharmony_ci	 * each chunk, and no need to unregister later either.
3262306a36Sopenharmony_ci	 */
3362306a36Sopenharmony_ci	shm->flags &= ~TEE_SHM_DYNAMIC;
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void pool_op_gen_free(struct tee_shm_pool *pool, struct tee_shm *shm)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	gen_pool_free(pool->private_data, (unsigned long)shm->kaddr,
4062306a36Sopenharmony_ci		      shm->size);
4162306a36Sopenharmony_ci	shm->kaddr = NULL;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void pool_op_gen_destroy_pool(struct tee_shm_pool *pool)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	gen_pool_destroy(pool->private_data);
4762306a36Sopenharmony_ci	kfree(pool);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const struct tee_shm_pool_ops pool_ops_generic = {
5162306a36Sopenharmony_ci	.alloc = pool_op_gen_alloc,
5262306a36Sopenharmony_ci	.free = pool_op_gen_free,
5362306a36Sopenharmony_ci	.destroy_pool = pool_op_gen_destroy_pool,
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct tee_shm_pool *tee_shm_pool_alloc_res_mem(unsigned long vaddr,
5762306a36Sopenharmony_ci						phys_addr_t paddr, size_t size,
5862306a36Sopenharmony_ci						int min_alloc_order)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	const size_t page_mask = PAGE_SIZE - 1;
6162306a36Sopenharmony_ci	struct tee_shm_pool *pool;
6262306a36Sopenharmony_ci	int rc;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Start and end must be page aligned */
6562306a36Sopenharmony_ci	if (vaddr & page_mask || paddr & page_mask || size & page_mask)
6662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	pool = kzalloc(sizeof(*pool), GFP_KERNEL);
6962306a36Sopenharmony_ci	if (!pool)
7062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	pool->private_data = gen_pool_create(min_alloc_order, -1);
7362306a36Sopenharmony_ci	if (!pool->private_data) {
7462306a36Sopenharmony_ci		rc = -ENOMEM;
7562306a36Sopenharmony_ci		goto err;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	rc = gen_pool_add_virt(pool->private_data, vaddr, paddr, size, -1);
7962306a36Sopenharmony_ci	if (rc) {
8062306a36Sopenharmony_ci		gen_pool_destroy(pool->private_data);
8162306a36Sopenharmony_ci		goto err;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	pool->ops = &pool_ops_generic;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return pool;
8762306a36Sopenharmony_cierr:
8862306a36Sopenharmony_ci	kfree(pool);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return ERR_PTR(rc);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
93