* [PATCH 0/4] KVM: arm64: nv: Fixes for external abort exception routing
@ 2025-05-30 23:06 Oliver Upton
2025-05-30 23:06 ` [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs Oliver Upton
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Oliver Upton @ 2025-05-30 23:06 UTC (permalink / raw)
To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Oliver Upton
SEAs and SErrors come with a set of exception routing rules that are
potentially affected by EL2. KVM currently ignores these and just
injects the exceptions into the present context.
This series aims to address that, enlightening some of the injection
plumbing to the presence of vEL2. Additionally, patches 2 and 4 are
minor fixes for bugs that I found along the way.
Oliver Upton (4):
KVM: arm64: nv: Respect exception routing rules for SEAs
KVM: arm64: nv: Ensure Address size faults affect correct ESR
KVM: arm64: nv: Honor SError exception routing / masking
KVM: arm64: Treat vCPU with pending SError as runnable
arch/arm64/include/asm/kvm_emulate.h | 31 ++++++++++--
arch/arm64/include/asm/kvm_host.h | 20 ++++++--
arch/arm64/include/asm/kvm_nested.h | 2 +
arch/arm64/kvm/arm.c | 7 ++-
arch/arm64/kvm/emulate-nested.c | 53 ++++++++++++++++++++
arch/arm64/kvm/guest.c | 36 +++++++------
arch/arm64/kvm/handle_exit.c | 4 +-
arch/arm64/kvm/inject_fault.c | 75 +++++++++++-----------------
arch/arm64/kvm/mmio.c | 6 +--
arch/arm64/kvm/mmu.c | 17 ++-----
arch/arm64/kvm/nested.c | 36 +++++++++++++
11 files changed, 203 insertions(+), 84 deletions(-)
base-commit: 1b85d923ba8c9e6afaf19e26708411adde94fba8
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs
2025-05-30 23:06 [PATCH 0/4] KVM: arm64: nv: Fixes for external abort exception routing Oliver Upton
@ 2025-05-30 23:06 ` Oliver Upton
2025-05-31 16:23 ` Marc Zyngier
2025-05-30 23:06 ` [PATCH 2/4] KVM: arm64: nv: Ensure Address size faults affect correct ESR Oliver Upton
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Oliver Upton @ 2025-05-30 23:06 UTC (permalink / raw)
To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Oliver Upton
Synchronous external aborts are taken to EL2 if ELIsInHost() or
HCR_EL2.TEA=1. Rework the SEA injection plumbing to respect the imposed
routing of the guest hypervisor and opportunistically rephrase things to
make their function a bit more obvious.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/include/asm/kvm_emulate.h | 15 +++++++++--
arch/arm64/kvm/emulate-nested.c | 20 ++++++++++++++
arch/arm64/kvm/guest.c | 8 ++++--
arch/arm64/kvm/inject_fault.c | 40 +++++++++-------------------
arch/arm64/kvm/mmio.c | 6 ++---
arch/arm64/kvm/mmu.c | 15 +++--------
6 files changed, 57 insertions(+), 47 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index bd020fc28aa9..e0fa8a5a2530 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -46,15 +46,26 @@ void kvm_skip_instr32(struct kvm_vcpu *vcpu);
void kvm_inject_undefined(struct kvm_vcpu *vcpu);
void kvm_inject_vabt(struct kvm_vcpu *vcpu);
-void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
-void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
+void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
+int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
void kvm_inject_size_fault(struct kvm_vcpu *vcpu);
+static inline int kvm_inject_sea_dabt(struct kvm_vcpu *vcpu, u64 addr)
+{
+ return kvm_inject_sea(vcpu, false, addr);
+}
+
+static inline int kvm_inject_sea_iabt(struct kvm_vcpu *vcpu, u64 addr)
+{
+ return kvm_inject_sea(vcpu, true, addr);
+}
+
void kvm_vcpu_wfi(struct kvm_vcpu *vcpu);
void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu);
int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2);
int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
+int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu)
{
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 3a384e9660b8..3d2f98fdca2f 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -2816,3 +2816,23 @@ int kvm_inject_nested_irq(struct kvm_vcpu *vcpu)
/* esr_el2 value doesn't matter for exits due to irqs. */
return kvm_inject_nested(vcpu, 0, except_type_irq);
}
+
+int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
+{
+ u64 esr;
+
+ /*
+ * The destination EL is in the same translation regime as the origin;
+ * directly inject the SEA.
+ */
+ if (is_hyp_ctxt(vcpu) || !(__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_TEA)) {
+ __kvm_inject_sea(vcpu, iabt, addr);
+ return 1;
+ }
+
+ esr = FIELD_PREP(ESR_ELx_EC_MASK,
+ iabt ? ESR_ELx_EC_IABT_LOW : ESR_ELx_EC_DABT_LOW);
+ esr |= ESR_ELx_FSC_EXTABT | ESR_ELx_IL;
+
+ return kvm_inject_s2_fault(vcpu, esr);
+}
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 2196979a24a3..dd5cce0006f3 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -839,6 +839,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
bool serror_pending = events->exception.serror_pending;
bool has_esr = events->exception.serror_has_esr;
bool ext_dabt_pending = events->exception.ext_dabt_pending;
+ int ret;
if (serror_pending && has_esr) {
if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
@@ -852,8 +853,11 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
kvm_inject_vabt(vcpu);
}
- if (ext_dabt_pending)
- kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
+ if (ext_dabt_pending) {
+ ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index a640e839848e..3e61fa0a721b 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -155,36 +155,23 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr)
vcpu_write_sys_reg(vcpu, far, FAR_EL1);
}
-/**
- * kvm_inject_dabt - inject a data abort into the guest
- * @vcpu: The VCPU to receive the data abort
- * @addr: The address to report in the DFAR
- *
- * It is assumed that this code is called from the VCPU thread and that the
- * VCPU therefore is not currently executing guest code.
- */
-void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr)
+void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
{
if (vcpu_el1_is_32bit(vcpu))
- inject_abt32(vcpu, false, addr);
+ inject_abt32(vcpu, iabt, addr);
else
- inject_abt64(vcpu, false, addr);
+ inject_abt64(vcpu, iabt, addr);
}
-/**
- * kvm_inject_pabt - inject a prefetch abort into the guest
- * @vcpu: The VCPU to receive the prefetch abort
- * @addr: The address to report in the DFAR
- *
- * It is assumed that this code is called from the VCPU thread and that the
- * VCPU therefore is not currently executing guest code.
- */
-void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr)
+int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
{
- if (vcpu_el1_is_32bit(vcpu))
- inject_abt32(vcpu, true, addr);
- else
- inject_abt64(vcpu, true, addr);
+ lockdep_assert_held(&vcpu->mutex);
+
+ if (vcpu_has_nv(vcpu))
+ return kvm_inject_nested_sea(vcpu, iabt, addr);
+
+ __kvm_inject_sea(vcpu, iabt, addr);
+ return 1;
}
void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
@@ -194,10 +181,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
addr = kvm_vcpu_get_fault_ipa(vcpu);
addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
- if (kvm_vcpu_trap_is_iabt(vcpu))
- kvm_inject_pabt(vcpu, addr);
- else
- kvm_inject_dabt(vcpu, addr);
+ __kvm_inject_sea(vcpu, kvm_vcpu_trap_is_iabt(vcpu), addr);
/*
* If AArch64 or LPAE, set FSC to 0 to indicate an Address
diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c
index ab365e839874..573a6ade2f4e 100644
--- a/arch/arm64/kvm/mmio.c
+++ b/arch/arm64/kvm/mmio.c
@@ -169,10 +169,8 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
trace_kvm_mmio_nisv(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
kvm_vcpu_get_hfar(vcpu), fault_ipa);
- if (vcpu_is_protected(vcpu)) {
- kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
- return 1;
- }
+ if (vcpu_is_protected(vcpu))
+ return kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
&vcpu->kvm->arch.flags)) {
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index e445db2cb4a4..0e0d51d7ab85 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1833,11 +1833,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
if (fault_ipa >= BIT_ULL(VTCR_EL2_IPA(vcpu->arch.hw_mmu->vtcr))) {
fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
- if (is_iabt)
- kvm_inject_pabt(vcpu, fault_ipa);
- else
- kvm_inject_dabt(vcpu, fault_ipa);
- return 1;
+ return kvm_inject_sea(vcpu, is_iabt, fault_ipa);
}
}
@@ -1909,8 +1905,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
}
if (kvm_vcpu_abt_iss1tw(vcpu)) {
- kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
- ret = 1;
+ ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
goto out_unlock;
}
@@ -1955,10 +1950,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
if (ret == 0)
ret = 1;
out:
- if (ret == -ENOEXEC) {
- kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu));
- ret = 1;
- }
+ if (ret == -ENOEXEC)
+ ret = kvm_inject_sea_iabt(vcpu, kvm_vcpu_get_hfar(vcpu));
out_unlock:
srcu_read_unlock(&vcpu->kvm->srcu, idx);
return ret;
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/4] KVM: arm64: nv: Ensure Address size faults affect correct ESR
2025-05-30 23:06 [PATCH 0/4] KVM: arm64: nv: Fixes for external abort exception routing Oliver Upton
2025-05-30 23:06 ` [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs Oliver Upton
@ 2025-05-30 23:06 ` Oliver Upton
2025-05-30 23:06 ` [PATCH 3/4] KVM: arm64: nv: Honor SError exception routing / masking Oliver Upton
2025-05-30 23:06 ` [PATCH 4/4] KVM: arm64: Treat vCPU with pending SError as runnable Oliver Upton
3 siblings, 0 replies; 7+ messages in thread
From: Oliver Upton @ 2025-05-30 23:06 UTC (permalink / raw)
To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Oliver Upton
For historical reasons, Address size faults are first injected into the
guest as an SEA and ESR_EL1 is subsequently modified to reflect the
correct FSC. Of course, when dealing with a vEL2 this should poke
ESR_EL2.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/inject_fault.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index 3e61fa0a721b..d45424e3e0ff 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -177,6 +177,8 @@ int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
{
unsigned long addr, esr;
+ int esr_elx;
+
addr = kvm_vcpu_get_fault_ipa(vcpu);
addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
@@ -194,9 +196,14 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
!(vcpu_read_sys_reg(vcpu, TCR_EL1) & TTBCR_EAE))
return;
- esr = vcpu_read_sys_reg(vcpu, ESR_EL1);
+ if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL2_SYNC)))
+ esr_elx = ESR_EL2;
+ else
+ esr_elx = ESR_EL1;
+
+ esr = vcpu_read_sys_reg(vcpu, esr_elx);
esr &= ~GENMASK_ULL(5, 0);
- vcpu_write_sys_reg(vcpu, esr, ESR_EL1);
+ vcpu_write_sys_reg(vcpu, esr, esr_elx);
}
/**
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/4] KVM: arm64: nv: Honor SError exception routing / masking
2025-05-30 23:06 [PATCH 0/4] KVM: arm64: nv: Fixes for external abort exception routing Oliver Upton
2025-05-30 23:06 ` [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs Oliver Upton
2025-05-30 23:06 ` [PATCH 2/4] KVM: arm64: nv: Ensure Address size faults affect correct ESR Oliver Upton
@ 2025-05-30 23:06 ` Oliver Upton
2025-05-30 23:06 ` [PATCH 4/4] KVM: arm64: Treat vCPU with pending SError as runnable Oliver Upton
3 siblings, 0 replies; 7+ messages in thread
From: Oliver Upton @ 2025-05-30 23:06 UTC (permalink / raw)
To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Oliver Upton
To date KVM has used HCR_EL2.VSE to track the state of a pending SError
for the guest. With this bit set, hardware respects the EL1 exception
routing / masking rules and injects the vSError when appropriate.
This isn't correct for NV guests as hardware is oblivious to vEL2's
intentions for SErrors. Better yet, with FEAT_NV2 the guest can change
the routing behind our back as HCR_EL2 is redirected to memory. Cope
with this mess by:
- Using a flag (instead of HCR_EL2.VSE) to track the pending SError
state when SErrors are unconditionally masked for the current context
- Resampling the routing / masking of a pending SError on every guest
entry/exit
- Emulating exception entry when SError routing implies a translation
regime change
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/include/asm/kvm_emulate.h | 16 ++++++++++++-
arch/arm64/include/asm/kvm_host.h | 20 +++++++++++++---
arch/arm64/include/asm/kvm_nested.h | 2 ++
arch/arm64/kvm/arm.c | 4 ++++
arch/arm64/kvm/emulate-nested.c | 33 +++++++++++++++++++++++++
arch/arm64/kvm/guest.c | 32 ++++++++++++++-----------
arch/arm64/kvm/handle_exit.c | 4 ++--
arch/arm64/kvm/inject_fault.c | 24 +++++++------------
arch/arm64/kvm/mmu.c | 2 +-
arch/arm64/kvm/nested.c | 36 ++++++++++++++++++++++++++++
10 files changed, 137 insertions(+), 36 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index e0fa8a5a2530..55ac1e88d028 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -45,7 +45,8 @@ bool kvm_condition_valid32(const struct kvm_vcpu *vcpu);
void kvm_skip_instr32(struct kvm_vcpu *vcpu);
void kvm_inject_undefined(struct kvm_vcpu *vcpu);
-void kvm_inject_vabt(struct kvm_vcpu *vcpu);
+void __kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr);
+int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr);
void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
void kvm_inject_size_fault(struct kvm_vcpu *vcpu);
@@ -60,12 +61,25 @@ static inline int kvm_inject_sea_iabt(struct kvm_vcpu *vcpu, u64 addr)
return kvm_inject_sea(vcpu, true, addr);
}
+static inline int kvm_inject_serror(struct kvm_vcpu *vcpu)
+{
+ /*
+ * ESR_ELx.ISV (later renamed to IDS) indicates whether or not
+ * ESR_ELx.ISS contains IMPLEMENTATION DEFINED syndrome information.
+ *
+ * Set the bit when injecting an SError w/o an ESR to indicate ISS
+ * does not follow the architected format.
+ */
+ return kvm_inject_serror_esr(vcpu, ESR_ELx_ISV);
+}
+
void kvm_vcpu_wfi(struct kvm_vcpu *vcpu);
void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu);
int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2);
int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
+int kvm_inject_nested_serror(struct kvm_vcpu *vcpu, u64 esr);
static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu)
{
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index d941abc6b5ee..75f7b6fde8be 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -817,7 +817,7 @@ struct kvm_vcpu_arch {
u8 iflags;
/* State flags for kernel bookkeeping, unused by the hypervisor code */
- u8 sflags;
+ u16 sflags;
/*
* Don't run the guest (internal implementation need).
@@ -953,9 +953,23 @@ struct kvm_vcpu_arch {
__vcpu_flags_preempt_enable(); \
} while (0)
+#define __vcpu_test_and_clear_flag(v, flagset, f, m) \
+ ({ \
+ typeof(v->arch.flagset) set; \
+ \
+ __vcpu_flags_preempt_disable(); \
+ set = __vcpu_get_flag(v, flagset, f, m); \
+ __vcpu_clear_flag(v, flagset, f, m); \
+ __vcpu_flags_preempt_enable(); \
+ \
+ set; \
+ })
+
#define vcpu_get_flag(v, ...) __vcpu_get_flag((v), __VA_ARGS__)
#define vcpu_set_flag(v, ...) __vcpu_set_flag((v), __VA_ARGS__)
#define vcpu_clear_flag(v, ...) __vcpu_clear_flag((v), __VA_ARGS__)
+#define vcpu_test_and_clear_flag(v, ...) \
+ __vcpu_test_and_clear_flag((v), __VA_ARGS__)
/* KVM_ARM_VCPU_INIT completed */
#define VCPU_INITIALIZED __vcpu_single_flag(cflags, BIT(0))
@@ -1015,6 +1029,8 @@ struct kvm_vcpu_arch {
#define IN_WFI __vcpu_single_flag(sflags, BIT(6))
/* KVM is currently emulating a nested ERET */
#define IN_NESTED_ERET __vcpu_single_flag(sflags, BIT(7))
+/* SError pending for nested guest */
+#define NESTED_SERROR_PENDING __vcpu_single_flag(sflags, BIT(8))
/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
@@ -1370,8 +1386,6 @@ static inline bool kvm_arm_is_pvtime_enabled(struct kvm_vcpu_arch *vcpu_arch)
return (vcpu_arch->steal.base != INVALID_GPA);
}
-void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 syndrome);
-
struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr);
DECLARE_KVM_HYP_PER_CPU(struct kvm_host_data, kvm_host_data);
diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h
index 0bd07ea068a1..7fd76f41c296 100644
--- a/arch/arm64/include/asm/kvm_nested.h
+++ b/arch/arm64/include/asm/kvm_nested.h
@@ -80,6 +80,8 @@ extern void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu);
extern void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu);
extern void check_nested_vcpu_requests(struct kvm_vcpu *vcpu);
+extern void kvm_nested_flush_hwstate(struct kvm_vcpu *vcpu);
+extern void kvm_nested_sync_hwstate(struct kvm_vcpu *vcpu);
struct kvm_s2_trans {
phys_addr_t output;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 36cfcffb40d8..5a4aca8082e7 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1187,6 +1187,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
*/
preempt_disable();
+ kvm_nested_flush_hwstate(vcpu);
+
if (kvm_vcpu_has_pmu(vcpu))
kvm_pmu_flush_hwstate(vcpu);
@@ -1286,6 +1288,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
/* Exit types that need handling before we can be preempted */
handle_exit_early(vcpu, ret);
+ kvm_nested_sync_hwstate(vcpu);
+
preempt_enable();
/*
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 3d2f98fdca2f..1039613aabce 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -2836,3 +2836,36 @@ int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
return kvm_inject_s2_fault(vcpu, esr);
}
+
+/*
+ * Injects an SError into a nested guest according to the exception routing /
+ * masking rules in R_NMMXK and R_JFKMF, respectively.
+ */
+int kvm_inject_nested_serror(struct kvm_vcpu *vcpu, u64 esr)
+{
+ bool el2 = __vcpu_sys_reg(vcpu, HCR_EL2) & (HCR_TGE | HCR_AMO);
+
+ /*
+ * SError exception routing does not require a translation regime
+ * change; let hardware deliver the vSError.
+ */
+ if (is_hyp_ctxt(vcpu) == el2) {
+ __kvm_inject_serror_esr(vcpu, esr);
+ return 1;
+ }
+
+ /*
+ * SError exception routing requires a regime change but is not subject
+ * to masking.
+ */
+ if (!is_hyp_ctxt(vcpu) && el2)
+ return kvm_inject_nested(vcpu, esr, except_type_serror);
+
+ /*
+ * SError exception remains pending. Stash the ESR and attempt injection
+ * when the routing / masking changes.
+ */
+ vcpu_set_vsesr(vcpu, esr);
+ vcpu_set_flag(vcpu, NESTED_SERROR_PENDING);
+ return 1;
+}
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index dd5cce0006f3..5af09373daa0 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -818,8 +818,9 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu,
struct kvm_vcpu_events *events)
{
- events->exception.serror_pending = !!(vcpu->arch.hcr_el2 & HCR_VSE);
events->exception.serror_has_esr = cpus_have_final_cap(ARM64_HAS_RAS_EXTN);
+ events->exception.serror_pending = (vcpu->arch.hcr_el2 & HCR_VSE) ||
+ vcpu_get_flag(vcpu, NESTED_SERROR_PENDING);
if (events->exception.serror_pending && events->exception.serror_has_esr)
events->exception.serror_esr = vcpu_get_vsesr(vcpu);
@@ -839,27 +840,30 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
bool serror_pending = events->exception.serror_pending;
bool has_esr = events->exception.serror_has_esr;
bool ext_dabt_pending = events->exception.ext_dabt_pending;
+ u64 esr = events->exception.serror_esr;
int ret;
- if (serror_pending && has_esr) {
- if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
- return -EINVAL;
-
- if (!((events->exception.serror_esr) & ~ESR_ELx_ISS_MASK))
- kvm_set_sei_esr(vcpu, events->exception.serror_esr);
- else
- return -EINVAL;
- } else if (serror_pending) {
- kvm_inject_vabt(vcpu);
- }
-
if (ext_dabt_pending) {
ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
if (ret < 0)
return ret;
}
- return 0;
+ if (!serror_pending)
+ return 0;
+
+ if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && has_esr)
+ return -EINVAL;
+
+ if (has_esr && (esr & ~ESR_ELx_ISS_MASK))
+ return -EINVAL;
+
+ if (has_esr)
+ ret = kvm_inject_serror_esr(vcpu, esr);
+ else
+ ret = kvm_inject_serror(vcpu);
+
+ return (ret < 0) ? ret : 0;
}
u32 __attribute_const__ kvm_target_cpu(void)
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index 453266c96481..145ea1f3aa16 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -32,7 +32,7 @@ typedef int (*exit_handle_fn)(struct kvm_vcpu *);
static void kvm_handle_guest_serror(struct kvm_vcpu *vcpu, u64 esr)
{
if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(NULL, esr))
- kvm_inject_vabt(vcpu);
+ kvm_inject_serror(vcpu);
}
static int handle_hvc(struct kvm_vcpu *vcpu)
@@ -496,7 +496,7 @@ void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index)
kvm_handle_guest_serror(vcpu, disr_to_esr(disr));
} else {
- kvm_inject_vabt(vcpu);
+ kvm_inject_serror(vcpu);
}
return;
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index d45424e3e0ff..46c0d2a91160 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -221,25 +221,19 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu)
inject_undef64(vcpu);
}
-void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 esr)
+void __kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr)
{
vcpu_set_vsesr(vcpu, esr & ESR_ELx_ISS_MASK);
*vcpu_hcr(vcpu) |= HCR_VSE;
}
-/**
- * kvm_inject_vabt - inject an async abort / SError into the guest
- * @vcpu: The VCPU to receive the exception
- *
- * It is assumed that this code is called from the VCPU thread and that the
- * VCPU therefore is not currently executing guest code.
- *
- * Systems with the RAS Extensions specify an imp-def ESR (ISV/IDS = 1) with
- * the remaining ISS all-zeros so that this error is not interpreted as an
- * uncategorized RAS error. Without the RAS Extensions we can't specify an ESR
- * value, so the CPU generates an imp-def value.
- */
-void kvm_inject_vabt(struct kvm_vcpu *vcpu)
+int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr)
{
- kvm_set_sei_esr(vcpu, ESR_ELx_ISV);
+ lockdep_assert_held(&vcpu->mutex);
+
+ if (vcpu_has_nv(vcpu))
+ return kvm_inject_nested_serror(vcpu, esr);
+
+ __kvm_inject_serror_esr(vcpu, esr);
+ return 1;
}
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 0e0d51d7ab85..ec58c5a82377 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1805,7 +1805,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
* There is no need to pass the error into the guest.
*/
if (kvm_handle_guest_sea())
- kvm_inject_vabt(vcpu);
+ return kvm_inject_serror(vcpu);
return 1;
}
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 291dbe38eb5c..67741da8353a 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1780,3 +1780,39 @@ void check_nested_vcpu_requests(struct kvm_vcpu *vcpu)
if (kvm_check_request(KVM_REQ_GUEST_HYP_IRQ_PENDING, vcpu))
kvm_inject_nested_irq(vcpu);
}
+
+/*
+ * One of the many architectural bugs in FEAT_NV2 is that the guest hypervisor
+ * can write to HCR_EL2 behind our back, potentially changing the exception
+ * routing / masking for even the host context.
+ *
+ * What follows is some slop to (1) react to exception routing / masking and (2)
+ * preserve the pending SError state across translation regimes.
+ */
+void kvm_nested_flush_hwstate(struct kvm_vcpu *vcpu)
+{
+ if (!vcpu_has_nv(vcpu))
+ return;
+
+ if (unlikely(vcpu_test_and_clear_flag(vcpu, NESTED_SERROR_PENDING)))
+ kvm_inject_nested_serror(vcpu, vcpu_get_vsesr(vcpu));
+}
+
+void kvm_nested_sync_hwstate(struct kvm_vcpu *vcpu)
+{
+ unsigned long *hcr = vcpu_hcr(vcpu);
+
+ if (!vcpu_has_nv(vcpu))
+ return;
+
+ /*
+ * We previously decided that an SError was deliverable to the guest.
+ * Reap the pending state from HCR_EL2 and...
+ */
+ if (unlikely(__test_and_clear_bit(__ffs(HCR_VSE), hcr)))
+ vcpu_set_flag(vcpu, NESTED_SERROR_PENDING);
+
+ /* Re-attempt SError injection in case the deliverability has changed */
+ if (unlikely(vcpu_test_and_clear_flag(vcpu, NESTED_SERROR_PENDING)))
+ kvm_inject_nested_serror(vcpu, vcpu_get_vsesr(vcpu));
+}
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/4] KVM: arm64: Treat vCPU with pending SError as runnable
2025-05-30 23:06 [PATCH 0/4] KVM: arm64: nv: Fixes for external abort exception routing Oliver Upton
` (2 preceding siblings ...)
2025-05-30 23:06 ` [PATCH 3/4] KVM: arm64: nv: Honor SError exception routing / masking Oliver Upton
@ 2025-05-30 23:06 ` Oliver Upton
3 siblings, 0 replies; 7+ messages in thread
From: Oliver Upton @ 2025-05-30 23:06 UTC (permalink / raw)
To: kvmarm; +Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Oliver Upton
Per R_VRLPB, a pending SError is a WFI wakeup event regardless of
PSTATE.A, meaning that the vCPU is runnable. Sample VSE in addition to
the other IRQ lines.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/arm.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 5a4aca8082e7..c5c1b3ec0f7e 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -740,7 +740,8 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
*/
int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
{
- bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF);
+ bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF | HCR_VSE);
+
return ((irq_lines || kvm_vgic_vcpu_pending_irq(v))
&& !kvm_arm_vcpu_stopped(v) && !v->arch.pause);
}
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs
2025-05-30 23:06 ` [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs Oliver Upton
@ 2025-05-31 16:23 ` Marc Zyngier
2025-05-31 17:51 ` Oliver Upton
0 siblings, 1 reply; 7+ messages in thread
From: Marc Zyngier @ 2025-05-31 16:23 UTC (permalink / raw)
To: Oliver Upton; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Zenghui Yu
On Sat, 31 May 2025 00:06:20 +0100,
Oliver Upton <oliver.upton@linux.dev> wrote:
>
> Synchronous external aborts are taken to EL2 if ELIsInHost() or
> HCR_EL2.TEA=1. Rework the SEA injection plumbing to respect the imposed
> routing of the guest hypervisor and opportunistically rephrase things to
> make their function a bit more obvious.
nit: this is only true when FEAT_RAS is implemented, which isn't the
case so far when NV is enabled.
>
> Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> ---
> arch/arm64/include/asm/kvm_emulate.h | 15 +++++++++--
> arch/arm64/kvm/emulate-nested.c | 20 ++++++++++++++
> arch/arm64/kvm/guest.c | 8 ++++--
> arch/arm64/kvm/inject_fault.c | 40 +++++++++-------------------
> arch/arm64/kvm/mmio.c | 6 ++---
> arch/arm64/kvm/mmu.c | 15 +++--------
> 6 files changed, 57 insertions(+), 47 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index bd020fc28aa9..e0fa8a5a2530 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -46,15 +46,26 @@ void kvm_skip_instr32(struct kvm_vcpu *vcpu);
>
> void kvm_inject_undefined(struct kvm_vcpu *vcpu);
> void kvm_inject_vabt(struct kvm_vcpu *vcpu);
> -void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
> -void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
> +void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
> +int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
> void kvm_inject_size_fault(struct kvm_vcpu *vcpu);
>
> +static inline int kvm_inject_sea_dabt(struct kvm_vcpu *vcpu, u64 addr)
> +{
> + return kvm_inject_sea(vcpu, false, addr);
> +}
> +
> +static inline int kvm_inject_sea_iabt(struct kvm_vcpu *vcpu, u64 addr)
> +{
> + return kvm_inject_sea(vcpu, true, addr);
> +}
> +
> void kvm_vcpu_wfi(struct kvm_vcpu *vcpu);
>
> void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu);
> int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2);
> int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
> +int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr);
>
> static inline void kvm_inject_nested_sve_trap(struct kvm_vcpu *vcpu)
> {
> diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
> index 3a384e9660b8..3d2f98fdca2f 100644
> --- a/arch/arm64/kvm/emulate-nested.c
> +++ b/arch/arm64/kvm/emulate-nested.c
> @@ -2816,3 +2816,23 @@ int kvm_inject_nested_irq(struct kvm_vcpu *vcpu)
> /* esr_el2 value doesn't matter for exits due to irqs. */
> return kvm_inject_nested(vcpu, 0, except_type_irq);
> }
> +
> +int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
> +{
> + u64 esr;
> +
> + /*
> + * The destination EL is in the same translation regime as the origin;
> + * directly inject the SEA.
> + */
> + if (is_hyp_ctxt(vcpu) || !(__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_TEA)) {
> + __kvm_inject_sea(vcpu, iabt, addr);
> + return 1;
> + }
I find this a bit confusing.
Here, we end-up *not* injecting a nested exception, but instead
delivering it in context. I think it would be clearer to move this
condition in kvm_inject_sea(), and then make __kvm_inject_sea()
static.
I guess the confusion also stems from the fact that we tend to lump
two things together:
- exception taken from EL2&0 to EL2
- exception taken from EL1&0 to EL2
I would like to make sure that it is the second case we deal with in
emulated-nested.c, and the first one locally.
At which point, you can end up with something like:
static inline bool is_nested_ctxt(struct kvm_vcpu *vcpu)
{
return vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu);
}
int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
{
lockdep_assert_held(&vcpu->mutex);
if (is_nested_ctxt(vcpu) && (__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_TEA))
return kvm_inject_nested_sea(vcpu, iabt, addr);
__kvm_inject_sea(vcpu, iabt, addr);
return 1;
}
I'll post a separate patch with the is_nested_ctxt() helper, as it
makes things more readable overall.
> +
> + esr = FIELD_PREP(ESR_ELx_EC_MASK,
> + iabt ? ESR_ELx_EC_IABT_LOW : ESR_ELx_EC_DABT_LOW);
> + esr |= ESR_ELx_FSC_EXTABT | ESR_ELx_IL;
> +
> + return kvm_inject_s2_fault(vcpu, esr);
> +}
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 2196979a24a3..dd5cce0006f3 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -839,6 +839,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
> bool serror_pending = events->exception.serror_pending;
> bool has_esr = events->exception.serror_has_esr;
> bool ext_dabt_pending = events->exception.ext_dabt_pending;
> + int ret;
>
> if (serror_pending && has_esr) {
> if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
> @@ -852,8 +853,11 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
> kvm_inject_vabt(vcpu);
> }
>
> - if (ext_dabt_pending)
> - kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> + if (ext_dabt_pending) {
> + ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> + if (ret < 0)
> + return ret;
> + }
>
> return 0;
> }
> diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
> index a640e839848e..3e61fa0a721b 100644
> --- a/arch/arm64/kvm/inject_fault.c
> +++ b/arch/arm64/kvm/inject_fault.c
> @@ -155,36 +155,23 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr)
> vcpu_write_sys_reg(vcpu, far, FAR_EL1);
> }
>
> -/**
> - * kvm_inject_dabt - inject a data abort into the guest
> - * @vcpu: The VCPU to receive the data abort
> - * @addr: The address to report in the DFAR
> - *
> - * It is assumed that this code is called from the VCPU thread and that the
> - * VCPU therefore is not currently executing guest code.
> - */
> -void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr)
> +void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
> {
> if (vcpu_el1_is_32bit(vcpu))
> - inject_abt32(vcpu, false, addr);
> + inject_abt32(vcpu, iabt, addr);
> else
> - inject_abt64(vcpu, false, addr);
> + inject_abt64(vcpu, iabt, addr);
> }
>
> -/**
> - * kvm_inject_pabt - inject a prefetch abort into the guest
> - * @vcpu: The VCPU to receive the prefetch abort
> - * @addr: The address to report in the DFAR
> - *
> - * It is assumed that this code is called from the VCPU thread and that the
> - * VCPU therefore is not currently executing guest code.
> - */
> -void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr)
> +int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
> {
> - if (vcpu_el1_is_32bit(vcpu))
> - inject_abt32(vcpu, true, addr);
> - else
> - inject_abt64(vcpu, true, addr);
> + lockdep_assert_held(&vcpu->mutex);
> +
> + if (vcpu_has_nv(vcpu))
> + return kvm_inject_nested_sea(vcpu, iabt, addr);
> +
> + __kvm_inject_sea(vcpu, iabt, addr);
> + return 1;
> }
>
> void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
> @@ -194,10 +181,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
> addr = kvm_vcpu_get_fault_ipa(vcpu);
> addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
>
> - if (kvm_vcpu_trap_is_iabt(vcpu))
> - kvm_inject_pabt(vcpu, addr);
> - else
> - kvm_inject_dabt(vcpu, addr);
> + __kvm_inject_sea(vcpu, kvm_vcpu_trap_is_iabt(vcpu), addr);
>
> /*
> * If AArch64 or LPAE, set FSC to 0 to indicate an Address
> diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c
> index ab365e839874..573a6ade2f4e 100644
> --- a/arch/arm64/kvm/mmio.c
> +++ b/arch/arm64/kvm/mmio.c
> @@ -169,10 +169,8 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
> trace_kvm_mmio_nisv(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
> kvm_vcpu_get_hfar(vcpu), fault_ipa);
>
> - if (vcpu_is_protected(vcpu)) {
> - kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> - return 1;
> - }
> + if (vcpu_is_protected(vcpu))
> + return kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
>
> if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
> &vcpu->kvm->arch.flags)) {
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index e445db2cb4a4..0e0d51d7ab85 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1833,11 +1833,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> if (fault_ipa >= BIT_ULL(VTCR_EL2_IPA(vcpu->arch.hw_mmu->vtcr))) {
> fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
>
> - if (is_iabt)
> - kvm_inject_pabt(vcpu, fault_ipa);
> - else
> - kvm_inject_dabt(vcpu, fault_ipa);
> - return 1;
> + return kvm_inject_sea(vcpu, is_iabt, fault_ipa);
> }
> }
>
> @@ -1909,8 +1905,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> }
>
> if (kvm_vcpu_abt_iss1tw(vcpu)) {
> - kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> - ret = 1;
> + ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> goto out_unlock;
> }
>
> @@ -1955,10 +1950,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> if (ret == 0)
> ret = 1;
> out:
> - if (ret == -ENOEXEC) {
> - kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> - ret = 1;
> - }
> + if (ret == -ENOEXEC)
> + ret = kvm_inject_sea_iabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> out_unlock:
> srcu_read_unlock(&vcpu->kvm->srcu, idx);
> return ret;
Other than my rambling above, this looks rather good. But there is a
bit more, "thanks" to FEAT_DoubleFault2:
- HCRX_EL2.TMEA also affects this patch, both on the SEA and SError
paths (both can be routed to EL2 when masked).
- SCTLR2_EL{1,2}2.EASE also influence the delivery of the SEA,
upgrading it to a SError (yes, this is the routing from hell and
ties directly into the following patches).
I was expecting to see FEAT_RAS being enabled at some point, but
that's not the case yet. Are you planning to do so?
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs
2025-05-31 16:23 ` Marc Zyngier
@ 2025-05-31 17:51 ` Oliver Upton
0 siblings, 0 replies; 7+ messages in thread
From: Oliver Upton @ 2025-05-31 17:51 UTC (permalink / raw)
To: Marc Zyngier; +Cc: kvmarm, Joey Gouly, Suzuki K Poulose, Zenghui Yu
Hey,
On Sat, May 31, 2025 at 05:23:44PM +0100, Marc Zyngier wrote:
> > +int kvm_inject_nested_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
> > +{
> > + u64 esr;
> > +
> > + /*
> > + * The destination EL is in the same translation regime as the origin;
> > + * directly inject the SEA.
> > + */
> > + if (is_hyp_ctxt(vcpu) || !(__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_TEA)) {
> > + __kvm_inject_sea(vcpu, iabt, addr);
> > + return 1;
> > + }
>
> I find this a bit confusing.
>
> Here, we end-up *not* injecting a nested exception, but instead
> delivering it in context. I think it would be clearer to move this
> condition in kvm_inject_sea(), and then make __kvm_inject_sea()
> static.
So I went about it this way because I didn't like having NV-specific
triage outside of the nested code, though I agree the meaning of this
helper is a bit muddied. The routing is simple now but...
> Other than my rambling above, this looks rather good. But there is a
> bit more, "thanks" to FEAT_DoubleFault2:
>
> - HCRX_EL2.TMEA also affects this patch, both on the SEA and SError
> paths (both can be routed to EL2 when masked).
>
> - SCTLR2_EL{1,2}2.EASE also influence the delivery of the SEA,
> upgrading it to a SError (yes, this is the routing from hell and
> ties directly into the following patches).
This is gonna be an ugly pile to deal with later on. Anyway, I'll pull
apart the routing / emulated exception entry to keep in-context
injection out of NV coed.
> I was expecting to see FEAT_RAS being enabled at some point, but
> that's not the case yet. Are you planning to do so?
Yup, still planning on it. Just wanted to flush out bug fixes on list.
Thanks,
Oliver
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-05-31 17:51 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-30 23:06 [PATCH 0/4] KVM: arm64: nv: Fixes for external abort exception routing Oliver Upton
2025-05-30 23:06 ` [PATCH 1/4] KVM: arm64: nv: Respect exception routing rules for SEAs Oliver Upton
2025-05-31 16:23 ` Marc Zyngier
2025-05-31 17:51 ` Oliver Upton
2025-05-30 23:06 ` [PATCH 2/4] KVM: arm64: nv: Ensure Address size faults affect correct ESR Oliver Upton
2025-05-30 23:06 ` [PATCH 3/4] KVM: arm64: nv: Honor SError exception routing / masking Oliver Upton
2025-05-30 23:06 ` [PATCH 4/4] KVM: arm64: Treat vCPU with pending SError as runnable 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.