mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'dma-mapping-7.1-2026-04-16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszyprowski/linux
Pull dma-mapping updates from Marek Szyprowski:
- added support for batched cache sync, what improves performance of
dma_map/unmap_sg() operations on ARM64 architecture (Barry Song)
- introduced DMA_ATTR_CC_SHARED attribute for explicitly shared memory
used in confidential computing (Jiri Pirko)
- refactored spaghetti-like code in drivers/of/of_reserved_mem.c and
its clients (Marek Szyprowski, shared branch with device-tree updates
to avoid merge conflicts)
- prepared Contiguous Memory Allocator related code for making dma-buf
drivers modularized (Maxime Ripard)
- added support for benchmarking dma_map_sg() calls to tools/dma
utility (Qinxin Xia)
* tag 'dma-mapping-7.1-2026-04-16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszyprowski/linux: (24 commits)
dma-buf: heaps: system: document system_cc_shared heap
dma-buf: heaps: system: add system_cc_shared heap for explicitly shared memory
dma-mapping: introduce DMA_ATTR_CC_SHARED for shared memory
mm: cma: Export cma_alloc(), cma_release() and cma_get_name()
dma: contiguous: Export dev_get_cma_area()
dma: contiguous: Make dma_contiguous_default_area static
dma: contiguous: Make dev_get_cma_area() a proper function
dma: contiguous: Turn heap registration logic around
of: reserved_mem: rework fdt_init_reserved_mem_node()
of: reserved_mem: clarify fdt_scan_reserved_mem*() functions
of: reserved_mem: rearrange code a bit
of: reserved_mem: replace CMA quirks by generic methods
of: reserved_mem: switch to ops based OF_DECLARE()
of: reserved_mem: use -ENODEV instead of -ENOENT
of: reserved_mem: remove fdt node from the structure
dma-mapping: fix false kernel-doc comment marker
dma-mapping: Support batch mode for dma_direct_{map,unmap}_sg
dma-mapping: Separate DMA sync issuing and completion waiting
arm64: Provide dcache_inval_poc_nosync helper
arm64: Provide dcache_clean_poc_nosync helper
...
This commit is contained in:
@@ -16,6 +16,13 @@ following heaps:
|
||||
|
||||
- The ``system`` heap allocates virtually contiguous, cacheable, buffers.
|
||||
|
||||
- The ``system_cc_shared`` heap allocates virtually contiguous, cacheable,
|
||||
buffers using shared (decrypted) memory. It is only present on
|
||||
confidential computing (CoCo) VMs where memory encryption is active
|
||||
(e.g., AMD SEV, Intel TDX). The allocated pages have the encryption
|
||||
bit cleared, making them accessible for device DMA without TDISP
|
||||
support. On non-CoCo VM configurations, this heap is not registered.
|
||||
|
||||
- The ``default_cma_region`` heap allocates physically contiguous,
|
||||
cacheable, buffers. Only present if a CMA region is present. Such a
|
||||
region is usually created either through the kernel commandline
|
||||
|
||||
@@ -54,6 +54,7 @@ config ARM64
|
||||
select ARCH_HAS_STRICT_MODULE_RWX
|
||||
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
|
||||
select ARCH_HAS_SYNC_DMA_FOR_CPU
|
||||
select ARCH_HAS_BATCHED_DMA_SYNC
|
||||
select ARCH_HAS_SYSCALL_WRAPPER
|
||||
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
|
||||
select ARCH_HAS_ZONE_DMA_SET if EXPERT
|
||||
|
||||
@@ -371,14 +371,13 @@ alternative_endif
|
||||
* [start, end) with dcache line size explicitly provided.
|
||||
*
|
||||
* op: operation passed to dc instruction
|
||||
* domain: domain used in dsb instruction
|
||||
* start: starting virtual address of the region
|
||||
* end: end virtual address of the region
|
||||
* linesz: dcache line size
|
||||
* fixup: optional label to branch to on user fault
|
||||
* Corrupts: start, end, tmp
|
||||
*/
|
||||
.macro dcache_by_myline_op op, domain, start, end, linesz, tmp, fixup
|
||||
.macro dcache_by_myline_op_nosync op, start, end, linesz, tmp, fixup
|
||||
sub \tmp, \linesz, #1
|
||||
bic \start, \start, \tmp
|
||||
alternative_if ARM64_WORKAROUND_4311569
|
||||
@@ -412,14 +411,28 @@ alternative_if ARM64_WORKAROUND_4311569
|
||||
cbnz \start, .Ldcache_op\@
|
||||
.endif
|
||||
alternative_else_nop_endif
|
||||
dsb \domain
|
||||
|
||||
_cond_uaccess_extable .Ldcache_op\@, \fixup
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Macro to perform a data cache maintenance for the interval
|
||||
* [start, end)
|
||||
* [start, end) without waiting for completion
|
||||
*
|
||||
* op: operation passed to dc instruction
|
||||
* start: starting virtual address of the region
|
||||
* end: end virtual address of the region
|
||||
* fixup: optional label to branch to on user fault
|
||||
* Corrupts: start, end, tmp1, tmp2
|
||||
*/
|
||||
.macro dcache_by_line_op_nosync op, start, end, tmp1, tmp2, fixup
|
||||
dcache_line_size \tmp1, \tmp2
|
||||
dcache_by_myline_op_nosync \op, \start, \end, \tmp1, \tmp2, \fixup
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Macro to perform a data cache maintenance for the interval
|
||||
* [start, end) and wait for completion
|
||||
*
|
||||
* op: operation passed to dc instruction
|
||||
* domain: domain used in dsb instruction
|
||||
@@ -429,8 +442,8 @@ alternative_else_nop_endif
|
||||
* Corrupts: start, end, tmp1, tmp2
|
||||
*/
|
||||
.macro dcache_by_line_op op, domain, start, end, tmp1, tmp2, fixup
|
||||
dcache_line_size \tmp1, \tmp2
|
||||
dcache_by_myline_op \op, \domain, \start, \end, \tmp1, \tmp2, \fixup
|
||||
dcache_by_line_op_nosync \op, \start, \end, \tmp1, \tmp2, \fixup
|
||||
dsb \domain
|
||||
.endm
|
||||
|
||||
/*
|
||||
|
||||
@@ -87,6 +87,11 @@ int cache_line_size(void);
|
||||
|
||||
#define dma_get_cache_alignment cache_line_size
|
||||
|
||||
static inline void arch_sync_dma_flush(void)
|
||||
{
|
||||
dsb(sy);
|
||||
}
|
||||
|
||||
/* Compress a u64 MPIDR value into 32 bits. */
|
||||
static inline u64 arch_compact_of_hwid(u64 id)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,8 @@ extern void icache_inval_pou(unsigned long start, unsigned long end);
|
||||
extern void dcache_clean_inval_poc(unsigned long start, unsigned long end);
|
||||
extern void dcache_inval_poc(unsigned long start, unsigned long end);
|
||||
extern void dcache_clean_poc(unsigned long start, unsigned long end);
|
||||
extern void dcache_inval_poc_nosync(unsigned long start, unsigned long end);
|
||||
extern void dcache_clean_poc_nosync(unsigned long start, unsigned long end);
|
||||
extern void dcache_clean_pop(unsigned long start, unsigned long end);
|
||||
extern void dcache_clean_pou(unsigned long start, unsigned long end);
|
||||
extern long caches_clean_inval_user_pou(unsigned long start, unsigned long end);
|
||||
|
||||
@@ -64,7 +64,8 @@ SYM_CODE_START(arm64_relocate_new_kernel)
|
||||
mov x19, x13
|
||||
copy_page x13, x12, x1, x2, x3, x4, x5, x6, x7, x8
|
||||
add x1, x19, #PAGE_SIZE
|
||||
dcache_by_myline_op civac, sy, x19, x1, x15, x20
|
||||
dcache_by_myline_op_nosync civac, x19, x1, x15, x20
|
||||
dsb sy
|
||||
b .Lnext
|
||||
.Ltest_indirection:
|
||||
tbz x16, IND_INDIRECTION_BIT, .Ltest_destination
|
||||
|
||||
+46
-11
@@ -132,17 +132,7 @@ alternative_else_nop_endif
|
||||
ret
|
||||
SYM_FUNC_END(dcache_clean_pou)
|
||||
|
||||
/*
|
||||
* dcache_inval_poc(start, end)
|
||||
*
|
||||
* Ensure that any D-cache lines for the interval [start, end)
|
||||
* are invalidated. Any partial lines at the ends of the interval are
|
||||
* also cleaned to PoC to prevent data loss.
|
||||
*
|
||||
* - start - kernel start address of region
|
||||
* - end - kernel end address of region
|
||||
*/
|
||||
SYM_FUNC_START(__pi_dcache_inval_poc)
|
||||
.macro __dcache_inval_poc_nosync
|
||||
dcache_line_size x2, x3
|
||||
sub x3, x2, #1
|
||||
tst x1, x3 // end cache line aligned?
|
||||
@@ -158,11 +148,41 @@ SYM_FUNC_START(__pi_dcache_inval_poc)
|
||||
3: add x0, x0, x2
|
||||
cmp x0, x1
|
||||
b.lo 2b
|
||||
.endm
|
||||
|
||||
/*
|
||||
* dcache_inval_poc(start, end)
|
||||
*
|
||||
* Ensure that any D-cache lines for the interval [start, end)
|
||||
* are invalidated. Any partial lines at the ends of the interval are
|
||||
* also cleaned to PoC to prevent data loss.
|
||||
*
|
||||
* - start - kernel start address of region
|
||||
* - end - kernel end address of region
|
||||
*/
|
||||
SYM_FUNC_START(__pi_dcache_inval_poc)
|
||||
__dcache_inval_poc_nosync
|
||||
dsb sy
|
||||
ret
|
||||
SYM_FUNC_END(__pi_dcache_inval_poc)
|
||||
SYM_FUNC_ALIAS(dcache_inval_poc, __pi_dcache_inval_poc)
|
||||
|
||||
/*
|
||||
* dcache_inval_poc_nosync(start, end)
|
||||
*
|
||||
* Issue the instructions of D-cache lines for the interval [start, end)
|
||||
* for invalidation. Not necessarily cleaned to PoC till an explicit dsb
|
||||
* sy is issued later
|
||||
*
|
||||
* - start - kernel start address of region
|
||||
* - end - kernel end address of region
|
||||
*/
|
||||
SYM_FUNC_START(__pi_dcache_inval_poc_nosync)
|
||||
__dcache_inval_poc_nosync
|
||||
ret
|
||||
SYM_FUNC_END(__pi_dcache_inval_poc_nosync)
|
||||
SYM_FUNC_ALIAS(dcache_inval_poc_nosync, __pi_dcache_inval_poc_nosync)
|
||||
|
||||
/*
|
||||
* dcache_clean_poc(start, end)
|
||||
*
|
||||
@@ -178,6 +198,21 @@ SYM_FUNC_START(__pi_dcache_clean_poc)
|
||||
SYM_FUNC_END(__pi_dcache_clean_poc)
|
||||
SYM_FUNC_ALIAS(dcache_clean_poc, __pi_dcache_clean_poc)
|
||||
|
||||
/*
|
||||
* dcache_clean_poc_nosync(start, end)
|
||||
*
|
||||
* Issue the instructions of D-cache lines for the interval [start, end).
|
||||
* not necessarily cleaned to the PoC till an explicit dsb sy afterward.
|
||||
*
|
||||
* - start - virtual start address of region
|
||||
* - end - virtual end address of region
|
||||
*/
|
||||
SYM_FUNC_START(__pi_dcache_clean_poc_nosync)
|
||||
dcache_by_line_op_nosync cvac, x0, x1, x2, x3
|
||||
ret
|
||||
SYM_FUNC_END(__pi_dcache_clean_poc_nosync)
|
||||
SYM_FUNC_ALIAS(dcache_clean_poc_nosync, __pi_dcache_clean_poc_nosync)
|
||||
|
||||
/*
|
||||
* dcache_clean_pop(start, end)
|
||||
*
|
||||
|
||||
@@ -17,7 +17,7 @@ void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
|
||||
{
|
||||
unsigned long start = (unsigned long)phys_to_virt(paddr);
|
||||
|
||||
dcache_clean_poc(start, start + size);
|
||||
dcache_clean_poc_nosync(start, start + size);
|
||||
}
|
||||
|
||||
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
|
||||
@@ -28,7 +28,7 @@ void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
|
||||
if (dir == DMA_TO_DEVICE)
|
||||
return;
|
||||
|
||||
dcache_inval_poc(start, start + size);
|
||||
dcache_inval_poc_nosync(start, start + size);
|
||||
}
|
||||
|
||||
void arch_dma_prep_coherent(struct page *page, size_t size)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <linux/cma.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-buf/heaps/cma.h>
|
||||
#include <linux/dma-heap.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/err.h>
|
||||
@@ -30,19 +29,6 @@
|
||||
|
||||
#define DEFAULT_CMA_NAME "default_cma_region"
|
||||
|
||||
static struct cma *dma_areas[MAX_CMA_AREAS] __initdata;
|
||||
static unsigned int dma_areas_num __initdata;
|
||||
|
||||
int __init dma_heap_cma_register_heap(struct cma *cma)
|
||||
{
|
||||
if (dma_areas_num >= ARRAY_SIZE(dma_areas))
|
||||
return -EINVAL;
|
||||
|
||||
dma_areas[dma_areas_num++] = cma;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cma_heap {
|
||||
struct dma_heap *heap;
|
||||
struct cma *cma;
|
||||
@@ -411,6 +397,7 @@ static int __init __add_cma_heap(struct cma *cma, const char *name)
|
||||
static int __init add_cma_heaps(void)
|
||||
{
|
||||
struct cma *default_cma = dev_get_cma_area(NULL);
|
||||
struct cma *cma;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
@@ -420,9 +407,7 @@ static int __init add_cma_heaps(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < dma_areas_num; i++) {
|
||||
struct cma *cma = dma_areas[i];
|
||||
|
||||
for (i = 0; (cma = dma_contiguous_get_area_by_idx(i)) != NULL; i++) {
|
||||
ret = __add_cma_heap(cma, cma_get_name(cma));
|
||||
if (ret) {
|
||||
pr_warn("Failed to add CMA heap %s", cma_get_name(cma));
|
||||
|
||||
@@ -10,17 +10,25 @@
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/cc_platform.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-heap.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/mem_encrypt.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
struct system_heap_priv {
|
||||
bool cc_shared;
|
||||
};
|
||||
|
||||
struct system_heap_buffer {
|
||||
struct dma_heap *heap;
|
||||
struct list_head attachments;
|
||||
@@ -29,6 +37,7 @@ struct system_heap_buffer {
|
||||
struct sg_table sg_table;
|
||||
int vmap_cnt;
|
||||
void *vaddr;
|
||||
bool cc_shared;
|
||||
};
|
||||
|
||||
struct dma_heap_attachment {
|
||||
@@ -36,6 +45,7 @@ struct dma_heap_attachment {
|
||||
struct sg_table table;
|
||||
struct list_head list;
|
||||
bool mapped;
|
||||
bool cc_shared;
|
||||
};
|
||||
|
||||
#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO)
|
||||
@@ -52,6 +62,34 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};
|
||||
static const unsigned int orders[] = {8, 4, 0};
|
||||
#define NUM_ORDERS ARRAY_SIZE(orders)
|
||||
|
||||
static int system_heap_set_page_decrypted(struct page *page)
|
||||
{
|
||||
unsigned long addr = (unsigned long)page_address(page);
|
||||
unsigned int nr_pages = 1 << compound_order(page);
|
||||
int ret;
|
||||
|
||||
ret = set_memory_decrypted(addr, nr_pages);
|
||||
if (ret)
|
||||
pr_warn_ratelimited("dma-buf system heap: failed to decrypt page at %p\n",
|
||||
page_address(page));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int system_heap_set_page_encrypted(struct page *page)
|
||||
{
|
||||
unsigned long addr = (unsigned long)page_address(page);
|
||||
unsigned int nr_pages = 1 << compound_order(page);
|
||||
int ret;
|
||||
|
||||
ret = set_memory_encrypted(addr, nr_pages);
|
||||
if (ret)
|
||||
pr_warn_ratelimited("dma-buf system heap: failed to re-encrypt page at %p, leaking memory\n",
|
||||
page_address(page));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dup_sg_table(struct sg_table *from, struct sg_table *to)
|
||||
{
|
||||
struct scatterlist *sg, *new_sg;
|
||||
@@ -90,6 +128,7 @@ static int system_heap_attach(struct dma_buf *dmabuf,
|
||||
a->dev = attachment->dev;
|
||||
INIT_LIST_HEAD(&a->list);
|
||||
a->mapped = false;
|
||||
a->cc_shared = buffer->cc_shared;
|
||||
|
||||
attachment->priv = a;
|
||||
|
||||
@@ -119,9 +158,11 @@ static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attac
|
||||
{
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
struct sg_table *table = &a->table;
|
||||
unsigned long attrs;
|
||||
int ret;
|
||||
|
||||
ret = dma_map_sgtable(attachment->dev, table, direction, 0);
|
||||
attrs = a->cc_shared ? DMA_ATTR_CC_SHARED : 0;
|
||||
ret = dma_map_sgtable(attachment->dev, table, direction, attrs);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@@ -188,8 +229,13 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
unsigned long addr = vma->vm_start;
|
||||
unsigned long pgoff = vma->vm_pgoff;
|
||||
struct scatterlist *sg;
|
||||
pgprot_t prot;
|
||||
int i, ret;
|
||||
|
||||
prot = vma->vm_page_prot;
|
||||
if (buffer->cc_shared)
|
||||
prot = pgprot_decrypted(prot);
|
||||
|
||||
for_each_sgtable_sg(table, sg, i) {
|
||||
unsigned long n = sg->length >> PAGE_SHIFT;
|
||||
|
||||
@@ -206,8 +252,7 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
if (addr + size > vma->vm_end)
|
||||
size = vma->vm_end - addr;
|
||||
|
||||
ret = remap_pfn_range(vma, addr, page_to_pfn(page),
|
||||
size, vma->vm_page_prot);
|
||||
ret = remap_pfn_range(vma, addr, page_to_pfn(page), size, prot);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -225,6 +270,7 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
|
||||
struct page **pages = vmalloc(sizeof(struct page *) * npages);
|
||||
struct page **tmp = pages;
|
||||
struct sg_page_iter piter;
|
||||
pgprot_t prot;
|
||||
void *vaddr;
|
||||
|
||||
if (!pages)
|
||||
@@ -235,7 +281,10 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
|
||||
*tmp++ = sg_page_iter_page(&piter);
|
||||
}
|
||||
|
||||
vaddr = vmap(pages, npages, VM_MAP, PAGE_KERNEL);
|
||||
prot = PAGE_KERNEL;
|
||||
if (buffer->cc_shared)
|
||||
prot = pgprot_decrypted(prot);
|
||||
vaddr = vmap(pages, npages, VM_MAP, prot);
|
||||
vfree(pages);
|
||||
|
||||
if (!vaddr)
|
||||
@@ -296,6 +345,14 @@ static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
|
||||
for_each_sgtable_sg(table, sg, i) {
|
||||
struct page *page = sg_page(sg);
|
||||
|
||||
/*
|
||||
* Intentionally leak pages that cannot be re-encrypted
|
||||
* to prevent shared memory from being reused.
|
||||
*/
|
||||
if (buffer->cc_shared &&
|
||||
system_heap_set_page_encrypted(page))
|
||||
continue;
|
||||
|
||||
__free_pages(page, compound_order(page));
|
||||
}
|
||||
sg_free_table(table);
|
||||
@@ -347,6 +404,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
unsigned long size_remaining = len;
|
||||
unsigned int max_order = orders[0];
|
||||
struct system_heap_priv *priv = dma_heap_get_drvdata(heap);
|
||||
bool cc_shared = priv->cc_shared;
|
||||
struct dma_buf *dmabuf;
|
||||
struct sg_table *table;
|
||||
struct scatterlist *sg;
|
||||
@@ -362,6 +421,7 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
|
||||
mutex_init(&buffer->lock);
|
||||
buffer->heap = heap;
|
||||
buffer->len = len;
|
||||
buffer->cc_shared = cc_shared;
|
||||
|
||||
INIT_LIST_HEAD(&pages);
|
||||
i = 0;
|
||||
@@ -396,6 +456,14 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
|
||||
list_del(&page->lru);
|
||||
}
|
||||
|
||||
if (cc_shared) {
|
||||
for_each_sgtable_sg(table, sg, i) {
|
||||
ret = system_heap_set_page_decrypted(sg_page(sg));
|
||||
if (ret)
|
||||
goto free_pages;
|
||||
}
|
||||
}
|
||||
|
||||
/* create the dmabuf */
|
||||
exp_info.exp_name = dma_heap_get_name(heap);
|
||||
exp_info.ops = &system_heap_buf_ops;
|
||||
@@ -413,6 +481,13 @@ free_pages:
|
||||
for_each_sgtable_sg(table, sg, i) {
|
||||
struct page *p = sg_page(sg);
|
||||
|
||||
/*
|
||||
* Intentionally leak pages that cannot be re-encrypted
|
||||
* to prevent shared memory from being reused.
|
||||
*/
|
||||
if (buffer->cc_shared &&
|
||||
system_heap_set_page_encrypted(p))
|
||||
continue;
|
||||
__free_pages(p, compound_order(p));
|
||||
}
|
||||
sg_free_table(table);
|
||||
@@ -428,6 +503,14 @@ static const struct dma_heap_ops system_heap_ops = {
|
||||
.allocate = system_heap_allocate,
|
||||
};
|
||||
|
||||
static struct system_heap_priv system_heap_priv = {
|
||||
.cc_shared = false,
|
||||
};
|
||||
|
||||
static struct system_heap_priv system_heap_cc_shared_priv = {
|
||||
.cc_shared = true,
|
||||
};
|
||||
|
||||
static int __init system_heap_create(void)
|
||||
{
|
||||
struct dma_heap_export_info exp_info;
|
||||
@@ -435,8 +518,18 @@ static int __init system_heap_create(void)
|
||||
|
||||
exp_info.name = "system";
|
||||
exp_info.ops = &system_heap_ops;
|
||||
exp_info.priv = NULL;
|
||||
exp_info.priv = &system_heap_priv;
|
||||
|
||||
sys_heap = dma_heap_add(&exp_info);
|
||||
if (IS_ERR(sys_heap))
|
||||
return PTR_ERR(sys_heap);
|
||||
|
||||
if (IS_ENABLED(CONFIG_HIGHMEM) ||
|
||||
!cc_platform_has(CC_ATTR_MEM_ENCRYPT))
|
||||
return 0;
|
||||
|
||||
exp_info.name = "system_cc_shared";
|
||||
exp_info.priv = &system_heap_cc_shared_priv;
|
||||
sys_heap = dma_heap_add(&exp_info);
|
||||
if (IS_ERR(sys_heap))
|
||||
return PTR_ERR(sys_heap);
|
||||
|
||||
@@ -1106,8 +1106,10 @@ void iommu_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
|
||||
return;
|
||||
|
||||
phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle);
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
if (!dev_is_dma_coherent(dev)) {
|
||||
arch_sync_dma_for_cpu(phys, size, dir);
|
||||
arch_sync_dma_flush();
|
||||
}
|
||||
|
||||
swiotlb_sync_single_for_cpu(dev, phys, size, dir);
|
||||
}
|
||||
@@ -1123,8 +1125,10 @@ void iommu_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
|
||||
phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle);
|
||||
swiotlb_sync_single_for_device(dev, phys, size, dir);
|
||||
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
if (!dev_is_dma_coherent(dev)) {
|
||||
arch_sync_dma_for_device(phys, size, dir);
|
||||
arch_sync_dma_flush();
|
||||
}
|
||||
}
|
||||
|
||||
void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
|
||||
@@ -1133,13 +1137,15 @@ void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
if (sg_dma_is_swiotlb(sgl))
|
||||
if (sg_dma_is_swiotlb(sgl)) {
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
iommu_dma_sync_single_for_cpu(dev, sg_dma_address(sg),
|
||||
sg->length, dir);
|
||||
else if (!dev_is_dma_coherent(dev))
|
||||
} else if (!dev_is_dma_coherent(dev)) {
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir);
|
||||
arch_sync_dma_flush();
|
||||
}
|
||||
}
|
||||
|
||||
void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
|
||||
@@ -1148,14 +1154,16 @@ void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
if (sg_dma_is_swiotlb(sgl))
|
||||
if (sg_dma_is_swiotlb(sgl)) {
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
iommu_dma_sync_single_for_device(dev,
|
||||
sg_dma_address(sg),
|
||||
sg->length, dir);
|
||||
else if (!dev_is_dma_coherent(dev))
|
||||
} else if (!dev_is_dma_coherent(dev)) {
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
arch_sync_dma_for_device(sg_phys(sg), sg->length, dir);
|
||||
arch_sync_dma_flush();
|
||||
}
|
||||
}
|
||||
|
||||
static phys_addr_t iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys,
|
||||
@@ -1230,8 +1238,10 @@ dma_addr_t iommu_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size,
|
||||
return DMA_MAPPING_ERROR;
|
||||
}
|
||||
|
||||
if (!coherent && !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO)))
|
||||
if (!coherent && !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) {
|
||||
arch_sync_dma_for_device(phys, size, dir);
|
||||
arch_sync_dma_flush();
|
||||
}
|
||||
|
||||
iova = __iommu_dma_map(dev, phys, size, prot, dma_mask);
|
||||
if (iova == DMA_MAPPING_ERROR &&
|
||||
@@ -1254,8 +1264,10 @@ void iommu_dma_unmap_phys(struct device *dev, dma_addr_t dma_handle,
|
||||
if (WARN_ON(!phys))
|
||||
return;
|
||||
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !dev_is_dma_coherent(dev))
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !dev_is_dma_coherent(dev)) {
|
||||
arch_sync_dma_for_cpu(phys, size, dir);
|
||||
arch_sync_dma_flush();
|
||||
}
|
||||
|
||||
__iommu_dma_unmap(dev, dma_handle, size);
|
||||
|
||||
@@ -2004,6 +2016,8 @@ int dma_iova_sync(struct device *dev, struct dma_iova_state *state,
|
||||
dma_addr_t addr = state->addr + offset;
|
||||
size_t iova_start_pad = iova_offset(iovad, addr);
|
||||
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
arch_sync_dma_flush();
|
||||
return iommu_sync_map(domain, addr - iova_start_pad,
|
||||
iova_align(iovad, size + iova_start_pad));
|
||||
}
|
||||
@@ -2017,6 +2031,8 @@ static void iommu_dma_iova_unlink_range_slow(struct device *dev,
|
||||
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
||||
struct iova_domain *iovad = &cookie->iovad;
|
||||
size_t iova_start_pad = iova_offset(iovad, addr);
|
||||
bool need_sync_dma = !dev_is_dma_coherent(dev) &&
|
||||
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO));
|
||||
dma_addr_t end = addr + size;
|
||||
|
||||
do {
|
||||
@@ -2040,6 +2056,9 @@ static void iommu_dma_iova_unlink_range_slow(struct device *dev,
|
||||
addr += len;
|
||||
iova_start_pad = 0;
|
||||
} while (addr < end);
|
||||
|
||||
if (need_sync_dma)
|
||||
arch_sync_dma_flush();
|
||||
}
|
||||
|
||||
static void __iommu_dma_iova_unlink(struct device *dev,
|
||||
|
||||
@@ -70,19 +70,20 @@ static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
|
||||
memunmap(timings);
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops tegra210_emc_table_ops = {
|
||||
.device_init = tegra210_emc_table_device_init,
|
||||
.device_release = tegra210_emc_table_device_release,
|
||||
};
|
||||
|
||||
static int tegra210_emc_table_init(struct reserved_mem *rmem)
|
||||
static int tegra210_emc_table_init(unsigned long node,
|
||||
struct reserved_mem *rmem)
|
||||
{
|
||||
pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base,
|
||||
(unsigned long)rmem->size);
|
||||
|
||||
rmem->ops = &tegra210_emc_table_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops tegra210_emc_table_ops = {
|
||||
.node_init = tegra210_emc_table_init,
|
||||
.device_init = tegra210_emc_table_device_init,
|
||||
.device_release = tegra210_emc_table_device_release,
|
||||
};
|
||||
|
||||
RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table",
|
||||
tegra210_emc_table_init);
|
||||
&tegra210_emc_table_ops);
|
||||
|
||||
+1
-1
@@ -1295,7 +1295,7 @@ void __init unflatten_device_tree(void)
|
||||
void *fdt = initial_boot_params;
|
||||
|
||||
/* Save the statically-placed regions in the reserved_mem array */
|
||||
fdt_scan_reserved_mem_reg_nodes();
|
||||
fdt_scan_reserved_mem_late();
|
||||
|
||||
/* Populate an empty root node when bootloader doesn't provide one */
|
||||
if (!fdt) {
|
||||
|
||||
@@ -186,7 +186,7 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
|
||||
#endif
|
||||
|
||||
int fdt_scan_reserved_mem(void);
|
||||
void __init fdt_scan_reserved_mem_reg_nodes(void);
|
||||
void __init fdt_scan_reserved_mem_late(void);
|
||||
|
||||
bool of_fdt_device_is_available(const void *blob, unsigned long node);
|
||||
|
||||
|
||||
+197
-143
@@ -24,8 +24,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
#include "of_private.h"
|
||||
|
||||
@@ -104,30 +102,12 @@ static void __init alloc_reserved_mem_array(void)
|
||||
reserved_mem = new_array;
|
||||
}
|
||||
|
||||
static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem);
|
||||
/*
|
||||
* fdt_reserved_mem_save_node() - save fdt node for second pass initialization
|
||||
*/
|
||||
static void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
|
||||
phys_addr_t base, phys_addr_t size)
|
||||
{
|
||||
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
|
||||
|
||||
if (reserved_mem_count == total_reserved_mem_cnt) {
|
||||
pr_err("not enough space for all defined regions.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rmem->fdt_node = node;
|
||||
rmem->name = uname;
|
||||
rmem->base = base;
|
||||
rmem->size = size;
|
||||
|
||||
/* Call the region specific initialization function */
|
||||
fdt_init_reserved_mem_node(rmem);
|
||||
|
||||
reserved_mem_count++;
|
||||
}
|
||||
static void fdt_init_reserved_mem_node(unsigned long node, const char *uname,
|
||||
phys_addr_t base, phys_addr_t size);
|
||||
static int fdt_validate_reserved_mem_node(unsigned long node,
|
||||
phys_addr_t *align);
|
||||
static int fdt_fixup_reserved_mem_node(unsigned long node,
|
||||
phys_addr_t base, phys_addr_t size);
|
||||
|
||||
static int __init early_init_dt_reserve_memory(phys_addr_t base,
|
||||
phys_addr_t size, bool nomap)
|
||||
@@ -154,21 +134,19 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
|
||||
const char *uname)
|
||||
{
|
||||
phys_addr_t base, size;
|
||||
int i, len;
|
||||
int i, len, err;
|
||||
const __be32 *prop;
|
||||
bool nomap, default_cma;
|
||||
bool nomap;
|
||||
|
||||
prop = of_flat_dt_get_addr_size_prop(node, "reg", &len);
|
||||
if (!prop)
|
||||
return -ENOENT;
|
||||
|
||||
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
|
||||
default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
|
||||
|
||||
if (default_cma && cma_skip_dt_default_reserved_mem()) {
|
||||
pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = fdt_validate_reserved_mem_node(node, NULL);
|
||||
if (err && err != -ENODEV)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
u64 b, s;
|
||||
@@ -179,10 +157,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
|
||||
size = s;
|
||||
|
||||
if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) {
|
||||
/* Architecture specific contiguous memory fixup. */
|
||||
if (of_flat_dt_is_compatible(node, "shared-dma-pool") &&
|
||||
of_get_flat_dt_prop(node, "reusable", NULL))
|
||||
dma_contiguous_early_fixup(base, size);
|
||||
fdt_fixup_reserved_mem_node(node, base, size);
|
||||
pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
|
||||
uname, &base, (unsigned long)(size / SZ_1M));
|
||||
} else {
|
||||
@@ -216,19 +191,66 @@ static int __init __reserved_mem_check_root(unsigned long node)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init __rmem_check_for_overlap(void);
|
||||
static int __init __rmem_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct reserved_mem *ra = a, *rb = b;
|
||||
|
||||
if (ra->base < rb->base)
|
||||
return -1;
|
||||
|
||||
if (ra->base > rb->base)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Put the dynamic allocations (address == 0, size == 0) before static
|
||||
* allocations at address 0x0 so that overlap detection works
|
||||
* correctly.
|
||||
*/
|
||||
if (ra->size < rb->size)
|
||||
return -1;
|
||||
if (ra->size > rb->size)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init __rmem_check_for_overlap(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (reserved_mem_count < 2)
|
||||
return;
|
||||
|
||||
sort(reserved_mem, reserved_mem_count, sizeof(reserved_mem[0]),
|
||||
__rmem_cmp, NULL);
|
||||
for (i = 0; i < reserved_mem_count - 1; i++) {
|
||||
struct reserved_mem *this, *next;
|
||||
|
||||
this = &reserved_mem[i];
|
||||
next = &reserved_mem[i + 1];
|
||||
|
||||
if (this->base + this->size > next->base) {
|
||||
phys_addr_t this_end, next_end;
|
||||
|
||||
this_end = this->base + this->size;
|
||||
next_end = next->base + next->size;
|
||||
pr_err("OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)\n",
|
||||
this->name, &this->base, &this_end,
|
||||
next->name, &next->base, &next_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined
|
||||
* reserved memory regions.
|
||||
* fdt_scan_reserved_mem_late() - Scan FDT and initialize remaining reserved
|
||||
* memory regions.
|
||||
*
|
||||
* This function is used to scan through the DT and store the
|
||||
* information for the reserved memory regions that are defined using
|
||||
* the "reg" property. The region node number, name, base address, and
|
||||
* size are all stored in the reserved_mem array by calling the
|
||||
* fdt_reserved_mem_save_node() function.
|
||||
* This function is used to scan again through the DT and initialize the
|
||||
* "static" reserved memory regions, that are defined using the "reg"
|
||||
* property. Each such region is then initialized with its specific init
|
||||
* function and stored in the global reserved_mem array.
|
||||
*/
|
||||
void __init fdt_scan_reserved_mem_reg_nodes(void)
|
||||
void __init fdt_scan_reserved_mem_late(void)
|
||||
{
|
||||
const void *fdt = initial_boot_params;
|
||||
phys_addr_t base, size;
|
||||
@@ -253,23 +275,25 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
|
||||
|
||||
fdt_for_each_subnode(child, fdt, node) {
|
||||
const char *uname;
|
||||
bool default_cma = of_get_flat_dt_prop(child, "linux,cma-default", NULL);
|
||||
u64 b, s;
|
||||
int ret;
|
||||
|
||||
if (!of_fdt_device_is_available(fdt, child))
|
||||
continue;
|
||||
if (default_cma && cma_skip_dt_default_reserved_mem())
|
||||
continue;
|
||||
|
||||
if (!of_flat_dt_get_addr_size(child, "reg", &b, &s))
|
||||
continue;
|
||||
|
||||
ret = fdt_validate_reserved_mem_node(child, NULL);
|
||||
if (ret && ret != -ENODEV)
|
||||
continue;
|
||||
|
||||
base = b;
|
||||
size = s;
|
||||
|
||||
if (size) {
|
||||
uname = fdt_get_name(fdt, child, NULL);
|
||||
fdt_reserved_mem_save_node(child, uname, base, size);
|
||||
fdt_init_reserved_mem_node(child, uname, base, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +304,14 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
|
||||
static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname);
|
||||
|
||||
/*
|
||||
* fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
|
||||
* fdt_scan_reserved_mem() - reserve and allocate memory occupied by
|
||||
* reserved memory regions.
|
||||
*
|
||||
* This function is used to scan through the FDT and mark memory occupied
|
||||
* by all static (defined by the "reg" property) reserved memory regions.
|
||||
* Then memory for all dynamic regions (defined by size & alignment) is
|
||||
* allocated, a region specific init function is called and region information
|
||||
* is stored in the reserved_mem array.
|
||||
*/
|
||||
int __init fdt_scan_reserved_mem(void)
|
||||
{
|
||||
@@ -397,7 +428,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
|
||||
phys_addr_t base = 0, align = 0, size;
|
||||
int i, len;
|
||||
const __be32 *prop;
|
||||
bool nomap, default_cma;
|
||||
bool nomap;
|
||||
int ret;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "size", &len);
|
||||
@@ -421,19 +452,10 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
|
||||
}
|
||||
|
||||
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
|
||||
default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
|
||||
|
||||
if (default_cma && cma_skip_dt_default_reserved_mem()) {
|
||||
pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Need adjust the alignment to satisfy the CMA requirement */
|
||||
if (IS_ENABLED(CONFIG_CMA)
|
||||
&& of_flat_dt_is_compatible(node, "shared-dma-pool")
|
||||
&& of_get_flat_dt_prop(node, "reusable", NULL)
|
||||
&& !nomap)
|
||||
align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES);
|
||||
ret = fdt_validate_reserved_mem_node(node, &align);
|
||||
if (ret && ret != -ENODEV)
|
||||
return ret;
|
||||
|
||||
prop = of_flat_dt_get_addr_size_prop(node, "alloc-ranges", &len);
|
||||
if (prop) {
|
||||
@@ -468,121 +490,151 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
|
||||
uname, (unsigned long)(size / SZ_1M));
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Architecture specific contiguous memory fixup. */
|
||||
if (of_flat_dt_is_compatible(node, "shared-dma-pool") &&
|
||||
of_get_flat_dt_prop(node, "reusable", NULL))
|
||||
dma_contiguous_early_fixup(base, size);
|
||||
/* Save region in the reserved_mem array */
|
||||
fdt_reserved_mem_save_node(node, uname, base, size);
|
||||
|
||||
fdt_fixup_reserved_mem_node(node, base, size);
|
||||
fdt_init_reserved_mem_node(node, uname, base, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern const struct of_device_id __reservedmem_of_table[];
|
||||
static const struct of_device_id __rmem_of_table_sentinel
|
||||
__used __section("__reservedmem_of_table_end");
|
||||
|
||||
/*
|
||||
* __reserved_mem_init_node() - call region specific reserved memory init code
|
||||
/**
|
||||
* fdt_fixup_reserved_mem_node() - call fixup function for a reserved memory node
|
||||
* @node: FDT node to fixup
|
||||
* @base: base address of the reserved memory region
|
||||
* @size: size of the reserved memory region
|
||||
*
|
||||
* This function iterates through the reserved memory drivers and calls
|
||||
* the node_fixup callback for the compatible entry matching the node.
|
||||
*
|
||||
* Return: 0 on success, -ENODEV if no compatible match found
|
||||
*/
|
||||
static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
|
||||
static int __init fdt_fixup_reserved_mem_node(unsigned long node,
|
||||
phys_addr_t base, phys_addr_t size)
|
||||
{
|
||||
extern const struct of_device_id __reservedmem_of_table[];
|
||||
const struct of_device_id *i;
|
||||
int ret = -ENOENT;
|
||||
int ret = -ENODEV;
|
||||
|
||||
for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {
|
||||
reservedmem_of_init_fn initfn = i->data;
|
||||
const char *compat = i->compatible;
|
||||
for (i = __reservedmem_of_table; ret == -ENODEV &&
|
||||
i < &__rmem_of_table_sentinel; i++) {
|
||||
const struct reserved_mem_ops *ops = i->data;
|
||||
|
||||
if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
|
||||
if (!of_flat_dt_is_compatible(node, i->compatible))
|
||||
continue;
|
||||
|
||||
ret = initfn(rmem);
|
||||
if (ops->node_fixup)
|
||||
ret = ops->node_fixup(node, base, size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* fdt_validate_reserved_mem_node() - validate a reserved memory node
|
||||
* @node: FDT node to validate
|
||||
* @align: pointer to store the validated alignment (may be modified by callback)
|
||||
*
|
||||
* This function iterates through the reserved memory drivers and calls
|
||||
* the node_validate callback for the compatible entry matching the node.
|
||||
*
|
||||
* Return: 0 on success, -ENODEV if no compatible match found
|
||||
*/
|
||||
static int __init fdt_validate_reserved_mem_node(unsigned long node, phys_addr_t *align)
|
||||
{
|
||||
const struct of_device_id *i;
|
||||
int ret = -ENODEV;
|
||||
|
||||
for (i = __reservedmem_of_table; ret == -ENODEV &&
|
||||
i < &__rmem_of_table_sentinel; i++) {
|
||||
const struct reserved_mem_ops *ops = i->data;
|
||||
|
||||
if (!of_flat_dt_is_compatible(node, i->compatible))
|
||||
continue;
|
||||
|
||||
if (ops->node_validate)
|
||||
ret = ops->node_validate(node, align);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* __reserved_mem_init_node() - initialize a reserved memory region
|
||||
* @rmem: reserved_mem structure to initialize
|
||||
* @node: FDT node describing the reserved memory region
|
||||
*
|
||||
* This function iterates through the reserved memory drivers and calls the
|
||||
* node_init callback for the compatible entry matching the node. On success,
|
||||
* the operations pointer is stored in the reserved_mem structure.
|
||||
*
|
||||
* Return: 0 on success, -ENODEV if no compatible match found
|
||||
*/
|
||||
static int __init __reserved_mem_init_node(struct reserved_mem *rmem,
|
||||
unsigned long node)
|
||||
{
|
||||
const struct of_device_id *i;
|
||||
int ret = -ENODEV;
|
||||
|
||||
for (i = __reservedmem_of_table; ret == -ENODEV &&
|
||||
i < &__rmem_of_table_sentinel; i++) {
|
||||
const struct reserved_mem_ops *ops = i->data;
|
||||
const char *compat = i->compatible;
|
||||
|
||||
if (!of_flat_dt_is_compatible(node, compat))
|
||||
continue;
|
||||
|
||||
ret = ops->node_init(node, rmem);
|
||||
if (ret == 0) {
|
||||
rmem->ops = ops;
|
||||
pr_info("initialized node %s, compatible id %s\n",
|
||||
rmem->name, compat);
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init __rmem_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct reserved_mem *ra = a, *rb = b;
|
||||
|
||||
if (ra->base < rb->base)
|
||||
return -1;
|
||||
|
||||
if (ra->base > rb->base)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Put the dynamic allocations (address == 0, size == 0) before static
|
||||
* allocations at address 0x0 so that overlap detection works
|
||||
* correctly.
|
||||
*/
|
||||
if (ra->size < rb->size)
|
||||
return -1;
|
||||
if (ra->size > rb->size)
|
||||
return 1;
|
||||
|
||||
if (ra->fdt_node < rb->fdt_node)
|
||||
return -1;
|
||||
if (ra->fdt_node > rb->fdt_node)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init __rmem_check_for_overlap(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (reserved_mem_count < 2)
|
||||
return;
|
||||
|
||||
sort(reserved_mem, reserved_mem_count, sizeof(reserved_mem[0]),
|
||||
__rmem_cmp, NULL);
|
||||
for (i = 0; i < reserved_mem_count - 1; i++) {
|
||||
struct reserved_mem *this, *next;
|
||||
|
||||
this = &reserved_mem[i];
|
||||
next = &reserved_mem[i + 1];
|
||||
|
||||
if (this->base + this->size > next->base) {
|
||||
phys_addr_t this_end, next_end;
|
||||
|
||||
this_end = this->base + this->size;
|
||||
next_end = next->base + next->size;
|
||||
pr_err("OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)\n",
|
||||
this->name, &this->base, &this_end,
|
||||
next->name, &next->base, &next_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fdt_init_reserved_mem_node() - Initialize a reserved memory region
|
||||
* @rmem: reserved_mem struct of the memory region to be initialized.
|
||||
* @node: fdt node of the initialized region
|
||||
* @uname: name of the reserved memory node
|
||||
* @base: base address of the reserved memory region
|
||||
* @size: size of the reserved memory region
|
||||
*
|
||||
* This function is used to call the region specific initialization
|
||||
* function for a reserved memory region.
|
||||
* This function calls the region-specific initialization function for a
|
||||
* reserved memory region and saves all region-specific data to the
|
||||
* reserved_mem array to allow of_reserved_mem_lookup() to find it.
|
||||
*/
|
||||
static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem)
|
||||
static void __init fdt_init_reserved_mem_node(unsigned long node, const char *uname,
|
||||
phys_addr_t base, phys_addr_t size)
|
||||
{
|
||||
unsigned long node = rmem->fdt_node;
|
||||
int err = 0;
|
||||
bool nomap;
|
||||
|
||||
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
|
||||
|
||||
if (reserved_mem_count == total_reserved_mem_cnt) {
|
||||
pr_err("not enough space for all defined regions.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rmem->name = uname;
|
||||
rmem->base = base;
|
||||
rmem->size = size;
|
||||
|
||||
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
|
||||
|
||||
err = __reserved_mem_init_node(rmem);
|
||||
if (err != 0 && err != -ENOENT) {
|
||||
err = __reserved_mem_init_node(rmem, node);
|
||||
if (err != 0 && err != -ENODEV) {
|
||||
pr_info("node %s compatible matching fail\n", rmem->name);
|
||||
rmem->name = NULL;
|
||||
|
||||
if (nomap)
|
||||
memblock_clear_nomap(rmem->base, rmem->size);
|
||||
else
|
||||
memblock_phys_free(rmem->base, rmem->size);
|
||||
return;
|
||||
} else {
|
||||
phys_addr_t end = rmem->base + rmem->size - 1;
|
||||
bool reusable =
|
||||
@@ -594,6 +646,8 @@ static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem)
|
||||
reusable ? "reusable" : "non-reusable",
|
||||
rmem->name ? rmem->name : "unknown");
|
||||
}
|
||||
|
||||
reserved_mem_count++;
|
||||
}
|
||||
|
||||
struct rmem_assigned_device {
|
||||
|
||||
@@ -262,10 +262,12 @@ static dma_addr_t xen_swiotlb_map_phys(struct device *dev, phys_addr_t phys,
|
||||
|
||||
done:
|
||||
if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dev_addr))))
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dev_addr)))) {
|
||||
arch_sync_dma_for_device(phys, size, dir);
|
||||
else
|
||||
arch_sync_dma_flush();
|
||||
} else {
|
||||
xen_dma_sync_for_device(dev, dev_addr, size, dir);
|
||||
}
|
||||
}
|
||||
return dev_addr;
|
||||
}
|
||||
@@ -287,10 +289,12 @@ static void xen_swiotlb_unmap_phys(struct device *hwdev, dma_addr_t dev_addr,
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
if (!dev_is_dma_coherent(hwdev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(hwdev, dev_addr))))
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(hwdev, dev_addr)))) {
|
||||
arch_sync_dma_for_cpu(paddr, size, dir);
|
||||
else
|
||||
arch_sync_dma_flush();
|
||||
} else {
|
||||
xen_dma_sync_for_cpu(hwdev, dev_addr, size, dir);
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: We use dev_addr here, not paddr! */
|
||||
@@ -308,10 +312,12 @@ xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr,
|
||||
struct io_tlb_pool *pool;
|
||||
|
||||
if (!dev_is_dma_coherent(dev)) {
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr))))
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) {
|
||||
arch_sync_dma_for_cpu(paddr, size, dir);
|
||||
else
|
||||
arch_sync_dma_flush();
|
||||
} else {
|
||||
xen_dma_sync_for_cpu(dev, dma_addr, size, dir);
|
||||
}
|
||||
}
|
||||
|
||||
pool = xen_swiotlb_find_pool(dev, dma_addr);
|
||||
@@ -331,10 +337,12 @@ xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr,
|
||||
__swiotlb_sync_single_for_device(dev, paddr, size, dir, pool);
|
||||
|
||||
if (!dev_is_dma_coherent(dev)) {
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr))))
|
||||
if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) {
|
||||
arch_sync_dma_for_device(paddr, size, dir);
|
||||
else
|
||||
arch_sync_dma_flush();
|
||||
} else {
|
||||
xen_dma_sync_for_device(dev, dma_addr, size, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,14 +61,4 @@ extern int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data)
|
||||
extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end);
|
||||
|
||||
extern void cma_reserve_pages_on_error(struct cma *cma);
|
||||
|
||||
#ifdef CONFIG_DMA_CMA
|
||||
extern bool cma_skip_dt_default_reserved_mem(void);
|
||||
#else
|
||||
static inline bool cma_skip_dt_default_reserved_mem(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef DMA_BUF_HEAP_CMA_H_
|
||||
#define DMA_BUF_HEAP_CMA_H_
|
||||
|
||||
struct cma;
|
||||
|
||||
#ifdef CONFIG_DMABUF_HEAPS_CMA
|
||||
int dma_heap_cma_register_heap(struct cma *cma);
|
||||
#else
|
||||
static inline int dma_heap_cma_register_heap(struct cma *cma)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif // CONFIG_DMABUF_HEAPS_CMA
|
||||
|
||||
#endif // DMA_BUF_HEAP_CMA_H_
|
||||
+12
-11
@@ -91,14 +91,8 @@ static inline void set_dma_ops(struct device *dev,
|
||||
#endif /* CONFIG_ARCH_HAS_DMA_OPS */
|
||||
|
||||
#ifdef CONFIG_DMA_CMA
|
||||
extern struct cma *dma_contiguous_default_area;
|
||||
|
||||
static inline struct cma *dev_get_cma_area(struct device *dev)
|
||||
{
|
||||
if (dev && dev->cma_area)
|
||||
return dev->cma_area;
|
||||
return dma_contiguous_default_area;
|
||||
}
|
||||
struct cma *dev_get_cma_area(struct device *dev);
|
||||
struct cma *dma_contiguous_get_area_by_idx(unsigned int idx);
|
||||
|
||||
void dma_contiguous_reserve(phys_addr_t addr_limit);
|
||||
int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
|
||||
@@ -117,6 +111,10 @@ static inline struct cma *dev_get_cma_area(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline struct cma *dma_contiguous_get_area_by_idx(unsigned int idx)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void dma_contiguous_reserve(phys_addr_t limit)
|
||||
{
|
||||
}
|
||||
@@ -147,9 +145,6 @@ static inline void dma_free_contiguous(struct device *dev, struct page *page,
|
||||
{
|
||||
__free_pages(page, get_order(size));
|
||||
}
|
||||
static inline void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_DMA_CMA*/
|
||||
|
||||
#ifdef CONFIG_DMA_DECLARE_COHERENT
|
||||
@@ -361,6 +356,12 @@ static inline void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
|
||||
}
|
||||
#endif /* ARCH_HAS_SYNC_DMA_FOR_CPU */
|
||||
|
||||
#ifndef CONFIG_ARCH_HAS_BATCHED_DMA_SYNC
|
||||
static inline void arch_sync_dma_flush(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL
|
||||
void arch_sync_dma_for_cpu_all(void);
|
||||
#else
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cache.h>
|
||||
|
||||
/**
|
||||
/*
|
||||
* List of possible attributes associated with a DMA mapping. The semantics
|
||||
* of each attribute should be defined in Documentation/core-api/dma-attributes.rst.
|
||||
*/
|
||||
@@ -92,6 +92,16 @@
|
||||
* flushing.
|
||||
*/
|
||||
#define DMA_ATTR_REQUIRE_COHERENT (1UL << 12)
|
||||
/*
|
||||
* DMA_ATTR_CC_SHARED: Indicates the DMA mapping is shared (decrypted) for
|
||||
* confidential computing guests. For normal system memory the caller must have
|
||||
* called set_memory_decrypted(), and pgprot_decrypted must be used when
|
||||
* creating CPU PTEs for the mapping. The same shared semantic may be passed
|
||||
* to the vIOMMU when it sets up the IOPTE. For MMIO use together with
|
||||
* DMA_ATTR_MMIO to indicate shared MMIO. Unless DMA_ATTR_MMIO is provided
|
||||
* a struct page is required.
|
||||
*/
|
||||
#define DMA_ATTR_CC_SHARED (1UL << 13)
|
||||
|
||||
/*
|
||||
* A dma_addr_t can hold any valid DMA or bus address for the platform. It can
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user