* [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes
@ 2026-06-09 18:51 Oliver Upton
2026-06-09 18:51 ` [PATCH 1/2] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:51 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton
Another day, another handful of ugly MMU bugs.
The first addresses an issue where the host stage-1 VNCR mapping only
relies on the guest stage-1 permissions, potentially allowing RW
accesses to an RO PFN.
The second avoids a BUG_ON() in the case that the output of stage-1
translation exists outside of a memslot, and instead does the usual
thing and injects an SEA.
Based on kvmarm/next since the first pile of MMU changes was taken
(thanks!)
Oliver Upton (2):
KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR
KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN
arch/arm64/include/asm/kvm_nested.h | 8 ++++++
arch/arm64/kvm/at.c | 8 ------
arch/arm64/kvm/nested.c | 40 +++++++++++++++++++++--------
3 files changed, 38 insertions(+), 18 deletions(-)
base-commit: 406f0c31f47877db036e885f15830106b89ca950
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
@ 2026-06-09 18:51 ` Oliver Upton
2026-06-09 18:51 ` [PATCH 2/2] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Oliver Upton
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:51 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton, stable
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>
---
arch/arm64/kvm/nested.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 257813289c76..cdbdf47fa6a2 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;
@@ -1395,7 +1396,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);
@@ -1404,6 +1405,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) {
@@ -1414,6 +1417,7 @@ 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;
@@ -1421,21 +1425,33 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw);
}
- 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);
}
@@ -1469,7 +1485,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;
@@ -1517,7 +1533,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;
@@ -1561,7 +1577,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] 9+ messages in thread
* [PATCH 2/2] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
2026-06-09 18:51 ` [PATCH 1/2] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
@ 2026-06-09 18:51 ` Oliver Upton
2026-06-09 18:51 ` [PATCH v2 0/5] KVM: arm64: nv: Even more VNCR fixes Oliver Upton
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:51 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton, stable
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().
Cc: stable@vger.kernel.org
Fixes: b55d3bd1e0b7 ("KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN")
Signed-off-by: Oliver Upton <oupton@kernel.org>
---
arch/arm64/include/asm/kvm_nested.h | 8 ++++++++
arch/arm64/kvm/at.c | 8 --------
arch/arm64/kvm/nested.c | 8 ++++++--
3 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index dc2957662ff2..cbdaaa2a2903 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h
@@ -388,6 +388,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 30e6fa8ac07c..8263c648207b 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -11,14 +11,6 @@
#include <asm/kvm_mmu.h>
#include <asm/lsui.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 cdbdf47fa6a2..940247b3530b 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1389,15 +1389,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) {
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 0/5] KVM: arm64: nv: Even more VNCR fixes
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
2026-06-09 18:51 ` [PATCH 1/2] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
2026-06-09 18:51 ` [PATCH 2/2] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Oliver Upton
@ 2026-06-09 18:51 ` Oliver Upton
2026-06-09 18:52 ` [PATCH v2 1/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:51 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton
Yes, even more.
On top of the first pile:
- KVM needs to fault in the PFN for write before mapping the L1 VNCR
with write permissions in the host stage-1
- Hack to prevent KVM from mapping a non-memory PFN with cacheable
mappings. This doesn't align perfectly with the semantics at stage-2,
boo hoo.
- Bonus change to break VNCR abort loops in case by bugging the VM if
KVM doesn't know how to handle the fault
Tested by running an L3 VM with kvmarm/next
v1: https://lore.kernel.org/kvmarm/20260608161446.718957-1-oupton@kernel.org/
Oliver Upton (5):
KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR
KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN
KVM: arm64: nv: Re-translate VNCR before injecting abort
KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory
KVM: arm64: nv: Mark VM as bugged for unexpected VNCR abort
arch/arm64/include/asm/kvm_nested.h | 8 ++
arch/arm64/kvm/at.c | 8 --
arch/arm64/kvm/nested.c | 164 ++++++++++++++--------------
3 files changed, 92 insertions(+), 88 deletions(-)
base-commit: 406f0c31f47877db036e885f15830106b89ca950
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
` (2 preceding siblings ...)
2026-06-09 18:51 ` [PATCH v2 0/5] KVM: arm64: nv: Even more VNCR fixes Oliver Upton
@ 2026-06-09 18:52 ` Oliver Upton
2026-06-09 18:52 ` [PATCH v2 2/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Oliver Upton
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:52 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton, stable
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>
---
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 257813289c76..84b3bd528e11 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;
@@ -1395,7 +1396,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);
@@ -1404,6 +1405,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) {
@@ -1414,28 +1417,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);
}
@@ -1469,7 +1485,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;
@@ -1517,7 +1533,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;
@@ -1561,7 +1577,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] 9+ messages in thread
* [PATCH v2 2/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
` (3 preceding siblings ...)
2026-06-09 18:52 ` [PATCH v2 1/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
@ 2026-06-09 18:52 ` Oliver Upton
2026-06-09 18:52 ` [PATCH v2 3/5] KVM: arm64: nv: Re-translate VNCR before injecting abort Oliver Upton
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:52 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton, stable
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().
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>
---
arch/arm64/include/asm/kvm_nested.h | 8 ++++++++
arch/arm64/kvm/at.c | 8 --------
arch/arm64/kvm/nested.c | 8 ++++++--
3 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index dc2957662ff2..cbdaaa2a2903 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h
@@ -388,6 +388,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 30e6fa8ac07c..8263c648207b 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -11,14 +11,6 @@
#include <asm/kvm_mmu.h>
#include <asm/lsui.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 84b3bd528e11..ebd7ccfeee99 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1389,15 +1389,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) {
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 3/5] KVM: arm64: nv: Re-translate VNCR before injecting abort
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
` (4 preceding siblings ...)
2026-06-09 18:52 ` [PATCH v2 2/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Oliver Upton
@ 2026-06-09 18:52 ` Oliver Upton
2026-06-09 18:52 ` [PATCH v2 4/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory Oliver Upton
2026-06-09 18:52 ` [PATCH v2 5/5] KVM: arm64: nv: Mark VM as bugged for unexpected VNCR abort Oliver Upton
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:52 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton, stable, Sashiko
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);
- 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);
+
return 1;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 4/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
` (5 preceding siblings ...)
2026-06-09 18:52 ` [PATCH v2 3/5] KVM: arm64: nv: Re-translate VNCR before injecting abort Oliver Upton
@ 2026-06-09 18:52 ` Oliver Upton
2026-06-09 18:52 ` [PATCH v2 5/5] KVM: arm64: nv: Mark VM as bugged for unexpected VNCR abort Oliver Upton
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:52 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton, stable
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>
---
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 d5c4b57123a9..a6bd60856fc3 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1413,6 +1413,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 -EFAULT;
+ }
+
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] 9+ messages in thread
* [PATCH v2 5/5] KVM: arm64: nv: Mark VM as bugged for unexpected VNCR abort
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
` (6 preceding siblings ...)
2026-06-09 18:52 ` [PATCH v2 4/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory Oliver Upton
@ 2026-06-09 18:52 ` Oliver Upton
7 siblings, 0 replies; 9+ messages in thread
From: Oliver Upton @ 2026-06-09 18:52 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Wei-Lin Chang, Oliver Upton
KVM is unlikely to resolve an unexpected VNCR abort, meaning that
returning to the guest will likely leave the vCPU stuck in an abort
loop. Bug the VM and exit to userspace instead.
Signed-off-by: Oliver Upton <oupton@kernel.org>
---
arch/arm64/kvm/nested.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index a6bd60856fc3..e9bd4991f7a0 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1484,8 +1484,8 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
return kvm_handle_guest_sea(vcpu);
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;
+ KVM_BUG(1, vcpu->kvm, "Unhandled VNCR abort, ESR=%llx\n", esr);
+ return -EIO;
}
ret = kvm_translate_vncr(vcpu, &is_gmem);
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-06-09 18:52 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-09 18:51 [PATCH 0/2] KVM: arm64: nv: A couple more VNCR fixes Oliver Upton
2026-06-09 18:51 ` [PATCH 1/2] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
2026-06-09 18:51 ` [PATCH 2/2] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Oliver Upton
2026-06-09 18:51 ` [PATCH v2 0/5] KVM: arm64: nv: Even more VNCR fixes Oliver Upton
2026-06-09 18:52 ` [PATCH v2 1/5] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR Oliver Upton
2026-06-09 18:52 ` [PATCH v2 2/5] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN Oliver Upton
2026-06-09 18:52 ` [PATCH v2 3/5] KVM: arm64: nv: Re-translate VNCR before injecting abort Oliver Upton
2026-06-09 18:52 ` [PATCH v2 4/5] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory Oliver Upton
2026-06-09 18:52 ` [PATCH v2 5/5] KVM: arm64: nv: Mark VM as bugged for unexpected VNCR abort Oliver Upton
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.