Linux KVM/arm64 development list
 help / color / mirror / Atom feed
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.

  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