Re: [PATCH v2 25/35] KVM: arm64: Reclaim faulting page from pKVM in spurious fault handler

Alexandru Elisei
2026-02-12T17:22:36Z

Hi Will,

Would be nice to merge this with the previous patch, that added the force
reclaim function, as it would make reviewing easier.

On Mon, Jan 19, 2026 at 12:46:18PM +0000, Will Deacon wrote:
> Host kernel accesses to pages that are inaccessible at stage-2 result in
> the injection of a translation fault, which is fatal unless an exception
> table fixup is registered for the faulting PC (e.g. for user access
> routines). This is undesirable, since a get_user_pages() call could be
> used to obtain a reference to a donated page and then a subsequent
> access via a kernel mapping would lead to a panic().
>
> Rework the spurious fault handler so that stage-2 faults injected back
> into the host result in the target page being forcefully reclaimed when
> no exception table fixup handler is registered.
>
> Signed-off-by: Will Deacon <will@kernel.org>
> ---
> arch/arm64/include/asm/virt.h | 6 ++++++
> arch/arm64/kvm/pkvm.c | 7 +++++++
> arch/arm64/mm/fault.c | 15 +++++++++------
> 3 files changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
> index b51ab6840f9c..e80addc923a4 100644
> --- a/arch/arm64/include/asm/virt.h
> +++ b/arch/arm64/include/asm/virt.h
> @@ -94,6 +94,12 @@ static inline bool is_pkvm_initialized(void)
> static_branch_likely(&kvm_protected_mode_initialized);
> }
>
> +#ifdef CONFIG_KVM
> +bool pkvm_reclaim_guest_page(phys_addr_t phys);
> +#else
> +static inline bool pkvm_reclaim_guest_page(phys_addr_t phys) { return false; }
> +#endif
> +
> /* Reports the availability of HYP mode */
> static inline bool is_hyp_mode_available(void)
> {
> diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> index 8be91051699e..d1926cb08c76 100644
> --- a/arch/arm64/kvm/pkvm.c
> +++ b/arch/arm64/kvm/pkvm.c
> @@ -563,3 +563,10 @@ int pkvm_pgtable_stage2_split(struct kvm_pgtable *pgt, u64 addr, u64 size,
> WARN_ON_ONCE(1);
> return -EINVAL;
> }
> +
> +bool pkvm_reclaim_guest_page(phys_addr_t phys)
> +{
> + int ret = kvm_call_hyp_nvhe(__pkvm_force_reclaim_guest_page, phys);

Nitpicking here, we have the functions __pkvm_reclaim_page_guest() and this
function, pkvm_reclaim_guest_page(), which calls
__pkvm_force_reclaim_guest_page, which in turn calls
__pkvm_host_force_reclaim_page_guest(). I think having a bit of naming
consistency would be really useful when navigating the source code.

It might also be useful to document that callers of the hypercall
__pkvm_force_reclaim_guest_page are not expected to unpin the page in case of
success, but callers of __pkvm_reclaim_dying_guest_page are.

Thanks,
Alex

> +
> + return !ret || ret == -EAGAIN;
> +}
> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> index 2294f2061866..5d62abee5262 100644
> --- a/arch/arm64/mm/fault.c
> +++ b/arch/arm64/mm/fault.c
> @@ -289,9 +289,6 @@ static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr,
> if (!is_el1_data_abort(esr) || !esr_fsc_is_translation_fault(esr))
> return false;
>
> - if (is_pkvm_stage2_abort(esr))
> - return false;
> -
> local_irq_save(flags);
> asm volatile("at s1e1r, %0" :: "r" (addr));
> isb();
> @@ -302,8 +299,12 @@ static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr,
> * If we now have a valid translation, treat the translation fault as
> * spurious.
> */
> - if (!(par & SYS_PAR_EL1_F))
> + if (!(par & SYS_PAR_EL1_F)) {
> + if (is_pkvm_stage2_abort(esr))
> + return pkvm_reclaim_guest_page(par & SYS_PAR_EL1_PA);
> +
> return true;
> + }
>
> /*
> * If we got a different type of fault from the AT instruction,
> @@ -389,9 +390,11 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr,
> if (!is_el1_instruction_abort(esr) && fixup_exception(regs, esr))
> return;
>
> - if (WARN_RATELIMIT(is_spurious_el1_translation_fault(addr, esr, regs),
> - "Ignoring spurious kernel translation fault at virtual address %016lx\n", addr))
> + if (is_spurious_el1_translation_fault(addr, esr, regs)) {
> + WARN_RATELIMIT(!is_pkvm_stage2_abort(esr),
> + "Ignoring spurious kernel translation fault at virtual address %016lx\n", addr);
> return;
> + }
>
> if (is_el1_mte_sync_tag_check_fault(esr)) {
> do_tag_recovery(addr, esr, regs);
> --
> 2.52.0.457.g6b5491de43-goog
>
>