* [RFC PATCH v2 1/2] firmware: arm_ffa: Add FFA_MEM_RECLAIM2 function ID
2026-10-16 8:23 [RFC PATCH v2 0/2] KVM: arm64: Support FFA_MEM_RECLAIM2 in host handler Snehal Koukuntla
@ 2026-10-16 8:23 ` Snehal Koukuntla
2026-10-16 8:23 ` [RFC PATCH v2 2/2] KVM: arm64: Support FFA_MEM_RECLAIM2 in host handler Snehal Koukuntla
1 sibling, 0 replies; 3+ messages in thread
From: Snehal Koukuntla @ 2026-10-16 8:23 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon, Sudeep Holla
Cc: Vincent Donnefort, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Mark Rutland, Sebastian Ene,
Mostafa Saleh, Snehal Koukuntla, linux-arm-kernel, kvmarm,
linux-kernel
FF-A v1.3 ALP5 (DEN0140 rev J) introduces the optional FFA_MEM_RECLAIM2
(0xC4000091) ABI, which enables an Owner to reclaim exclusive access to
a subset of pages of a memory region without requiring the Relayer to
maintain intermediate transaction state.
Add the FFA_MEM_RECLAIM2 function ID definition to arm_ffa.h.
Link: https://support.arm.com/documentation/den0140/j
Signed-off-by: Snehal Koukuntla <snehalreddy@google.com>
---
Changes in v2:
- Rename FFA_FN64_MEM_RECLAIM2 to FFA_MEM_RECLAIM2, following the
existing convention for 64-bit-only ABIs (sashiko)
include/linux/arm_ffa.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
index e71d83ee0aef..85c2e490a387 100644
--- a/include/linux/arm_ffa.h
+++ b/include/linux/arm_ffa.h
@@ -78,6 +78,7 @@
#define FFA_EL3_INTR_HANDLE FFA_SMC_32(0x8C)
#define FFA_MSG_SEND_DIRECT_REQ2 FFA_SMC_64(0x8D)
#define FFA_MSG_SEND_DIRECT_RESP2 FFA_SMC_64(0x8E)
+#define FFA_MEM_RECLAIM2 FFA_SMC_64(0x91)
/*
* For some calls it is necessary to use SMC64 to pass or return 64-bit values.
--
2.56.0.176.g3575e65682-goog
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC PATCH v2 2/2] KVM: arm64: Support FFA_MEM_RECLAIM2 in host handler
2026-10-16 8:23 [RFC PATCH v2 0/2] KVM: arm64: Support FFA_MEM_RECLAIM2 in host handler Snehal Koukuntla
2026-10-16 8:23 ` [RFC PATCH v2 1/2] firmware: arm_ffa: Add FFA_MEM_RECLAIM2 function ID Snehal Koukuntla
@ 2026-10-16 8:23 ` Snehal Koukuntla
1 sibling, 0 replies; 3+ messages in thread
From: Snehal Koukuntla @ 2026-10-16 8:23 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon, Sudeep Holla
Cc: Vincent Donnefort, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Mark Rutland, Sebastian Ene,
Mostafa Saleh, Snehal Koukuntla, linux-arm-kernel, kvmarm,
linux-kernel
FF-A v1.3 ALP5 (DEN0140 rev J) adds the FFA_MEM_RECLAIM2 interface,
allowing an Owner to reclaim a subset of pages from a shared or lent
memory region by populating constituent memory region descriptors in
its TX buffer along with a starting descriptor byte offset and initial
page index. Upon completion, the Relayer returns the number of 4K pages
reclaimed in x3.
Support FFA_MEM_RECLAIM2 in the host FF-A proxy handler. Validate the
register arguments, copy the constituent descriptors from the host TX
buffer into the hypervisor's TX buffer, and check the descriptor bounds,
the total page count and the initial page index on that copy before
forwarding the call to the SPMD. On success, unshare the reclaimed page
ranges from the host stage-2 page tables.
Link: https://support.arm.com/documentation/den0140/j
Signed-off-by: Snehal Koukuntla <snehalreddy@google.com>
---
Changes in v2:
- Rename FFA_FN64_MEM_RECLAIM2 to FFA_MEM_RECLAIM2
- Check that the sum of pg_cnt across all constituent descriptors
matches total_pages, guarding against overflow (Vincent)
- Simplify the flags and offset alignment checks (Vincent)
arch/arm64/kvm/hyp/nvhe/ffa.c | 126 ++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c
index 5ad74ab265b5..2f49ffaa9361 100644
--- a/arch/arm64/kvm/hyp/nvhe/ffa.c
+++ b/arch/arm64/kvm/hyp/nvhe/ffa.c
@@ -232,6 +232,19 @@ static void ffa_mem_reclaim(struct arm_smccc_1_2_regs *res, u32 handle_lo,
}, res);
}
+static void ffa_mem_reclaim2(struct arm_smccc_1_2_regs *res, u64 handle,
+ u64 flags, u64 x3, u64 x4, u64 x5)
+{
+ hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) {
+ .a0 = FFA_MEM_RECLAIM2,
+ .a1 = handle,
+ .a2 = flags,
+ .a3 = x3,
+ .a4 = x4,
+ .a5 = x5,
+ }, res);
+}
+
static void ffa_retrieve_req(struct arm_smccc_1_2_regs *res, u32 len)
{
hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) {
@@ -611,6 +624,116 @@ static void __do_ffa_mem_xfer(const u64 func_id,
__do_ffa_mem_xfer((fid), (res), (ctxt)); \
} while (0);
+static void do_ffa_mem_reclaim2(struct arm_smccc_1_2_regs *res,
+ struct kvm_cpu_context *ctxt)
+{
+ DECLARE_REG(u64, handle, ctxt, 1);
+ DECLARE_REG(u64, flags, ctxt, 2);
+ DECLARE_REG(u64, x3, ctxt, 3);
+ DECLARE_REG(u64, x4, ctxt, 4);
+ DECLARE_REG(u64, x5, ctxt, 5);
+ u32 req_desc_cnt = (u32)(x3 >> 32);
+ u32 total_pages = (u32)x3;
+ u32 desc_size = (u32)x4;
+ u32 offset = (u32)x5;
+ u32 page_index = (u32)(x5 >> 32);
+ struct ffa_mem_region_addr_range *range;
+ u32 declared_pages = 0;
+ u32 reclaimed_pages;
+ u32 reclaimed_count = 0;
+ u32 in_idx;
+ int ret = 0;
+
+ if (ffa_check_unused_args_sbz(ctxt, 6) || flags > 1 || (x4 >> 32)) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
+
+ if (!req_desc_cnt || !total_pages) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
+
+ if (desc_size != sizeof(struct ffa_mem_region_addr_range)) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
+
+ if ((u64)req_desc_cnt * desc_size > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
+
+ if (offset % desc_size) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
+
+ if (offset >= (u64)req_desc_cnt * desc_size) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
+
+ hyp_spin_lock(&host_buffers.lock);
+ if (!host_buffers.tx) {
+ ret = FFA_RET_INVALID_PARAMETERS;
+ goto out_unlock;
+ }
+
+ memcpy(hyp_buffers.tx, host_buffers.tx, (u64)req_desc_cnt * desc_size);
+
+ for (in_idx = 0; in_idx < req_desc_cnt; in_idx++) {
+ range = (void *)hyp_buffers.tx + in_idx * desc_size;
+ if (check_add_overflow(declared_pages, range->pg_cnt, &declared_pages)) {
+ ret = FFA_RET_INVALID_PARAMETERS;
+ goto out_unlock;
+ }
+ }
+
+ range = (void *)hyp_buffers.tx + offset;
+ if (declared_pages != total_pages || page_index >= range->pg_cnt) {
+ ret = FFA_RET_INVALID_PARAMETERS;
+ goto out_unlock;
+ }
+
+ ffa_mem_reclaim2(res, handle, flags, x3, x4, x5);
+ if (res->a0 != FFA_SUCCESS)
+ goto out_unlock;
+
+ reclaimed_pages = res->a3;
+
+ for (in_idx = offset / desc_size; in_idx < req_desc_cnt; in_idx++) {
+ struct ffa_mem_region_addr_range temp;
+ u32 pg_cnt, to_reclaim;
+
+ if (reclaimed_count >= reclaimed_pages)
+ break;
+
+ range = (void *)hyp_buffers.tx + in_idx * desc_size;
+ if (!range->pg_cnt)
+ continue;
+
+ pg_cnt = range->pg_cnt - page_index;
+ to_reclaim = min(pg_cnt, reclaimed_pages - reclaimed_count);
+
+ temp = (struct ffa_mem_region_addr_range) {
+ .address = range->address + (u64)page_index * FFA_PAGE_SIZE,
+ .pg_cnt = to_reclaim,
+ };
+
+ WARN_ON(ffa_host_unshare_ranges(&temp, 1));
+
+ reclaimed_count += to_reclaim;
+ page_index = 0;
+ }
+
+out_unlock:
+ hyp_spin_unlock(&host_buffers.lock);
+
+ if (ret)
+ ffa_to_smccc_res(res, ret);
+}
+
static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
struct kvm_cpu_context *ctxt)
{
@@ -1107,6 +1230,9 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
case FFA_MEM_RECLAIM:
do_ffa_mem_reclaim(&res, host_ctxt);
goto out_handled;
+ case FFA_MEM_RECLAIM2:
+ do_ffa_mem_reclaim2(&res, host_ctxt);
+ goto out_handled;
case FFA_MEM_LEND:
case FFA_FN64_MEM_LEND:
do_ffa_mem_xfer(FFA_FN64_MEM_LEND, &res, host_ctxt);
--
2.56.0.176.g3575e65682-goog
^ permalink raw reply [flat|nested] 3+ messages in thread