From: Marc Zyngier <maz@kernel.org>
To: Oliver Upton <oupton@kernel.org>
Cc: kvmarm@lists.linux.dev, Joey Gouly <joey.gouly@arm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Wei-Lin Chang <weilin.chang@arm.com>,
stable@vger.kernel.org, Sashiko <sashiko-bot@kernel.org>
Subject: Re: [PATCH RESEND v2 3/5] KVM: arm64: nv: Re-translate VNCR before injecting abort
Date: Wed, 10 Jun 2026 13:42:34 +0100 [thread overview]
Message-ID: <867bo6tvlx.wl-maz@kernel.org> (raw)
In-Reply-To: <20260609185514.746507-4-oupton@kernel.org>
On Tue, 09 Jun 2026 19:55:12 +0100,
Oliver Upton <oupton@kernel.org> wrote:
>
> KVM faults in the VNCR page with FOLL_WRITE whenver the guest aborts for
> a write, similar to how a regular stage-2 mapping is handled. It is
> entirely possible that the guest reads from the VNCR before writing to
> it, in which case the PFN could only be read-only.
>
> Invalidate the VNCR TLB and re-fetch the translation upon taking a VNCR
> abort, allowing the host mapping to be faulted in for write the second
> time around. Interestingly enough, this also satisfies the ordering
> requirements of FEAT_ETS2/3 between descriptor updates and MMU faults.
>
> Cc: stable@vger.kernel.org
> Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2")
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Signed-off-by: Oliver Upton <oupton@kernel.org>
> ---
> arch/arm64/kvm/nested.c | 115 +++++++++++++++-------------------------
> 1 file changed, 44 insertions(+), 71 deletions(-)
>
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index ebd7ccfeee99..d5c4b57123a9 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -1460,92 +1460,65 @@ static void handle_vncr_perm(struct kvm_vcpu *vcpu)
> kvm_inject_nested_sync(vcpu, esr);
> }
>
> -static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu)
> -{
> - struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
> -
> - lockdep_assert_held_read(&vcpu->kvm->mmu_lock);
> -
> - if (!vt->valid)
> - return false;
> -
> - if (read_vncr_el2(vcpu) != vt->gva)
> - return false;
> -
> - if (vt->wr.nG)
> - return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid;
> -
> - return true;
> -}
> -
> int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
> {
> struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
> u64 esr = kvm_vcpu_get_esr(vcpu);
> + bool is_gmem, perm;
> + int ret;
>
> WARN_ON_ONCE(!(esr & ESR_ELx_VNCR));
>
> if (kvm_vcpu_abt_issea(vcpu))
> return kvm_handle_guest_sea(vcpu);
>
> - if (esr_fsc_is_permission_fault(esr)) {
> - handle_vncr_perm(vcpu);
> - } else if (esr_fsc_is_translation_fault(esr)) {
> - bool valid, is_gmem = false;
> - int ret;
> -
> - scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
> - valid = kvm_vncr_tlb_lookup(vcpu);
> -
> - if (!valid)
> - ret = kvm_translate_vncr(vcpu, &is_gmem);
> - else
> - ret = -EPERM;
> + if (!esr_fsc_is_translation_fault(esr) && !esr_fsc_is_permission_fault(esr)) {
> + WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr);
> + return 1;
> + }
>
> - switch (ret) {
> - case -EAGAIN:
> - /* Let's try again... */
> - break;
> - case -ENOMEM:
> - /*
> - * For guest_memfd, this indicates that it failed to
> - * create a folio to back the memory. Inform userspace.
> - */
> - if (is_gmem)
> - return 0;
> - /* Otherwise, let's try again... */
> - break;
> - case -EFAULT:
> - case -EIO:
> - case -EHWPOISON:
> - if (is_gmem)
> - return 0;
> - fallthrough;
> - case -EINVAL:
> - case -ENOENT:
> - case -EACCES:
> - /*
> - * Translation failed, inject the corresponding
> - * exception back to EL2.
> - */
> - BUG_ON(!vt->wr.failed);
> + ret = kvm_translate_vncr(vcpu, &is_gmem);
> + switch (ret) {
> + case -EAGAIN:
> + /* Let's try again... */
> + break;
> + case -ENOMEM:
> + /*
> + * For guest_memfd, this indicates that it failed to
> + * create a folio to back the memory. Inform userspace.
> + */
> + if (is_gmem)
> + return 0;
> + /* Otherwise, let's try again... */
> + break;
> + case -EFAULT:
> + case -EIO:
> + case -EHWPOISON:
> + if (is_gmem)
> + return 0;
> + fallthrough;
> + case -EINVAL:
> + case -ENOENT:
> + case -EACCES:
> + /*
> + * Translation failed, inject the corresponding
> + * exception back to EL2.
> + */
> + BUG_ON(!vt->wr.failed);
Maybe we can lose this BUG_ON(). We rely on it being correct anyway.
>
> - esr &= ~ESR_ELx_FSC;
> - esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
> + esr &= ~ESR_ELx_FSC;
> + esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
>
> - kvm_inject_nested_sync(vcpu, esr);
> - break;
> - case -EPERM:
> - /* Hack to deal with POE until we get kernel support */
> - handle_vncr_perm(vcpu);
> - break;
> - case 0:
> - break;
> - }
> - } else {
> - WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr);
> + kvm_inject_nested_sync(vcpu, esr);
> + break;
> + case 0:
> + break;
> }
>
> + perm = kvm_is_write_fault(vcpu) ? vt->wr.pw && vt->hpa_writable : vt->wr.pr;
> + if (!perm)
> + handle_vncr_perm(vcpu);
> +
Isn't there a problem here, where anything that doesn't perform an
early return ends up evaluating the permissions, even if the
translation has failed? My hunch is that you'd want something like
this:
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 736fdd6e99cdc..0cefc73d97199 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1460,13 +1460,12 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
kvm_inject_nested_sync(vcpu, esr);
break;
case 0:
+ perm = kvm_is_write_fault(vcpu) ? vt->wr.pw && vt->hpa_writable : vt->wr.pr;
+ if (!perm)
+ handle_vncr_perm(vcpu);
break;
}
- perm = kvm_is_write_fault(vcpu) ? vt->wr.pw && vt->hpa_writable : vt->wr.pr;
- if (!perm)
- handle_vncr_perm(vcpu);
-
return 1;
}
Thoughts?
M.
--
Without deviation from the norm, progress is not possible.
next prev parent reply other threads:[~2026-06-10 12:42 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-09 18:55 [PATCH RESEND v2 0/5] KVM: arm64: nv: Even more VNCR fixes Oliver Upton
2026-06-09 18:55 ` [PATCH RESEND v2 1/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
2026-06-09 18:55 ` [PATCH RESEND v2 2/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Oliver Upton
2026-06-09 18:55 ` [PATCH RESEND v2 3/5] KVM: arm64: nv: Re-translate VNCR before injecting abort Oliver Upton
2026-06-10 12:42 ` Marc Zyngier [this message]
2026-06-10 13:46 ` Wei-Lin Chang
2026-06-09 18:55 ` [PATCH RESEND v2 4/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory Oliver Upton
2026-06-09 18:55 ` [PATCH RESEND v2 5/5] KVM: arm64: nv: Mark VM as bugged for unexpected VNCR abort Oliver Upton
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=867bo6tvlx.wl-maz@kernel.org \
--to=maz@kernel.org \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=oupton@kernel.org \
--cc=sashiko-bot@kernel.org \
--cc=stable@vger.kernel.org \
--cc=suzuki.poulose@arm.com \
--cc=weilin.chang@arm.com \
--cc=yuzenghui@huawei.com \
/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