From: Vincent Donnefort <vdonnefort@google.com>
To: catalin.marinas@arm.com, will@kernel.org, rppt@kernel.org,
akpm@linux-foundation.org, sudeep.holla@kernel.org,
jenswi@kernel.org, robh@kernel.org
Cc: mark.rutland@arm.com, sumit.garg@kernel.org, ardb@kernel.org,
thierry.reding@kernel.org, david@kernel.org,
danielmentz@google.com, linux-arm-kernel@lists.infradead.org,
linux-mm@kvack.org, op-tee@lists.trustedfirmware.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Vincent Donnefort <vdonnefort@google.com>
Subject: [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool
Date: Wed, 2 Sep 2026 11:47:11 +0100 [thread overview]
Message-ID: <20260902104712.2399797-10-vdonnefort@google.com> (raw)
In-Reply-To: <20260902104712.2399797-1-vdonnefort@google.com>
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
next prev parent reply other threads:[~2026-09-02 10:51 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Vincent Donnefort [this message]
2026-09-02 17:38 ` [PATCH v9 09/10] firmware: arm_ffa: Introduce ffa-lend-pool 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260902104712.2399797-10-vdonnefort@google.com \
--to=vdonnefort@google.com \
--cc=akpm@linux-foundation.org \
--cc=ardb@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=danielmentz@google.com \
--cc=david@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=jenswi@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mark.rutland@arm.com \
--cc=op-tee@lists.trustedfirmware.org \
--cc=robh@kernel.org \
--cc=rppt@kernel.org \
--cc=sudeep.holla@kernel.org \
--cc=sumit.garg@kernel.org \
--cc=thierry.reding@kernel.org \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox