133 lines
3.4 KiB
C
Executable file
133 lines
3.4 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Secure IOVA Management
|
|
*
|
|
* Copyright (c) 2021 Samsung Electronics Co., Ltd.
|
|
* Author: <hyesoo.yu@samsung.com> for Samsung
|
|
*/
|
|
|
|
#include <linux/dma-heap.h>
|
|
#include <linux/genalloc.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/slab.h>
|
|
|
|
#define SECURE_DMA_BASE 0x40000000
|
|
/*
|
|
* MFC have H/W restriction that could only access 0xC000_0000
|
|
* offset from base, so secure device virtual address manager
|
|
* uses the size as 0xC000_0000.
|
|
*/
|
|
#define SECURE_DMA_SIZE 0x40000000
|
|
|
|
static struct gen_pool *secure_iova_pool;
|
|
|
|
/*
|
|
* Alignment to a secure address larger than 16MiB is not beneficial because
|
|
* the protection alignment just needs 64KiB by the buffer protection H/W and
|
|
* the largest granule of H/W security firewall (the secure context of SysMMU)
|
|
* is 16MiB.
|
|
*/
|
|
#define MAX_SECURE_VA_ALIGN (SZ_16M / PAGE_SIZE)
|
|
|
|
unsigned long secure_iova_alloc(unsigned long size, unsigned int align)
|
|
{
|
|
unsigned long out_addr;
|
|
struct genpool_data_align alignment = {
|
|
.align = max_t(int, PFN_DOWN(align), MAX_SECURE_VA_ALIGN),
|
|
};
|
|
|
|
if (WARN_ON_ONCE(!secure_iova_pool))
|
|
return 0;
|
|
|
|
out_addr = gen_pool_alloc_algo(secure_iova_pool, size,
|
|
gen_pool_first_fit_align, &alignment);
|
|
|
|
if (out_addr == 0)
|
|
pr_err("failed alloc secure iova. %zu/%zu bytes used",
|
|
gen_pool_avail(secure_iova_pool),
|
|
gen_pool_size(secure_iova_pool));
|
|
|
|
return out_addr;
|
|
}
|
|
EXPORT_SYMBOL_GPL(secure_iova_alloc);
|
|
|
|
void secure_iova_free(unsigned long addr, unsigned long size)
|
|
{
|
|
gen_pool_free(secure_iova_pool, addr, size);
|
|
}
|
|
EXPORT_SYMBOL_GPL(secure_iova_free);
|
|
|
|
static int __init samsung_secure_iova_init(void)
|
|
{
|
|
struct device_node *np;
|
|
phys_addr_t base = SECURE_DMA_BASE, size = SECURE_DMA_SIZE;
|
|
int ret;
|
|
|
|
secure_iova_pool = gen_pool_create(PAGE_SHIFT, -1);
|
|
if (!secure_iova_pool) {
|
|
pr_err("failed to create Secure IOVA pool\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for_each_node_by_name(np, "secure-iova-domain") {
|
|
struct dma_heap *hpa_heap;
|
|
int naddr = of_n_addr_cells(np);
|
|
int nsize = of_n_size_cells(np);
|
|
const __be32 *prop;
|
|
int len;
|
|
|
|
prop = of_get_property(np, "#address-cells", NULL);
|
|
if (prop)
|
|
naddr = be32_to_cpup(prop);
|
|
|
|
prop = of_get_property(np, "#size-cells", NULL);
|
|
if (prop)
|
|
nsize = be32_to_cpup(prop);
|
|
|
|
prop = of_get_property(np, "domain-ranges", &len);
|
|
if (prop && len > 0) {
|
|
base = (phys_addr_t)of_read_number(prop, naddr);
|
|
prop += naddr;
|
|
size = (phys_addr_t)of_read_number(prop, nsize);
|
|
} else {
|
|
pr_err("%s: failed to get domain-ranges attributes\n", __func__);
|
|
break;
|
|
}
|
|
|
|
hpa_heap = dma_heap_find("system-secure-gpu_buffer-secure");
|
|
if (hpa_heap) {
|
|
dma_heap_put(hpa_heap);
|
|
|
|
prop = of_get_property(np, "hpa,reserved", &len);
|
|
if (prop && len > 0) {
|
|
size -= (phys_addr_t)of_read_number(prop, nsize);
|
|
} else {
|
|
base = SECURE_DMA_BASE;
|
|
size = SECURE_DMA_SIZE;
|
|
pr_err("%s: failed to get hpa,reserved attributes\n", __func__);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = gen_pool_add(secure_iova_pool, base, size, -1);
|
|
if (ret) {
|
|
pr_err("failed to set address range of Secure IOVA pool\n");
|
|
gen_pool_destroy(secure_iova_pool);
|
|
return ret;
|
|
}
|
|
pr_info("Add secure iova ranges %#lx-%#lx\n", base, size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit samsung_secure_iova_exit(void)
|
|
{
|
|
gen_pool_destroy(secure_iova_pool);
|
|
}
|
|
|
|
module_init(samsung_secure_iova_init);
|
|
module_exit(samsung_secure_iova_exit);
|
|
MODULE_DESCRIPTION("Samsung Secure IOVA Manager");
|
|
MODULE_LICENSE("GPL v2");
|