// SPDX-License-Identifier: GPL-2.0 /* * Secure IOVA Management * * Copyright (c) 2021 Samsung Electronics Co., Ltd. * Author: for Samsung */ #include #include #include #include #include #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");