* FAILED: patch "[PATCH] KVM: arm64: nv: Re-translate VNCR before injecting abort" failed to apply to 6.18-stable tree
@ 2026-07-20 12:22 gregkh
2026-07-21 9:58 ` [PATCH 6.18.y 1/5] KVM: arm64: Deduplicate ASID retrieval code Marc Zyngier
0 siblings, 1 reply; 6+ messages in thread
From: gregkh @ 2026-07-20 12:22 UTC (permalink / raw)
To: oupton, maz, sashiko-bot; +Cc: stable
The patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026072033-frigidly-corncob-3b28@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f Mon Sep 17 00:00:00 2001
From: Oliver Upton <oupton@kernel.org>
Date: Thu, 18 Jun 2026 16:42:04 -0700
Subject: [PATCH] KVM: arm64: nv: Re-translate VNCR before injecting abort
KVM faults in the VNCR page with FOLL_WRITE whenever 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>
Link: https://patch.msgid.link/20260618234207.1063941-4-oupton@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 53dea9c3f14f..7fffd86eee94 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1466,88 +1466,61 @@ 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 = false;
+ bool 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;
-
- 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.
- */
- 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 {
+ 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;
+ }
+
+ ret = kvm_translate_vncr(vcpu, &is_gmem);
+ switch (ret) {
+ case -EAGAIN:
+ /* Let's try again... */
+ return 1;
+ 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.
+ */
+ esr &= ~ESR_ELx_FSC;
+ esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
+
+ 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;
}
return 1;
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 6.18.y 1/5] KVM: arm64: Deduplicate ASID retrieval code
2026-07-20 12:22 FAILED: patch "[PATCH] KVM: arm64: nv: Re-translate VNCR before injecting abort" failed to apply to 6.18-stable tree gregkh
@ 2026-07-21 9:58 ` Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 2/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Marc Zyngier
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Marc Zyngier @ 2026-07-21 9:58 UTC (permalink / raw)
To: stable; +Cc: Joey Gouly, Jonathan Cameron
We currently have three versions of the ASID retrieval code, one
in the S1 walker, and two in the VNCR handling (although the last
two are limited to the EL2&0 translation regime).
Make this code common, and take this opportunity to also simplify
the code a bit while switching over to the TTBRx_EL1_ASID macro.
Reviewed-by: Joey Gouly <joey.gouly@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20260225104718.14209-1-maz@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
(cherry picked from commit 54e367cb94d6bef941bbc1132d9959dc73bd4b6f)
[maz: cherry-pick as dependency of bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f]
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/include/asm/kvm_nested.h | 2 +
arch/arm64/kvm/at.c | 27 +------------
arch/arm64/kvm/nested.c | 60 +++++++++++++++--------------
3 files changed, 35 insertions(+), 54 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index f7c06a840963d..4d7df3aa7dde6 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h
@@ -363,6 +363,8 @@ int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu);
int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu);
void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val);
+u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime);
+
#define vncr_fixmap(c) \
({ \
u32 __c = (c); \
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index e6de6aac6ede2..7a3d07ee6a76c 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -491,31 +491,8 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
wr->pa |= va & GENMASK_ULL(va_bottom - 1, 0);
wr->nG = (wi->regime != TR_EL2) && (desc & PTE_NG);
- if (wr->nG) {
- u64 asid_ttbr, tcr;
-
- switch (wi->regime) {
- case TR_EL10:
- tcr = vcpu_read_sys_reg(vcpu, TCR_EL1);
- asid_ttbr = ((tcr & TCR_A1) ?
- vcpu_read_sys_reg(vcpu, TTBR1_EL1) :
- vcpu_read_sys_reg(vcpu, TTBR0_EL1));
- break;
- case TR_EL20:
- tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
- asid_ttbr = ((tcr & TCR_A1) ?
- vcpu_read_sys_reg(vcpu, TTBR1_EL2) :
- vcpu_read_sys_reg(vcpu, TTBR0_EL2));
- break;
- default:
- BUG();
- }
-
- wr->asid = FIELD_GET(TTBR_ASID_MASK, asid_ttbr);
- if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
- !(tcr & TCR_ASID16))
- wr->asid &= GENMASK(7, 0);
- }
+ if (wr->nG)
+ wr->asid = get_asid_by_regime(vcpu, wi->regime);
return 0;
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index b1f52f82c9ef6..255a69d03584f 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -817,6 +817,33 @@ int kvm_inject_s2_fault(struct kvm_vcpu *vcpu, u64 esr_el2)
return kvm_inject_nested_sync(vcpu, esr_el2);
}
+u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime)
+{
+ enum vcpu_sysreg ttbr_elx;
+ u64 tcr;
+ u16 asid;
+
+ switch (regime) {
+ case TR_EL10:
+ tcr = vcpu_read_sys_reg(vcpu, TCR_EL1);
+ ttbr_elx = (tcr & TCR_A1) ? TTBR1_EL1 : TTBR0_EL1;
+ break;
+ case TR_EL20:
+ tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
+ ttbr_elx = (tcr & TCR_A1) ? TTBR1_EL2 : TTBR0_EL2;
+ break;
+ default:
+ BUG();
+ }
+
+ asid = FIELD_GET(TTBRx_EL1_ASID, vcpu_read_sys_reg(vcpu, ttbr_elx));
+ if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
+ !(tcr & TCR_ASID16))
+ asid &= GENMASK(7, 0);
+
+ return asid;
+}
+
static void invalidate_vncr(struct vncr_tlb *vt)
{
vt->valid = false;
@@ -1283,20 +1310,8 @@ static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu)
if (read_vncr_el2(vcpu) != vt->gva)
return false;
- if (vt->wr.nG) {
- u64 tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
- u64 ttbr = ((tcr & TCR_A1) ?
- vcpu_read_sys_reg(vcpu, TTBR1_EL2) :
- vcpu_read_sys_reg(vcpu, TTBR0_EL2));
- u16 asid;
-
- asid = FIELD_GET(TTBR_ASID_MASK, ttbr);
- if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
- !(tcr & TCR_ASID16))
- asid &= GENMASK(7, 0);
-
- return asid == vt->wr.asid;
- }
+ if (vt->wr.nG)
+ return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid;
return true;
}
@@ -1399,21 +1414,8 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
if (read_vncr_el2(vcpu) != vt->gva)
return;
- if (vt->wr.nG) {
- u64 tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
- u64 ttbr = ((tcr & TCR_A1) ?
- vcpu_read_sys_reg(vcpu, TTBR1_EL2) :
- vcpu_read_sys_reg(vcpu, TTBR0_EL2));
- u16 asid;
-
- asid = FIELD_GET(TTBR_ASID_MASK, ttbr);
- if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
- !(tcr & TCR_ASID16))
- asid &= GENMASK(7, 0);
-
- if (asid != vt->wr.asid)
- return;
- }
+ if (vt->wr.nG && get_asid_by_regime(vcpu, TR_EL20) != vt->wr.asid)
+ return;
vt->cpu = smp_processor_id();
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 6.18.y 2/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR
2026-07-21 9:58 ` [PATCH 6.18.y 1/5] KVM: arm64: Deduplicate ASID retrieval code Marc Zyngier
@ 2026-07-21 9:58 ` Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 3/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Marc Zyngier
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Marc Zyngier @ 2026-07-21 9:58 UTC (permalink / raw)
To: stable; +Cc: Joey Gouly, Jonathan Cameron, Oliver Upton
From: Oliver Upton <oupton@kernel.org>
KVM currently maps the L1 VNCR into the host stage-1 by relying entirely
on the permissions of the guest stage-1. At the same time, it is
entirely possible that the backing PFN is read-only (e.g. RO memslot),
meaning that the L1 VNCR should use at most a read-only mapping.
Cache the writability of the PFN in the VNCR TLB and use it to constrain
the resulting fixmap permissions. Promote VNCR permission faults to an
SEA in the case where the guest attempts to write to a read-only
endpoint. Conveniently, this also plugs a page leak found by Sashiko [*]
resulting from the early return for a read-only PFN.
Cc: stable@vger.kernel.org
Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2")
Link: https://lore.kernel.org/kvm/20260608082603.16AEC1F00893@smtp.kernel.org/
Signed-off-by: Oliver Upton <oupton@kernel.org>
Link: https://patch.msgid.link/20260618234207.1063941-2-oupton@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
(cherry picked from commit 2684e02bac41c5220f6c1ab2bdcc957b71812977)
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/nested.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 255a69d03584f..7aabddfd895ef 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -24,6 +24,7 @@ struct vncr_tlb {
struct s1_walk_result wr;
u64 hpa;
+ bool hpa_writable;
/* -1 when not mapped on a CPU */
int cpu;
@@ -1253,7 +1254,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
if (!*is_gmem) {
pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0,
&writable, &page);
- if (is_error_noslot_pfn(pfn) || (write_fault && !writable))
+ if (is_error_noslot_pfn(pfn))
return -EFAULT;
} else {
ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL);
@@ -1262,6 +1263,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
write_fault, false, false);
return ret;
}
+
+ writable = !(memslot->flags & KVM_MEM_READONLY);
}
scoped_guard(write_lock, &vcpu->kvm->mmu_lock) {
@@ -1272,28 +1275,41 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
vt->gva = va;
vt->hpa = pfn << PAGE_SHIFT;
+ vt->hpa_writable = writable;
vt->valid = true;
vt->cpu = -1;
kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu);
- kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw);
+ kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable);
}
- if (vt->wr.pw)
+ if (vt->wr.pw && vt->hpa_writable)
mark_page_dirty(vcpu->kvm, gfn);
return 0;
}
-static void inject_vncr_perm(struct kvm_vcpu *vcpu)
+static void handle_vncr_perm(struct kvm_vcpu *vcpu)
{
struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
u64 esr = kvm_vcpu_get_esr(vcpu);
+ u64 fsc;
+
+ /*
+ * Promote to an external abort if the stage-1 permits writes but the
+ * HPA is read-only (e.g. RO memslot).
+ */
+ if (kvm_is_write_fault(vcpu) && vt->wr.pw && !vt->hpa_writable)
+ fsc = ESR_ELx_FSC_EXTABT;
+ /*
+ * Otherwise, inject a permission fault using the guest's translation
+ * level rather than the host's.
+ */
+ else
+ fsc = ESR_ELx_FSC_PERM_L(vt->wr.level);
- /* Adjust the fault level to reflect that of the guest's */
esr &= ~ESR_ELx_FSC;
- esr |= FIELD_PREP(ESR_ELx_FSC,
- ESR_ELx_FSC_PERM_L(vt->wr.level));
+ esr |= FIELD_PREP(ESR_ELx_FSC, fsc);
kvm_inject_nested_sync(vcpu, esr);
}
@@ -1327,7 +1343,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
return kvm_handle_guest_sea(vcpu);
if (esr_fsc_is_permission_fault(esr)) {
- inject_vncr_perm(vcpu);
+ handle_vncr_perm(vcpu);
} else if (esr_fsc_is_translation_fault(esr)) {
bool valid, is_gmem = false;
int ret;
@@ -1375,7 +1391,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
break;
case -EPERM:
/* Hack to deal with POE until we get kernel support */
- inject_vncr_perm(vcpu);
+ handle_vncr_perm(vcpu);
break;
case 0:
break;
@@ -1419,7 +1435,7 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
vt->cpu = smp_processor_id();
- if (vt->wr.pw && vt->wr.pr)
+ if (vt->hpa_writable && vt->wr.pw && vt->wr.pr)
prot = PAGE_KERNEL;
else if (vt->wr.pr)
prot = PAGE_KERNEL_RO;
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 6.18.y 3/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN
2026-07-21 9:58 ` [PATCH 6.18.y 1/5] KVM: arm64: Deduplicate ASID retrieval code Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 2/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Marc Zyngier
@ 2026-07-21 9:58 ` Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 4/5] KVM: arm64: nv: Re-translate VNCR before injecting abort Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 5/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory Marc Zyngier
3 siblings, 0 replies; 6+ messages in thread
From: Marc Zyngier @ 2026-07-21 9:58 UTC (permalink / raw)
To: stable; +Cc: Joey Gouly, Jonathan Cameron, Oliver Upton
From: Oliver Upton <oupton@kernel.org>
kvm_handle_vncr_abort() assumes that s1_walk_result conveys an abort
when kvm_translate_vncr() returns -EFAULT. This is not always the case
as it's possible to encounter 'late' failures on the output of S1
translation, e.g. a GFN outside of the memslots.
Fix it by preparing an external abort before returning from
kvm_translate_vncr(). Get rid of the BUG_ON() in the fault injection
path while at it.
Cc: stable@vger.kernel.org
Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2")
Signed-off-by: Oliver Upton <oupton@kernel.org>
Link: https://patch.msgid.link/20260618234207.1063941-3-oupton@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
(cherry picked from commit 9f3e83345a56280efffe235c65593c7e544c0fcc)
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/include/asm/kvm_nested.h | 8 ++++++++
arch/arm64/kvm/at.c | 8 --------
arch/arm64/kvm/nested.c | 10 ++++++----
3 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index 4d7df3aa7dde6..6f768e361c894 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h
@@ -353,6 +353,14 @@ struct s1_walk_result {
bool failed;
};
+static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
+{
+ wr->fst = fst;
+ wr->ptw = s1ptw;
+ wr->s2 = s1ptw;
+ wr->failed = true;
+}
+
int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
struct s1_walk_result *wr, u64 va);
int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa,
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index 7a3d07ee6a76c..2bdc49bef8613 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -10,14 +10,6 @@
#include <asm/kvm_hyp.h>
#include <asm/kvm_mmu.h>
-static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
-{
- wr->fst = fst;
- wr->ptw = s1ptw;
- wr->s2 = s1ptw;
- wr->failed = true;
-}
-
#define S1_MMU_DISABLED (-127)
static int get_ia_size(struct s1_walk_info *wi)
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 7aabddfd895ef..ece0fd268ecc2 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1247,15 +1247,19 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
gfn = vt->wr.pa >> PAGE_SHIFT;
memslot = gfn_to_memslot(vcpu->kvm, gfn);
- if (!memslot)
+ if (!memslot) {
+ fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false);
return -EFAULT;
+ }
*is_gmem = kvm_slot_has_gmem(memslot);
if (!*is_gmem) {
pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0,
&writable, &page);
- if (is_error_noslot_pfn(pfn))
+ if (is_error_noslot_pfn(pfn)) {
+ fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false);
return -EFAULT;
+ }
} else {
ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL);
if (ret) {
@@ -1382,8 +1386,6 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
* Translation failed, inject the corresponding
* exception back to EL2.
*/
- BUG_ON(!vt->wr.failed);
-
esr &= ~ESR_ELx_FSC;
esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 6.18.y 4/5] KVM: arm64: nv: Re-translate VNCR before injecting abort
2026-07-21 9:58 ` [PATCH 6.18.y 1/5] KVM: arm64: Deduplicate ASID retrieval code Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 2/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 3/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Marc Zyngier
@ 2026-07-21 9:58 ` Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 5/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory Marc Zyngier
3 siblings, 0 replies; 6+ messages in thread
From: Marc Zyngier @ 2026-07-21 9:58 UTC (permalink / raw)
To: stable; +Cc: Joey Gouly, Jonathan Cameron, Oliver Upton, Sashiko
From: Oliver Upton <oupton@kernel.org>
KVM faults in the VNCR page with FOLL_WRITE whenever 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>
Link: https://patch.msgid.link/20260618234207.1063941-4-oupton@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
(cherry picked from commit bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f)
Signed-off-by: Marc Zyngier <maz@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 ece0fd268ecc2..0e418975649f2 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1318,88 +1318,61 @@ 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 = false;
+ bool 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;
-
- 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.
- */
- 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 {
+ 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;
+ }
+
+ ret = kvm_translate_vncr(vcpu, &is_gmem);
+ switch (ret) {
+ case -EAGAIN:
+ /* Let's try again... */
+ return 1;
+ 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.
+ */
+ esr &= ~ESR_ELx_FSC;
+ esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
+
+ 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;
}
return 1;
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 6.18.y 5/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory
2026-07-21 9:58 ` [PATCH 6.18.y 1/5] KVM: arm64: Deduplicate ASID retrieval code Marc Zyngier
` (2 preceding siblings ...)
2026-07-21 9:58 ` [PATCH 6.18.y 4/5] KVM: arm64: nv: Re-translate VNCR before injecting abort Marc Zyngier
@ 2026-07-21 9:58 ` Marc Zyngier
3 siblings, 0 replies; 6+ messages in thread
From: Marc Zyngier @ 2026-07-21 9:58 UTC (permalink / raw)
To: stable; +Cc: Joey Gouly, Jonathan Cameron, Oliver Upton
From: Oliver Upton <oupton@kernel.org>
When constructing an L1 VNCR mapping, KVM unconditionally uses cacheable
memory attributes, even if the underlying PFN isn't memory. This gets
particularly hairy if the endpoint doesn't support cacheable memory
attributes, potentially throwing an SError on writeback...
While KVM does permit cacheable memory attributes on certain PFNMAP
VMAs, kvm_translate_vncr() isn't currently grabbing the VMA. So do the
simpler thing for now and just reject everything that isn't memory.
Cc: stable@vger.kernel.org
Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2")
Signed-off-by: Oliver Upton <oupton@kernel.org>
Link: https://patch.msgid.link/20260618234207.1063941-5-oupton@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
(cherry picked from commit 4bd7dbe0b2243e6aa735cae4d5e1ff988b30b2a6)
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/nested.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 0e418975649f2..10da38dff8b91 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1271,6 +1271,17 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
writable = !(memslot->flags & KVM_MEM_READONLY);
}
+ /*
+ * FIXME: This check is too restrictive as KVM allows cacheable memory
+ * attributes for PFNMAP VMAs that have cacheable attributes in host
+ * stage-1.
+ */
+ if (!pfn_is_map_memory(pfn)) {
+ kvm_release_faultin_page(vcpu->kvm, page, true, false);
+ fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false);
+ return -EINVAL;
+ }
+
scoped_guard(write_lock, &vcpu->kvm->mmu_lock) {
if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) {
kvm_release_faultin_page(vcpu->kvm, page, true, false);
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-07-21 9:58 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20 12:22 FAILED: patch "[PATCH] KVM: arm64: nv: Re-translate VNCR before injecting abort" failed to apply to 6.18-stable tree gregkh
2026-07-21 9:58 ` [PATCH 6.18.y 1/5] KVM: arm64: Deduplicate ASID retrieval code Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 2/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 3/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 4/5] KVM: arm64: nv: Re-translate VNCR before injecting abort Marc Zyngier
2026-07-21 9:58 ` [PATCH 6.18.y 5/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory Marc Zyngier
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox