* [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map
@ 2026-09-02 10:47 Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 01/10] memblock: Introduce MEMBLOCK_LLMAP Vincent Donnefort
` (10 more replies)
0 siblings, 11 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
This series is a follow-up to the discussion that has started here [1].
While standalone, it also provides primitives reusable for the VPR DMA
heap.
When memory is lent to the Secure world via FF-A, CPU speculative
accesses from NS to the lent pages can still occur as long as it retains
a cacheable mapping to it.
Ideally, lent memory would be "no-map" but that would mean giving up
MiBs of useful memory, so let's try to do better with the help of a CMA
pool.
On arm64, modifying the direct map at runtime is generally restricted
because the linear map defaults to block mapping and splitting blocks at
runtime may trigger fatal page fault, unless the CPU implements BBML3
or the entire direct map was mapped at page granularity from boot.
Forcing last-level mappings system-wide incurs a severe penalty we want
to avoid. Instead, this series introduces targeted last-level mappings
for designated memory regions, along with the "arm,ffa-lend-pool" CMA
driver to manage unmapping and remapping on lend/reclaim transitions:
1. memblock & OF reserved memory ("ll-map"):
- Introduce MEMBLOCK_LLMAP and the DT "ll-map" property for reserved-memory
nodes to force last-level (PTE) mappings only for a specific region.
2. set_memory infrastructure:
- Introduce can_set_direct_map_range() to check if a specific address
range is mapped with last-level entries and can be modified safely.
- Introduce __set_direct_map_*() variants that bypass redundant checks
when the caller has already validated the range.
3. "arm,ffa-lend-pool" driver
- Introduce the "arm,ffa-lend-pool" CMA reserved-memory driver, which
unmaps pages prior to lending (ffa_prepare_lend()) and restores them
when reclaimed (ffa_lend_reclaimed()).
4. Optee support
- Hook OP-TEE dynamic protected memory pools to "arm,ffa-lend-pool" for
both SMC (via DT memory-region phandle) and FF-A (via
ffa_lend_pool_attach()) transports.
Testing:
========
Tested with QEMU v8 using OP-TEE OS (built with CFG_CORE_DYN_PROTMEM=y)
under both SMC and FF-A transports [2]
static void dump_direct_map(const char *label)
{
printf("\n=== %s ===\n", label);
fflush(stdout);
system("sed -n '/Linear Mapping start/,/Linear Mapping end/p' /sys/kernel/debug/kernel_page_tables");
fflush(stdout);
}
int main(int argc, char *argv[])
{
int heap_fd;
int dmabuf_fd;
struct dma_heap_allocation_data data = { 0 };
size_t size = 1024 * 1024; /* 1MB */
if (argc > 1)
size = strtoul(argv[1], NULL, 0);
dump_direct_map("BEFORE ALLOCATION");
heap_fd = open("/dev/dma_heap/protected,secure-video", O_RDWR);
if (heap_fd < 0) {
perror("open /dev/dma_heap/protected,secure-video");
return 1;
}
printf("\nOpened /dev/dma_heap/protected,secure-video\n");
printf("Allocating %zu bytes of protected memory via DMA heap...\n", size);
data.len = size;
data.fd_flags = O_RDWR | O_CLOEXEC;
if (ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data) < 0) {
perror("ioctl DMA_HEAP_IOCTL_ALLOC");
close(heap_fd);
return 1;
}
dmabuf_fd = data.fd;
printf("Successfully allocated %zu bytes! dmabuf_fd = %d\n", size, dmabuf_fd);
dump_direct_map("DURING LEND (EXPECT HOLE IN DIRECT MAP)");
printf("\nReleasing dmabuf_fd...\n");
close(dmabuf_fd);
close(heap_fd);
dump_direct_map("AFTER RECLAIM (RESTORED DIRECT MAP)");
return 0;
}
[1] https://lore.kernel.org/all/20260807-tegra-vpr-v4-7-5510d16af89e@nvidia.com/
[2] https://optee.readthedocs.io/en/latest/building/gits/build.html#qemu-v8
Vincent Donnefort (10):
memblock: Introduce MEMBLOCK_LLMAP
of: reserved_mem: Introduce "ll-map" property
set_memory.h: Introduce can_set_direct_map_range()
set_memory.h: Introduce __set_direct_map*()
arm64: can_set_direct_map() if BBML3
arm64: Implement can_set_direct_map_range()
arm64: Implement __set_direct_map*()
arm64: Add support for MEMBLOCK_LLMAP
firmware: arm_ffa: Introduce ffa-lend-pool
optee: Add support for arm,ffa-lend-pool
arch/arm64/include/asm/set_memory.h | 7 +
arch/arm64/mm/mmu.c | 23 ++-
arch/arm64/mm/pageattr.c | 67 +++++++-
drivers/firmware/arm_ffa/Kconfig | 5 +
drivers/firmware/arm_ffa/Makefile | 1 +
drivers/firmware/arm_ffa/lend_pool.c | 223 +++++++++++++++++++++++++++
drivers/of/of_reserved_mem.c | 104 ++++++++++---
drivers/tee/optee/ffa_abi.c | 13 +-
drivers/tee/optee/protmem.c | 8 -
drivers/tee/optee/smc_abi.c | 17 +-
drivers/tee/tee_shm.c | 11 +-
include/linux/arm_ffa.h | 21 +++
include/linux/memblock.h | 9 ++
include/linux/set_memory.h | 39 +++++
mm/memblock.c | 50 ++++++
15 files changed, 545 insertions(+), 53 deletions(-)
create mode 100644 drivers/firmware/arm_ffa/lend_pool.c
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v9 01/10] memblock: Introduce MEMBLOCK_LLMAP
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property Vincent Donnefort
` (9 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
Keeping last-level mappings is interesting on some architectures as it
allows mapping/unmapping pages from the kernel direct map without the
risk of splitting blocks which, under the break-before-make rule, may
trigger page-faults the kernel can't handle.
However, mapping the entire direct map at PTE-level is costly. So
instead, create a new memblock flag MEMBLOCK_LLMAP to enable the system
to decide which region must be covered by mappings up to the last-level.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index d62db9e776cf..d40a5ded188d 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -52,6 +52,7 @@ extern unsigned long long max_possible_pfn;
* kernel that we know is good to use. It is the only memory that
* allocations may happen from in this phase.
* @MEMBLOCK_RSRV_HUGETLB: memory is reserved for hugetlb pages
+ * @MEMBLOCK_LLMAP: memory region to be mapped using last-level mapping
*/
enum memblock_flags {
MEMBLOCK_NONE = 0x0, /* No special request */
@@ -63,6 +64,7 @@ enum memblock_flags {
MEMBLOCK_RSRV_KERN = 0x20, /* memory reserved for kernel use */
MEMBLOCK_KHO_SCRATCH = 0x40, /* scratch memory for kexec handover */
MEMBLOCK_RSRV_HUGETLB = 0x80, /* memory reserved for hugetlb pages */
+ MEMBLOCK_LLMAP = 0x100,/* last-level mapping */
};
/**
@@ -160,6 +162,8 @@ int memblock_reserved_mark_noinit(phys_addr_t base, phys_addr_t size);
int memblock_reserved_mark_kern(phys_addr_t base, phys_addr_t size);
int memblock_mark_kho_scratch(phys_addr_t base, phys_addr_t size);
int memblock_clear_kho_scratch(phys_addr_t base, phys_addr_t size);
+int memblock_mark_llmap(phys_addr_t base, phys_addr_t size);
+int memblock_clear_llmap(phys_addr_t base, phys_addr_t size);
void memblock_free(void *ptr, size_t size);
void reset_all_zones_managed_pages(void);
@@ -306,6 +310,11 @@ static inline bool memblock_is_kho_scratch(struct memblock_region *m)
return m->flags & MEMBLOCK_KHO_SCRATCH;
}
+static inline bool memblock_is_llmap(struct memblock_region *m)
+{
+ return m->flags & MEMBLOCK_LLMAP;
+}
+
int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
unsigned long *end_pfn);
void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn,
diff --git a/mm/memblock.c b/mm/memblock.c
index 9ce86349a29f..1591b50503ed 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -1119,6 +1119,16 @@ int __init_memblock memblock_mark_mirror(phys_addr_t base, phys_addr_t size)
*/
int __init_memblock memblock_mark_nomap(phys_addr_t base, phys_addr_t size)
{
+ struct memblock_region *r;
+
+ memblock_cap_size(base, &size);
+
+ for_each_mem_region(r) {
+ if (memblock_is_llmap(r) &&
+ memblock_addrs_overlap(base, size, r->base, r->size))
+ return -EINVAL;
+ }
+
return memblock_setclr_flag(&memblock.memory, base, size, 1, MEMBLOCK_NOMAP);
}
@@ -1204,6 +1214,45 @@ __init int memblock_clear_kho_scratch(phys_addr_t base, phys_addr_t size)
MEMBLOCK_KHO_SCRATCH);
}
+/**
+ * memblock_mark_llmap - Mark a memory region with flag MEMBLOCK_LLMAP.
+ * @base: the base phys addr of the region
+ * @size: the size of the region
+ *
+ * If supported by the architecture, such region is mapped at the last-level in
+ * the kernel direct map.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int __init_memblock memblock_mark_llmap(phys_addr_t base, phys_addr_t size)
+{
+ struct memblock_region *r;
+
+ memblock_cap_size(base, &size);
+
+ for_each_mem_region(r) {
+ if (memblock_is_nomap(r) &&
+ memblock_addrs_overlap(base, size, r->base, r->size))
+ return -EINVAL;
+ }
+
+ return memblock_setclr_flag(&memblock.memory, base, size, 1,
+ MEMBLOCK_LLMAP);
+}
+
+/**
+ * memblock_clear_llmap - Clear flag MEMBLOCK_LLMAP for a specified region.
+ * @base: the base phys addr of the region
+ * @size: the size of the region
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int __init_memblock memblock_clear_llmap(phys_addr_t base, phys_addr_t size)
+{
+ return memblock_setclr_flag(&memblock.memory, base, size, 0,
+ MEMBLOCK_LLMAP);
+}
+
static bool should_skip_region(struct memblock_type *type,
struct memblock_region *m,
int nid, int flags)
@@ -2886,6 +2935,7 @@ static const char * const flagname[] = {
[ilog2(MEMBLOCK_RSRV_NOINIT)] = "RSV_NIT",
[ilog2(MEMBLOCK_RSRV_KERN)] = "RSV_KERN",
[ilog2(MEMBLOCK_KHO_SCRATCH)] = "KHO_SCRATCH",
+ [ilog2(MEMBLOCK_LLMAP)] = "LLMAP",
};
static int memblock_debug_show(struct seq_file *m, void *private)
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 01/10] memblock: Introduce MEMBLOCK_LLMAP Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 17:24 ` Rob Herring
2026-09-02 10:47 ` [PATCH v9 03/10] set_memory.h: Introduce can_set_direct_map_range() Vincent Donnefort
` (8 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
Keeping last-level mappings is interesting on some architectures as it
allows mapping/unmapping pages from the kernel direct map without the
risk of splitting blocks which, under the break-before-make rule, may
trigger page-faults the kernel can't handle.
Add an "ll-map" property for reserved-memory regions. When set, it
splits the underlying memblock and sets the MEMBLOCK_LLMAP flag.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 8c9d6395d6a3..9ff2d02cb149 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -32,9 +32,32 @@ static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
static int reserved_mem_count;
+enum of_rmem_flags {
+ OF_RMEM_NONE = 0,
+ OF_RMEM_NOMAP = BIT(0),
+ OF_RMEM_LLMAP = BIT(1),
+};
+
+static int __init of_reserved_mem_flags(unsigned long node, enum of_rmem_flags *flags)
+{
+ *flags = OF_RMEM_NONE;
+
+ if (of_get_flat_dt_prop(node, "no-map", NULL))
+ *flags |= OF_RMEM_NOMAP;
+ if (of_get_flat_dt_prop(node, "ll-map", NULL))
+ *flags |= OF_RMEM_LLMAP;
+
+ if ((*flags & OF_RMEM_NOMAP) && (*flags & OF_RMEM_LLMAP)) {
+ pr_err("Reserved memory: no-map and ll-map are mutually exclusive\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
- phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
- phys_addr_t *res_base)
+ phys_addr_t align, phys_addr_t start, phys_addr_t end,
+ enum of_rmem_flags flags, phys_addr_t *res_base)
{
phys_addr_t base;
int err = 0;
@@ -46,10 +69,21 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
return -ENOMEM;
*res_base = base;
- if (nomap) {
- err = memblock_mark_nomap(base, size);
- if (err)
+ if (flags & OF_RMEM_LLMAP) {
+ err = memblock_mark_llmap(base, size);
+ if (err) {
memblock_phys_free(base, size);
+ return err;
+ }
+ }
+
+ if (flags & OF_RMEM_NOMAP) {
+ err = memblock_mark_nomap(base, size);
+ if (err) {
+ if (flags & OF_RMEM_LLMAP)
+ memblock_clear_llmap(base, size);
+ memblock_phys_free(base, size);
+ }
}
if (!err)
@@ -119,17 +153,29 @@ 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)
+ phys_addr_t size,
+ enum of_rmem_flags flags)
{
- if (nomap) {
+ int err;
+
+ if (flags & OF_RMEM_LLMAP) {
+ err = memblock_mark_llmap(base, size);
+ if (err)
+ return err;
+ }
+
+ if (flags & OF_RMEM_NOMAP) {
/*
* If the memory is already reserved (by another region), we
* should not allow it to be marked nomap, but don't worry
* if the region isn't memory as it won't be mapped.
*/
if (memblock_overlaps_region(&memblock.memory, base, size) &&
- memblock_is_region_reserved(base, size))
+ memblock_is_region_reserved(base, size)) {
+ if (flags & OF_RMEM_LLMAP)
+ memblock_clear_llmap(base, size);
return -EBUSY;
+ }
return memblock_mark_nomap(base, size);
}
@@ -143,10 +189,10 @@ static int __init early_init_dt_reserve_memory(phys_addr_t base,
static int __init __reserved_mem_reserve_reg(unsigned long node,
const char *uname)
{
+ enum of_rmem_flags flags;
phys_addr_t base, size;
- int len, err;
const __be32 *prop;
- bool nomap;
+ int len, err;
u64 b, s;
prop = of_flat_dt_get_addr_size_prop(node, "reg", &len);
@@ -157,7 +203,9 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
pr_warn("Reserved memory: node '%s' has %d <base size> entries, only the first is used\n",
uname, len);
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ err = of_reserved_mem_flags(node, &flags);
+ if (err)
+ return err;
err = fdt_validate_reserved_mem_node(node, NULL);
if (err && err != -ENODEV)
@@ -167,7 +215,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
base = b;
size = s;
- if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) {
+ if (size && early_init_dt_reserve_memory(base, size, flags) == 0) {
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));
@@ -399,8 +447,8 @@ int __init fdt_scan_reserved_mem(void)
* reserved regions to keep the reserved memory contiguous if possible.
*/
static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
- phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
- phys_addr_t *res_base)
+ phys_addr_t align, phys_addr_t start, phys_addr_t end,
+ enum of_rmem_flags flags, phys_addr_t *res_base)
{
bool prev_bottom_up = memblock_bottom_up();
bool bottom_up = false, top_down = false;
@@ -435,7 +483,7 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
memblock_set_bottom_up(bottom_up);
ret = early_init_dt_alloc_reserved_memory_arch(size, align,
- start, end, nomap, res_base);
+ start, end, flags, res_base);
/* Restore old setting if needed */
if (bottom_up != top_down)
@@ -452,9 +500,9 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
{
phys_addr_t start = 0, end = 0;
phys_addr_t base = 0, align = 0, size;
+ enum of_rmem_flags flags;
int i, len;
const __be32 *prop;
- bool nomap;
int ret;
prop = of_get_flat_dt_prop(node, "size", &len);
@@ -477,7 +525,9 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
align = dt_mem_next_cell(dt_root_addr_cells, &prop);
}
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ ret = of_reserved_mem_flags(node, &flags);
+ if (ret)
+ return ret;
ret = fdt_validate_reserved_mem_node(node, &align);
if (ret && ret != -ENODEV)
@@ -495,7 +545,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
base = 0;
ret = __reserved_mem_alloc_in_range(size, align,
- start, end, nomap, &base);
+ start, end, flags, &base);
if (ret == 0) {
pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n",
uname, &base,
@@ -505,7 +555,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
}
} else {
ret = early_init_dt_alloc_reserved_memory_arch(size, align,
- 0, 0, nomap, &base);
+ 0, 0, flags, &base);
if (ret == 0)
pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n",
uname, &base, (unsigned long)(size / SZ_1M));
@@ -635,8 +685,8 @@ static int __init __reserved_mem_init_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)
{
+ enum of_rmem_flags flags;
int err = 0;
- bool nomap;
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
@@ -650,14 +700,19 @@ static void __init fdt_init_reserved_mem_node(unsigned long node, const char *un
rmem->base = base;
rmem->size = size;
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ err = of_reserved_mem_flags(node, &flags);
+ if (err)
+ return;
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)
+ if (flags & OF_RMEM_LLMAP)
+ memblock_clear_llmap(rmem->base, rmem->size);
+
+ if (flags & OF_RMEM_NOMAP)
memblock_clear_nomap(rmem->base, rmem->size);
else
memblock_phys_free(rmem->base, rmem->size);
@@ -667,9 +722,10 @@ static void __init fdt_init_reserved_mem_node(unsigned long node, const char *un
bool reusable =
(of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
- pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
+ pr_info("%pa..%pa (%lu KiB) %s %s %s %s\n",
&rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
- nomap ? "nomap" : "map",
+ (flags & OF_RMEM_LLMAP) ? "last-level " : "",
+ (flags & OF_RMEM_NOMAP) ? "nomap" : "map",
reusable ? "reusable" : "non-reusable",
rmem->name ? rmem->name : "unknown");
}
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 03/10] set_memory.h: Introduce can_set_direct_map_range()
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 01/10] memblock: Introduce MEMBLOCK_LLMAP Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 04/10] set_memory.h: Introduce __set_direct_map*() Vincent Donnefort
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
While can_set_direct_map() tells whether the direct map can be modified
globally, can_set_direct_map_range() checks if a specific range can be
modified. e.g. if mapped at PTE-level.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
index 3030d9245f5a..88175c3fa751 100644
--- a/include/linux/set_memory.h
+++ b/include/linux/set_memory.h
@@ -44,6 +44,12 @@ static inline bool kernel_page_present(struct page *page)
{
return true;
}
+
+static inline bool can_set_direct_map_range(struct page *page,
+ unsigned long nr_pages)
+{
+ return false;
+}
#else /* CONFIG_ARCH_HAS_SET_DIRECT_MAP */
/*
* Some architectures, e.g. ARM64 can disable direct map modifications at
@@ -56,6 +62,14 @@ static inline bool can_set_direct_map(void)
}
#define can_set_direct_map can_set_direct_map
#endif
+
+#ifndef can_set_direct_map_range
+static inline bool can_set_direct_map_range(struct page *page, unsigned long nr_pages)
+{
+ return can_set_direct_map();
+}
+#define can_set_direct_map_range can_set_direct_map_range
+#endif
#endif /* CONFIG_ARCH_HAS_SET_DIRECT_MAP */
#ifdef CONFIG_X86_64
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 04/10] set_memory.h: Introduce __set_direct_map*()
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (2 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 03/10] set_memory.h: Introduce can_set_direct_map_range() Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 05/10] arm64: can_set_direct_map() if BBML3 Vincent Donnefort
` (6 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
set_direct_map_*() implementations verify wheter the direct map can be
effectively set. This verification can be costly. Add an "advanced" API
__set_direct_map_*() where the direct map is modified without any check.
The caller must have verified it is safe earlier with one of the
can_set_direct_map() or can_set_direct_map_range() functions.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
index 88175c3fa751..5a1917a3fd52 100644
--- a/include/linux/set_memory.h
+++ b/include/linux/set_memory.h
@@ -34,6 +34,15 @@ static inline int set_direct_map_default_noflush(struct page *page)
return 0;
}
+static inline int __set_direct_map_invalid_noflush(struct page *page)
+{
+ return 0;
+}
+static inline int __set_direct_map_default_noflush(struct page *page)
+{
+ return 0;
+}
+
static inline int set_direct_map_valid_noflush(struct page *page,
unsigned nr, bool valid)
{
@@ -70,6 +79,22 @@ static inline bool can_set_direct_map_range(struct page *page, unsigned long nr_
}
#define can_set_direct_map_range can_set_direct_map_range
#endif
+
+#ifndef __set_direct_map_invalid_noflush
+static inline int __set_direct_map_invalid_noflush(struct page *page)
+{
+ return set_direct_map_invalid_noflush(page);
+}
+#define __set_direct_map_invalid_noflush __set_direct_map_invalid_noflush
+#endif
+
+#ifndef __set_direct_map_default_noflush
+static inline int __set_direct_map_default_noflush(struct page *page)
+{
+ return set_direct_map_default_noflush(page);
+}
+#define __set_direct_map_default_noflush __set_direct_map_default_noflush
+#endif
#endif /* CONFIG_ARCH_HAS_SET_DIRECT_MAP */
#ifdef CONFIG_X86_64
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 05/10] arm64: can_set_direct_map() if BBML3
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (3 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 04/10] set_memory.h: Introduce __set_direct_map*() Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 06/10] arm64: Implement can_set_direct_map_range() Vincent Donnefort
` (5 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
Systems supporting BBML3 can split blocks without risking unhandled
page-faults. It is therefore safe for them to set the direct map
unconditionally.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c..58898ae53f5d 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -99,7 +99,8 @@ bool can_set_direct_map(void)
* Realms need to make pages shared/protected at page granularity.
*/
return rodata_full || debug_pagealloc_enabled() ||
- arm64_kfence_can_set_direct_map() || is_realm_world();
+ arm64_kfence_can_set_direct_map() || is_realm_world() ||
+ system_supports_bbml3();
}
static int update_range_prot(unsigned long start, unsigned long size,
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 06/10] arm64: Implement can_set_direct_map_range()
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (4 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 05/10] arm64: can_set_direct_map() if BBML3 Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 07/10] arm64: Implement __set_direct_map*() Vincent Donnefort
` (4 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
For systems where can_set_direct_map() is false, it is still possible
that a subregion of the direct map can be modified if it is mapped at
the last-level. Add an implementation for can_set_direct_map_range(). It
falls back to a page table walk to verify the mapping level if
can_set_direct_map() is false.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index 90f61b17275e..1a37f4ef130b 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -9,6 +9,9 @@
bool can_set_direct_map(void);
#define can_set_direct_map can_set_direct_map
+bool can_set_direct_map_range(struct page *page, unsigned long nr_pages);
+#define can_set_direct_map_range can_set_direct_map_range
+
int set_memory_valid(unsigned long addr, int numpages, int enable);
int set_direct_map_invalid_noflush(struct page *page);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 58898ae53f5d..c59ef17eb0d0 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -103,6 +103,48 @@ bool can_set_direct_map(void)
system_supports_bbml3();
}
+bool can_set_direct_map_range(struct page *page, unsigned long nr_pages)
+{
+ unsigned long addr = (unsigned long)page_address(page);
+ unsigned long end = addr + nr_pages * PAGE_SIZE;
+
+ if (can_set_direct_map())
+ return true;
+
+ /*
+ * If !can_set_direct_map() then no one can split blocks and it is safe
+ * to walk the page-table lockless.
+ */
+ while (addr < end) {
+ pud_t *pudp, pud;
+ pmd_t *pmdp, pmd;
+ pgd_t *pgdp;
+ p4d_t *p4dp;
+
+ pgdp = pgd_offset_k(addr);
+ if (pgd_none(READ_ONCE(*pgdp)))
+ return false;
+
+ p4dp = p4d_offset(pgdp, addr);
+ if (p4d_none(READ_ONCE(*p4dp)))
+ return false;
+
+ pudp = pud_offset(p4dp, addr);
+ pud = READ_ONCE(*pudp);
+ if (pud_none(pud) || pud_leaf(pud))
+ return false;
+
+ pmdp = pmd_offset(pudp, addr);
+ pmd = READ_ONCE(*pmdp);
+ if (pmd_none(pmd) || pmd_leaf(pmd))
+ return false;
+
+ addr = pmd_addr_end(addr, end);
+ }
+
+ return true;
+}
+
static int update_range_prot(unsigned long start, unsigned long size,
pgprot_t set_mask, pgprot_t clear_mask)
{
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 07/10] arm64: Implement __set_direct_map*()
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (5 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 06/10] arm64: Implement can_set_direct_map_range() Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 08/10] arm64: Add support for MEMBLOCK_LLMAP Vincent Donnefort
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
Add implementation for the unsafe functions __set_direct_map*(). They do
not verify for can_set_direct_map() and expect the caller to do so
beforehand.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index 1a37f4ef130b..080a40846bbe 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -16,6 +16,10 @@ int set_memory_valid(unsigned long addr, int numpages, int enable);
int set_direct_map_invalid_noflush(struct page *page);
int set_direct_map_default_noflush(struct page *page);
+int __set_direct_map_invalid_noflush(struct page *page);
+#define __set_direct_map_invalid_noflush __set_direct_map_invalid_noflush
+int __set_direct_map_default_noflush(struct page *page);
+#define __set_direct_map_default_noflush __set_direct_map_default_noflush
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index c59ef17eb0d0..28adab0edace 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -294,28 +294,38 @@ int set_memory_valid(unsigned long addr, int numpages, int enable)
__pgprot(PTE_PRESENT_VALID_KERNEL));
}
-int set_direct_map_invalid_noflush(struct page *page)
+int __set_direct_map_invalid_noflush(struct page *page)
{
pgprot_t clear_mask = __pgprot(PTE_PRESENT_VALID_KERNEL);
pgprot_t set_mask = __pgprot(PTE_PRESENT_INVALID);
+ return update_range_prot((unsigned long)page_address(page),
+ PAGE_SIZE, set_mask, clear_mask);
+}
+
+int set_direct_map_invalid_noflush(struct page *page)
+{
if (!can_set_direct_map())
return 0;
+ return __set_direct_map_invalid_noflush(page);
+}
+
+int __set_direct_map_default_noflush(struct page *page)
+{
+ pgprot_t set_mask = __pgprot(PTE_PRESENT_VALID_KERNEL | PTE_WRITE);
+ pgprot_t clear_mask = __pgprot(PTE_PRESENT_INVALID | PTE_RDONLY);
+
return update_range_prot((unsigned long)page_address(page),
PAGE_SIZE, set_mask, clear_mask);
}
int set_direct_map_default_noflush(struct page *page)
{
- pgprot_t set_mask = __pgprot(PTE_PRESENT_VALID_KERNEL | PTE_WRITE);
- pgprot_t clear_mask = __pgprot(PTE_PRESENT_INVALID | PTE_RDONLY);
-
if (!can_set_direct_map())
return 0;
- return update_range_prot((unsigned long)page_address(page),
- PAGE_SIZE, set_mask, clear_mask);
+ return __set_direct_map_default_noflush(page);
}
static int __set_memory_enc_dec(unsigned long addr,
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 08/10] arm64: Add support for MEMBLOCK_LLMAP
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (6 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 07/10] arm64: Implement __set_direct_map*() Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool Vincent Donnefort
` (2 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
With the MEMBLOCK_LLMAP flag, force a last-level mapping.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 79d90226fd5d..c0fcc76914d1 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1184,9 +1184,8 @@ static void __init map_mem(void)
phys_addr_t init_begin = __pa_symbol(__init_begin);
phys_addr_t init_end = __pa_symbol(__init_end);
phys_addr_t kernel_end = __pa_symbol(__bss_stop);
- phys_addr_t start, end;
int flags = NO_EXEC_MAPPINGS;
- u64 i;
+ struct memblock_region *r;
/*
* Setting hierarchical PXNTable attributes on table entries covering
@@ -1226,9 +1225,13 @@ static void __init map_mem(void)
flags);
/* map all the memory banks */
- for_each_mem_range(i, &start, &end) {
+ for_each_mem_region(r) {
+ phys_addr_t start, end;
+
+ if (memblock_is_nomap(r))
+ continue;
/*
- * for_each_mem_range may return sub-page-aligned boundaries
+ * for_each_mem_region may return sub-page-aligned boundaries
* after memblock_mark_nomap() splits regions at byte precision.
* __create_pgd_mapping_locked aligns phys down to PAGE_MASK,
* which could accidentally map no-map memory on the boundary.
@@ -1237,17 +1240,23 @@ static void __init map_mem(void)
* regions. The cost is at most one page of unmapped gap at
* each boundary.
*/
- start = PAGE_ALIGN(start);
- end = end & PAGE_MASK;
+ start = PAGE_ALIGN(r->base);
+ end = (r->base + r->size) & PAGE_MASK;
if (start >= end)
continue;
+
+ int rflags = flags;
+
+ if (memblock_is_llmap(r))
+ rflags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
+
/*
* The linear map must allow allocation tags reading/writing
* if MTE is present. Otherwise, it has the same attributes as
* PAGE_KERNEL.
*/
__map_memblock(start, end, pgprot_tagged(PAGE_KERNEL),
- flags);
+ rflags);
}
}
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (7 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 08/10] arm64: Add support for MEMBLOCK_LLMAP Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 17:38 ` Rob Herring
2026-09-02 10:47 ` [PATCH v9 10/10] optee: Add support for arm,ffa-lend-pool Vincent Donnefort
2026-09-02 13:27 ` [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
10 siblings, 1 reply; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
When memory is lent to the Secure world via FF-A, fatal CPU speculative
reads from Non-Secure can still occur as long as it retains a cacheable
mapping to that memory. Introduce the "arm,ffa-lend-pool"
reserved-memory CMA driver to unmap pages before lending
(ffa_prepare_lend()) and restore them upon reclaim
(ffa_lend_reclaimed()). Devices bind to the pool via the "memory-region"
DT property or via ffa_lend_pool_attach().
reserved-memory {
#address-cells = <0x2>;
#size-cells = <0x2>;
ranges;
ffa_lend: ffa-lend-pool {
compatible = "arm,ffa-lend-pool";
reusable;
ll-map;
size = <0x0 0x4000000>;
};
};
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/drivers/firmware/arm_ffa/Kconfig b/drivers/firmware/arm_ffa/Kconfig
index 5e3ae5cf82e8..66dbf74c37c3 100644
--- a/drivers/firmware/arm_ffa/Kconfig
+++ b/drivers/firmware/arm_ffa/Kconfig
@@ -19,3 +19,8 @@ config ARM_FFA_SMCCC
bool
default ARM_FFA_TRANSPORT
depends on ARM64 && HAVE_ARM_SMCCC_DISCOVERY
+
+config ARM_FFA_LEND_POOL
+ bool
+ default y
+ depends on ARM_FFA_TRANSPORT && CMA && OF_RESERVED_MEM
diff --git a/drivers/firmware/arm_ffa/Makefile b/drivers/firmware/arm_ffa/Makefile
index 168990a7e792..5ea3019c407b 100644
--- a/drivers/firmware/arm_ffa/Makefile
+++ b/drivers/firmware/arm_ffa/Makefile
@@ -6,3 +6,4 @@ ffa-core-objs := $(ffa-bus-y)
ffa-module-objs := $(ffa-driver-y) $(ffa-transport-y)
obj-$(CONFIG_ARM_FFA_TRANSPORT) = ffa-core.o
obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa-module.o
+obj-$(CONFIG_ARM_FFA_LEND_POOL) += lend_pool.o
diff --git a/drivers/firmware/arm_ffa/lend_pool.c b/drivers/firmware/arm_ffa/lend_pool.c
new file mode 100644
index 000000000000..67a36f5d5aa6
--- /dev/null
+++ b/drivers/firmware/arm_ffa/lend_pool.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Arm FF-A Reserved Memory CMA driver for Memory Lending
+ *
+ * Prevents CPU speculative reads to secure memory by unmapping it from the
+ * kernel direct map. This only works if the reserved-memory is mapped at the
+ * last-level ("ll-map;" or "rodata=full") or if all CPUs in the system support
+ * BBML3.
+ *
+ * Copyright (C) 2026 Google LLC
+ * Author: Vincent Donnefort <vdonnefort@google.com>
+ */
+
+#include <linux/arm_ffa.h>
+#include <linux/cleanup.h>
+#include <linux/cma.h>
+#include <linux/dma-map-ops.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/rcupdate.h>
+#include <linux/set_memory.h>
+#include <linux/xarray.h>
+
+static DEFINE_XARRAY(ffa_lend_devices);
+
+static bool ffa_lend_pool_contains(struct device *dev, phys_addr_t addr, size_t size)
+{
+ phys_addr_t base, end;
+
+ if (!dev)
+ return false;
+
+ guard(rcu)();
+
+ if (xa_load(&ffa_lend_devices, (unsigned long)dev) != dev)
+ return false;
+
+ if (WARN_ON_ONCE(!dev->cma_area))
+ return false;
+
+ base = cma_get_base(dev->cma_area);
+ end = base + cma_get_size(dev->cma_area);
+
+ return addr >= base && (addr + size) <= end;
+}
+
+/**
+ * ffa_prepare_lend() - Prepare a memory region to be lent in FF-A
+ * @dev: Device attached to the lend pool
+ * @addr: Physical start address of the memory region
+ * @size: Size in bytes
+ *
+ * When memory is lent via FF-A, TrustZone transitions it to the secure state.
+ * As long as Arm CPUs retain a valid mapping to that now-secure memory, they
+ * can speculatively read it, which is fatal on some systems.
+ *
+ * ffa_prepare_lend() prevents this by unmapping the memory range from the
+ * kernel's direct map.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int ffa_prepare_lend(struct device *dev, phys_addr_t addr, size_t size)
+{
+ unsigned long nr_pages = size >> PAGE_SHIFT;
+ struct page *page;
+ unsigned long i;
+ int ret;
+
+ if (!ffa_lend_pool_contains(dev, addr, size))
+ return -ENODEV;
+
+ page = pfn_to_page(PHYS_PFN(addr));
+ for (i = 0; i < nr_pages; i++) {
+ ret = __set_direct_map_invalid_noflush(page + i);
+
+ if (ret) {
+ while (i--)
+ __set_direct_map_default_noflush(page + i);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ffa_prepare_lend);
+
+/**
+ * ffa_lend_reclaimed() - Restore a reclaimed FF-A memory region
+ * @dev: Device attached to the lend pool
+ * @addr: Physical start address of the memory region
+ * @size: Size in bytes
+ *
+ * Restores a memory range into the kernel's direct mapping. It must be called
+ * after a successful FF-A memory reclaim invocation.
+ */
+void ffa_lend_reclaimed(struct device *dev, phys_addr_t addr, size_t size)
+{
+ unsigned long nr_pages = size >> PAGE_SHIFT;
+ struct page *page;
+ unsigned long i;
+
+ if (!ffa_lend_pool_contains(dev, addr, size))
+ return;
+
+ page = pfn_to_page(PHYS_PFN(addr));
+ for (i = 0; i < nr_pages; i++)
+ __set_direct_map_default_noflush(page + i);
+}
+EXPORT_SYMBOL_GPL(ffa_lend_reclaimed);
+
+/**
+ * ffa_lend_pool_attach() - Attach a device to the FF-A lend pool
+ * @dev: Device to attach
+ *
+ * FF-A devices are dynamically discovered and might not have an associated
+ * device tree node with a "memory-region" phandle. In that case, drivers must
+ * use this function to attach to the "arm,ffa-lend-pool" reserved memory
+ * region.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int ffa_lend_pool_attach(struct device *dev)
+{
+ struct device_node *np;
+ struct reserved_mem *rmem;
+
+ np = of_find_compatible_node(NULL, NULL, "arm,ffa-lend-pool");
+ if (!np)
+ return -ENODEV;
+
+ rmem = of_reserved_mem_lookup(np);
+ of_node_put(np);
+ if (!rmem || !rmem->ops || !rmem->ops->device_init)
+ return -EINVAL;
+
+ return rmem->ops->device_init(rmem, dev);
+}
+EXPORT_SYMBOL_GPL(ffa_lend_pool_attach);
+
+/**
+ * ffa_lend_pool_detach() - Detach a device from the FF-A lend pool
+ * @dev: Device to detach
+ *
+ * Releases the device from the "arm,ffa-lend-pool" reserved memory region.
+ */
+void ffa_lend_pool_detach(struct device *dev)
+{
+ struct device_node *np;
+ struct reserved_mem *rmem;
+
+ np = of_find_compatible_node(NULL, NULL, "arm,ffa-lend-pool");
+ if (!np)
+ return;
+
+ rmem = of_reserved_mem_lookup(np);
+ of_node_put(np);
+
+ if (rmem && rmem->ops && rmem->ops->device_release)
+ rmem->ops->device_release(rmem, dev);
+}
+EXPORT_SYMBOL_GPL(ffa_lend_pool_detach);
+
+static int __init ffa_lend_pool_setup(unsigned long node, struct reserved_mem *rmem)
+{
+ struct cma *cma;
+ int ret;
+
+ if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
+ of_get_flat_dt_prop(node, "no-map", NULL)) {
+ pr_err("FF-A lend pool: node must be 'reusable' and not 'no-map'\n");
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(rmem->base | rmem->size, CMA_MIN_ALIGNMENT_BYTES)) {
+ pr_err("FF-A lend pool: incorrect alignment of CMA region\n");
+ return -EINVAL;
+ }
+
+ ret = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
+ if (ret) {
+ pr_err("FF-A lend pool: unable to setup CMA region (%d)\n", ret);
+ return ret;
+ }
+
+ rmem->priv = cma;
+
+ return 0;
+}
+
+static int ffa_lend_pool_device_init(struct reserved_mem *rmem, struct device *dev)
+{
+ int ret;
+
+ if (!can_set_direct_map_range(pfn_to_page(PHYS_PFN(rmem->base)), rmem->size / PAGE_SIZE)) {
+ pr_err("FF-A lend pool: reserved memory cannot be unmapped in direct map\n");
+ return -EINVAL;
+ }
+
+ dev->cma_area = rmem->priv;
+
+ ret = xa_err(xa_store(&ffa_lend_devices, (unsigned long)dev, dev, GFP_KERNEL));
+ if (ret)
+ dev->cma_area = NULL;
+
+ return ret;
+}
+
+static void ffa_lend_pool_device_release(struct reserved_mem *rmem, struct device *dev)
+{
+ xa_erase(&ffa_lend_devices, (unsigned long)dev);
+ dev->cma_area = NULL;
+}
+
+static const struct reserved_mem_ops ffa_lend_pool_ops = {
+ .node_init = ffa_lend_pool_setup,
+ .device_init = ffa_lend_pool_device_init,
+ .device_release = ffa_lend_pool_device_release,
+};
+RESERVEDMEM_OF_DECLARE(ffa_lend_pool, "arm,ffa-lend-pool", &ffa_lend_pool_ops);
diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
index e71d83ee0aef..fa3570f85ff7 100644
--- a/include/linux/arm_ffa.h
+++ b/include/linux/arm_ffa.h
@@ -519,4 +519,25 @@ struct ffa_ops {
const struct ffa_notifier_ops *notifier_ops;
};
+#if IS_ENABLED(CONFIG_ARM_FFA_LEND_POOL)
+int ffa_lend_pool_attach(struct device *dev);
+void ffa_lend_pool_detach(struct device *dev);
+int ffa_prepare_lend(struct device *dev, phys_addr_t paddr, size_t size);
+void ffa_lend_reclaimed(struct device *dev, phys_addr_t paddr, size_t size);
+#else
+static inline int ffa_lend_pool_attach(struct device *dev)
+{
+ return -ENODEV;
+}
+static inline void ffa_lend_pool_detach(struct device *dev)
+{
+}
+static inline int ffa_prepare_lend(struct device *dev, phys_addr_t paddr, size_t size)
+{
+ return -ENODEV;
+}
+static inline void ffa_lend_reclaimed(struct device *dev, phys_addr_t paddr, size_t size)
+{
+}
+#endif
#endif /* _LINUX_ARM_FFA_H */
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v9 10/10] optee: Add support for arm,ffa-lend-pool
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (8 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool Vincent Donnefort
@ 2026-09-02 10:47 ` Vincent Donnefort
2026-09-02 13:27 ` [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 10:47 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel, Vincent Donnefort
Hook OP-TEE dynamically allocated protected memory pools to the
"arm,ffa-lend-pool" driver. While the SMC transport platform device
resolves the pool through its DT "memory-region" property, the FF-A
transport lacks a device tree node and binds via ffa_lend_pool_attach().
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index 633715b98625..4d6555171918 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -979,6 +979,7 @@ static void optee_ffa_remove(struct ffa_device *ffa_dev)
mutex_destroy(&optee->ffa.mutex);
rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL);
+ ffa_lend_pool_detach(&optee->teedev->dev);
kfree(optee);
}
@@ -1042,13 +1043,21 @@ static int optee_ffa_protmem_pool_init(struct optee *optee, u32 sec_caps)
int rc = 0;
if (sec_caps & OPTEE_FFA_SEC_CAP_PROTMEM) {
+ rc = ffa_lend_pool_attach(&optee->teedev->dev);
+ if (rc && rc != -ENODEV)
+ return rc;
+
pool = optee_protmem_alloc_dyn_pool(optee, id);
- if (IS_ERR(pool))
+ if (IS_ERR(pool)) {
+ ffa_lend_pool_detach(&optee->teedev->dev);
return PTR_ERR(pool);
+ }
rc = tee_device_register_dma_heap(optee->teedev, id, pool);
- if (rc)
+ if (rc) {
pool->ops->destroy_pool(pool);
+ ffa_lend_pool_detach(&optee->teedev->dev);
+ }
}
return rc;
diff --git a/drivers/tee/optee/protmem.c b/drivers/tee/optee/protmem.c
index be3abf6e8aa6..9b64db9b4e64 100644
--- a/drivers/tee/optee/protmem.c
+++ b/drivers/tee/optee/protmem.c
@@ -42,14 +42,6 @@ static int init_dyn_protmem(struct optee_protmem_dyn_pool *rp)
goto err_null_protmem;
}
- /*
- * TODO unmap the memory range since the physical memory will
- * become inaccesible after the lend_protmem() call.
- *
- * If the platform supports a hypervisor at EL2, it will unmap the
- * intermediate physical memory for us and stop cache pre-fetch of
- * the memory.
- */
rc = rp->optee->ops->lend_protmem(rp->optee, rp->protmem,
rp->mem_attrs,
rp->mem_attr_count, rp->use_case);
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index b8a2bdac3208..5949aa717cad 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/rpmb.h>
#include <linux/sched.h>
@@ -1528,6 +1529,8 @@ static void optee_smc_remove(struct platform_device *pdev)
if (optee->smc.memremaped_shm)
memunmap(optee->smc.memremaped_shm);
+ of_reserved_mem_device_release(&optee->teedev->dev);
+
kfree(optee);
}
@@ -1712,16 +1715,22 @@ static int optee_protmem_pool_init(struct optee *optee)
if (!protm && !dyn_protm)
return 0;
+ of_reserved_mem_device_init_by_idx(&optee->teedev->dev,
+ dev_of_node(optee->teedev->dev.parent), 0);
if (protm)
pool = static_protmem_pool_init(optee);
if (dyn_protm && IS_ERR(pool))
pool = optee_protmem_alloc_dyn_pool(optee, heap_id);
- if (IS_ERR(pool))
+ if (IS_ERR(pool)) {
+ of_reserved_mem_device_release(&optee->teedev->dev);
return PTR_ERR(pool);
+ }
rc = tee_device_register_dma_heap(optee->teedev, heap_id, pool);
- if (rc)
+ if (rc) {
pool->ops->destroy_pool(pool);
+ of_reserved_mem_device_release(&optee->teedev->dev);
+ }
return rc;
}
@@ -1833,14 +1842,14 @@ static int optee_probe(struct platform_device *pdev)
(sec_caps & OPTEE_SMC_SEC_CAP_RPMB_PROBE))
optee->in_kernel_rpmb_routing = true;
- teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee);
+ teedev = tee_device_alloc(&optee_clnt_desc, &pdev->dev, pool, optee);
if (IS_ERR(teedev)) {
rc = PTR_ERR(teedev);
goto err_free_optee;
}
optee->teedev = teedev;
- teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee);
+ teedev = tee_device_alloc(&optee_supp_desc, &pdev->dev, pool, optee);
if (IS_ERR(teedev)) {
rc = PTR_ERR(teedev);
goto err_unreg_teedev;
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 6742b3579c86..49a9b2993c83 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -3,6 +3,7 @@
* Copyright (c) 2015-2017, 2019-2021 Linaro Limited
*/
#include <linux/anon_inodes.h>
+#include <linux/arm_ffa.h>
#include <linux/device.h>
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
@@ -43,6 +44,7 @@ static void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm)
dma_mem = container_of(shm, struct tee_shm_dma_mem, shm);
p = dma_mem;
+ ffa_lend_reclaimed(&teedev->dev, shm->paddr, shm->size);
dma_free_pages(&teedev->dev, shm->size, dma_mem->page,
dma_mem->dma_addr, DMA_BIDIRECTIONAL);
#endif
@@ -288,6 +290,7 @@ struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx,
struct tee_shm_dma_mem *dma_mem;
dma_addr_t dma_addr;
struct page *page;
+ int ret;
if (!tee_device_get(teedev))
return ERR_PTR(-EINVAL);
@@ -297,9 +300,13 @@ struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx,
if (!page)
goto err_put_teedev;
+ ret = ffa_prepare_lend(&teedev->dev, page_to_phys(page), page_count * PAGE_SIZE);
+ if (ret && ret != -ENODEV)
+ goto err_free_pages;
+
dma_mem = kzalloc_obj(*dma_mem);
if (!dma_mem)
- goto err_free_pages;
+ goto err_map_pages;
refcount_set(&dma_mem->shm.refcount, 1);
dma_mem->shm.ctx = ctx;
@@ -313,6 +320,8 @@ struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx,
return &dma_mem->shm;
+err_map_pages:
+ ffa_lend_reclaimed(&teedev->dev, page_to_phys(page), page_count * PAGE_SIZE);
err_free_pages:
dma_free_pages(&teedev->dev, page_count * PAGE_SIZE, page, dma_addr,
DMA_BIDIRECTIONAL);
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
` (9 preceding siblings ...)
2026-09-02 10:47 ` [PATCH v9 10/10] optee: Add support for arm,ffa-lend-pool Vincent Donnefort
@ 2026-09-02 13:27 ` Vincent Donnefort
10 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-02 13:27 UTC (permalink / raw)
To: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi, robh
Cc: mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel
This is of course v1, not v9...
On Wed, Sep 02, 2026 at 11:47:02AM +0100, Vincent Donnefort wrote:
> This series is a follow-up to the discussion that has started here [1].
> While standalone, it also provides primitives reusable for the VPR DMA
> heap.
>
> When memory is lent to the Secure world via FF-A, CPU speculative
> accesses from NS to the lent pages can still occur as long as it retains
> a cacheable mapping to it.
>
> Ideally, lent memory would be "no-map" but that would mean giving up
> MiBs of useful memory, so let's try to do better with the help of a CMA
> pool.
>
> On arm64, modifying the direct map at runtime is generally restricted
> because the linear map defaults to block mapping and splitting blocks at
> runtime may trigger fatal page fault, unless the CPU implements BBML3
> or the entire direct map was mapped at page granularity from boot.
> Forcing last-level mappings system-wide incurs a severe penalty we want
> to avoid. Instead, this series introduces targeted last-level mappings
> for designated memory regions, along with the "arm,ffa-lend-pool" CMA
> driver to manage unmapping and remapping on lend/reclaim transitions:
>
> 1. memblock & OF reserved memory ("ll-map"):
> - Introduce MEMBLOCK_LLMAP and the DT "ll-map" property for reserved-memory
> nodes to force last-level (PTE) mappings only for a specific region.
>
> 2. set_memory infrastructure:
> - Introduce can_set_direct_map_range() to check if a specific address
> range is mapped with last-level entries and can be modified safely.
> - Introduce __set_direct_map_*() variants that bypass redundant checks
> when the caller has already validated the range.
>
> 3. "arm,ffa-lend-pool" driver
> - Introduce the "arm,ffa-lend-pool" CMA reserved-memory driver, which
> unmaps pages prior to lending (ffa_prepare_lend()) and restores them
> when reclaimed (ffa_lend_reclaimed()).
>
> 4. Optee support
> - Hook OP-TEE dynamic protected memory pools to "arm,ffa-lend-pool" for
> both SMC (via DT memory-region phandle) and FF-A (via
> ffa_lend_pool_attach()) transports.
>
> Testing:
> ========
>
> Tested with QEMU v8 using OP-TEE OS (built with CFG_CORE_DYN_PROTMEM=y)
> under both SMC and FF-A transports [2]
>
> static void dump_direct_map(const char *label)
> {
> printf("\n=== %s ===\n", label);
> fflush(stdout);
> system("sed -n '/Linear Mapping start/,/Linear Mapping end/p' /sys/kernel/debug/kernel_page_tables");
> fflush(stdout);
> }
>
> int main(int argc, char *argv[])
> {
> int heap_fd;
> int dmabuf_fd;
> struct dma_heap_allocation_data data = { 0 };
> size_t size = 1024 * 1024; /* 1MB */
>
> if (argc > 1)
> size = strtoul(argv[1], NULL, 0);
>
> dump_direct_map("BEFORE ALLOCATION");
>
> heap_fd = open("/dev/dma_heap/protected,secure-video", O_RDWR);
> if (heap_fd < 0) {
> perror("open /dev/dma_heap/protected,secure-video");
> return 1;
> }
>
> printf("\nOpened /dev/dma_heap/protected,secure-video\n");
> printf("Allocating %zu bytes of protected memory via DMA heap...\n", size);
>
> data.len = size;
> data.fd_flags = O_RDWR | O_CLOEXEC;
> if (ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data) < 0) {
> perror("ioctl DMA_HEAP_IOCTL_ALLOC");
> close(heap_fd);
> return 1;
> }
>
> dmabuf_fd = data.fd;
> printf("Successfully allocated %zu bytes! dmabuf_fd = %d\n", size, dmabuf_fd);
>
> dump_direct_map("DURING LEND (EXPECT HOLE IN DIRECT MAP)");
>
> printf("\nReleasing dmabuf_fd...\n");
> close(dmabuf_fd);
> close(heap_fd);
>
> dump_direct_map("AFTER RECLAIM (RESTORED DIRECT MAP)");
>
> return 0;
> }
>
> [1] https://lore.kernel.org/all/20260807-tegra-vpr-v4-7-5510d16af89e@nvidia.com/
> [2] https://optee.readthedocs.io/en/latest/building/gits/build.html#qemu-v8
>
> Vincent Donnefort (10):
> memblock: Introduce MEMBLOCK_LLMAP
> of: reserved_mem: Introduce "ll-map" property
> set_memory.h: Introduce can_set_direct_map_range()
> set_memory.h: Introduce __set_direct_map*()
> arm64: can_set_direct_map() if BBML3
> arm64: Implement can_set_direct_map_range()
> arm64: Implement __set_direct_map*()
> arm64: Add support for MEMBLOCK_LLMAP
> firmware: arm_ffa: Introduce ffa-lend-pool
> optee: Add support for arm,ffa-lend-pool
>
> arch/arm64/include/asm/set_memory.h | 7 +
> arch/arm64/mm/mmu.c | 23 ++-
> arch/arm64/mm/pageattr.c | 67 +++++++-
> drivers/firmware/arm_ffa/Kconfig | 5 +
> drivers/firmware/arm_ffa/Makefile | 1 +
> drivers/firmware/arm_ffa/lend_pool.c | 223 +++++++++++++++++++++++++++
> drivers/of/of_reserved_mem.c | 104 ++++++++++---
> drivers/tee/optee/ffa_abi.c | 13 +-
> drivers/tee/optee/protmem.c | 8 -
> drivers/tee/optee/smc_abi.c | 17 +-
> drivers/tee/tee_shm.c | 11 +-
> include/linux/arm_ffa.h | 21 +++
> include/linux/memblock.h | 9 ++
> include/linux/set_memory.h | 39 +++++
> mm/memblock.c | 50 ++++++
> 15 files changed, 545 insertions(+), 53 deletions(-)
> create mode 100644 drivers/firmware/arm_ffa/lend_pool.c
>
>
> base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
> --
> 2.55.0.970.g62bdec98f9-goog
>
--
Vincent
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property
2026-09-02 10:47 ` [PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property Vincent Donnefort
@ 2026-09-02 17:24 ` Rob Herring
2026-09-03 10:03 ` Vincent Donnefort
0 siblings, 1 reply; 16+ messages in thread
From: Rob Herring @ 2026-09-02 17:24 UTC (permalink / raw)
To: Vincent Donnefort
Cc: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi,
mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel
On Wed, Sep 02, 2026 at 11:47:04AM +0100, Vincent Donnefort wrote:
> Keeping last-level mappings is interesting on some architectures as it
> allows mapping/unmapping pages from the kernel direct map without the
> risk of splitting blocks which, under the break-before-make rule, may
> trigger page-faults the kernel can't handle.
>
> Add an "ll-map" property for reserved-memory regions. When set, it
> splits the underlying memblock and sets the MEMBLOCK_LLMAP flag.
Where is this documented? Any DT property must have a schema.
Though I'm not thrilled about more flag properties. Handling all the
combinations of properties has proven to be painful. Can this be implied
from the compatible string instead?
Rob
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool
2026-09-02 10:47 ` [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool Vincent Donnefort
@ 2026-09-02 17:38 ` Rob Herring
2026-09-03 10:10 ` Vincent Donnefort
0 siblings, 1 reply; 16+ messages in thread
From: Rob Herring @ 2026-09-02 17:38 UTC (permalink / raw)
To: Vincent Donnefort
Cc: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi,
mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel
On Wed, Sep 02, 2026 at 11:47:11AM +0100, Vincent Donnefort wrote:
> When memory is lent to the Secure world via FF-A, fatal CPU speculative
> reads from Non-Secure can still occur as long as it retains a cacheable
> mapping to that memory. Introduce the "arm,ffa-lend-pool"
> reserved-memory CMA driver to unmap pages before lending
> (ffa_prepare_lend()) and restore them upon reclaim
> (ffa_lend_reclaimed()). Devices bind to the pool via the "memory-region"
> DT property or via ffa_lend_pool_attach().
>
> reserved-memory {
> #address-cells = <0x2>;
> #size-cells = <0x2>;
> ranges;
>
> ffa_lend: ffa-lend-pool {
> compatible = "arm,ffa-lend-pool";
Needs a binding schema...
> reusable;
> ll-map;
> size = <0x0 0x4000000>;
> };
> };
>
> Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
[...]
> +static int __init ffa_lend_pool_setup(unsigned long node, struct reserved_mem *rmem)
> +{
> + struct cma *cma;
> + int ret;
> +
> + if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
> + of_get_flat_dt_prop(node, "no-map", NULL)) {
> + pr_err("FF-A lend pool: node must be 'reusable' and not 'no-map'\n");
> + return -EINVAL;
Your schema should enforce/check this.
[...]
> +static const struct reserved_mem_ops ffa_lend_pool_ops = {
> + .node_init = ffa_lend_pool_setup,
> + .device_init = ffa_lend_pool_device_init,
> + .device_release = ffa_lend_pool_device_release,
> +};
> +RESERVEDMEM_OF_DECLARE(ffa_lend_pool, "arm,ffa-lend-pool", &ffa_lend_pool_ops);
Do you need this early? If not, you can create a platform driver for
"arm,ffa-lend-pool". ramoops is implemented that way for example.
Rob
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property
2026-09-02 17:24 ` Rob Herring
@ 2026-09-03 10:03 ` Vincent Donnefort
0 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-03 10:03 UTC (permalink / raw)
To: Rob Herring
Cc: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi,
mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel
On Wed, Sep 02, 2026 at 12:24:36PM -0500, Rob Herring wrote:
> On Wed, Sep 02, 2026 at 11:47:04AM +0100, Vincent Donnefort wrote:
> > Keeping last-level mappings is interesting on some architectures as it
> > allows mapping/unmapping pages from the kernel direct map without the
> > risk of splitting blocks which, under the break-before-make rule, may
> > trigger page-faults the kernel can't handle.
> >
> > Add an "ll-map" property for reserved-memory regions. When set, it
> > splits the underlying memblock and sets the MEMBLOCK_LLMAP flag.
>
> Where is this documented? Any DT property must have a schema.
Ack.
>
> Though I'm not thrilled about more flag properties. Handling all the
> combinations of properties has proven to be painful. Can this be implied
> from the compatible string instead?
>
> Rob
We need this before the kernel direct map has been created (paging_init()).
I would have moved that directly into the driver lend_pool.c, but then it is
probed too late (unflatten_device_tree()).
--
Vincent
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool
2026-09-02 17:38 ` Rob Herring
@ 2026-09-03 10:10 ` Vincent Donnefort
0 siblings, 0 replies; 16+ messages in thread
From: Vincent Donnefort @ 2026-09-03 10:10 UTC (permalink / raw)
To: Rob Herring
Cc: catalin.marinas, will, rppt, akpm, sudeep.holla, jenswi,
mark.rutland, sumit.garg, ardb, thierry.reding, david,
danielmentz, linux-arm-kernel, linux-mm, op-tee, devicetree,
linux-kernel
On Wed, Sep 02, 2026 at 12:38:26PM -0500, Rob Herring wrote:
> On Wed, Sep 02, 2026 at 11:47:11AM +0100, Vincent Donnefort wrote:
> > When memory is lent to the Secure world via FF-A, fatal CPU speculative
> > reads from Non-Secure can still occur as long as it retains a cacheable
> > mapping to that memory. Introduce the "arm,ffa-lend-pool"
> > reserved-memory CMA driver to unmap pages before lending
> > (ffa_prepare_lend()) and restore them upon reclaim
> > (ffa_lend_reclaimed()). Devices bind to the pool via the "memory-region"
> > DT property or via ffa_lend_pool_attach().
> >
> > reserved-memory {
> > #address-cells = <0x2>;
> > #size-cells = <0x2>;
> > ranges;
> >
> > ffa_lend: ffa-lend-pool {
> > compatible = "arm,ffa-lend-pool";
>
> Needs a binding schema...
Ack
>
> > reusable;
> > ll-map;
> > size = <0x0 0x4000000>;
> > };
> > };
> >
> > Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
>
> [...]
>
> > +static int __init ffa_lend_pool_setup(unsigned long node, struct reserved_mem *rmem)
> > +{
> > + struct cma *cma;
> > + int ret;
> > +
> > + if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
> > + of_get_flat_dt_prop(node, "no-map", NULL)) {
> > + pr_err("FF-A lend pool: node must be 'reusable' and not 'no-map'\n");
> > + return -EINVAL;
>
> Your schema should enforce/check this.
>
> [...]
>
> > +static const struct reserved_mem_ops ffa_lend_pool_ops = {
> > + .node_init = ffa_lend_pool_setup,
> > + .device_init = ffa_lend_pool_device_init,
> > + .device_release = ffa_lend_pool_device_release,
> > +};
> > +RESERVEDMEM_OF_DECLARE(ffa_lend_pool, "arm,ffa-lend-pool", &ffa_lend_pool_ops);
>
> Do you need this early? If not, you can create a platform driver for
> "arm,ffa-lend-pool". ramoops is implemented that way for example.
>
> Rob
Only memblock_mark_llmap() must be called very early.
The rest just need to be in place before the TEE driver probed. So a platform
driver should work.
Thanks!
--
Vincent
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-09-03 10:10 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 10:47 [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 01/10] memblock: Introduce MEMBLOCK_LLMAP Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property Vincent Donnefort
2026-09-02 17:24 ` Rob Herring
2026-09-03 10:03 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 03/10] set_memory.h: Introduce can_set_direct_map_range() Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 04/10] set_memory.h: Introduce __set_direct_map*() Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 05/10] arm64: can_set_direct_map() if BBML3 Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 06/10] arm64: Implement can_set_direct_map_range() Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 07/10] arm64: Implement __set_direct_map*() Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 08/10] arm64: Add support for MEMBLOCK_LLMAP Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool Vincent Donnefort
2026-09-02 17:38 ` Rob Herring
2026-09-03 10:10 ` Vincent Donnefort
2026-09-02 10:47 ` [PATCH v9 10/10] optee: Add support for arm,ffa-lend-pool Vincent Donnefort
2026-09-02 13:27 ` [PATCH v9 00/10] arm64: Unmap FF-A lent memory from direct map Vincent Donnefort
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox