]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: selftests: Test effective value of HCR_EL2.AMO
authorOliver Upton <oliver.upton@linux.dev>
Fri, 26 Sep 2025 22:44:54 +0000 (15:44 -0700)
committerMarc Zyngier <maz@kernel.org>
Mon, 13 Oct 2025 13:17:03 +0000 (14:17 +0100)
A defect against the architecture now allows an implementation to treat
AMO as 1 when HCR_EL2.{E2H, TGE} = {1, 0}. KVM now takes advantage of
this interpretation to address a quality of emulation issue w.r.t.
SError injection.

Add a corresponding test case and expect a pending SError to be taken.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Marc Zyngier <maz@kernel.org>
tools/testing/selftests/kvm/arm64/external_aborts.c
tools/testing/selftests/kvm/include/arm64/processor.h

index 592b26ded779a8576651d5d04cb4b1fda11b1315..d8fe17a6cc592c0397493f537fb2568af12be7ee 100644 (file)
@@ -359,6 +359,44 @@ static void test_mmio_ease(void)
        kvm_vm_free(vm);
 }
 
+static void test_serror_amo_guest(void)
+{
+       /*
+        * The ISB is entirely unnecessary (and highlights how FEAT_NV2 is borked)
+        * since the write is redirected to memory. But don't write (intentionally)
+        * broken code!
+        */
+       sysreg_clear_set(hcr_el2, HCR_EL2_AMO | HCR_EL2_TGE, 0);
+       isb();
+
+       GUEST_SYNC(0);
+       GUEST_ASSERT(read_sysreg(isr_el1) & ISR_EL1_A);
+
+       /*
+        * KVM treats the effective value of AMO as 1 when
+        * HCR_EL2.{E2H,TGE} = {1, 0}, meaning the SError will be taken when
+        * unmasked.
+        */
+       local_serror_enable();
+       isb();
+       local_serror_disable();
+
+       GUEST_FAIL("Should've taken pending SError exception");
+}
+
+static void test_serror_amo(void)
+{
+       struct kvm_vcpu *vcpu;
+       struct kvm_vm *vm = vm_create_with_dabt_handler(&vcpu, test_serror_amo_guest,
+                                                       unexpected_dabt_handler);
+
+       vm_install_exception_handler(vm, VECTOR_ERROR_CURRENT, expect_serror_handler);
+       vcpu_run_expect_sync(vcpu);
+       vcpu_inject_serror(vcpu);
+       vcpu_run_expect_done(vcpu);
+       kvm_vm_free(vm);
+}
+
 int main(void)
 {
        test_mmio_abort();
@@ -369,4 +407,9 @@ int main(void)
        test_serror_emulated();
        test_mmio_ease();
        test_s1ptw_abort();
+
+       if (!test_supports_el2())
+               return 0;
+
+       test_serror_amo();
 }
index 6f481475c135c57643cae9715d469a0584f85e04..ff928716574df9d7593f32816a354d4c579b4ef7 100644 (file)
@@ -305,7 +305,17 @@ void test_wants_mte(void);
 void test_disable_default_vgic(void);
 
 bool vm_supports_el2(struct kvm_vm *vm);
-static bool vcpu_has_el2(struct kvm_vcpu *vcpu)
+
+static inline bool test_supports_el2(void)
+{
+       struct kvm_vm *vm = vm_create(1);
+       bool supported = vm_supports_el2(vm);
+
+       kvm_vm_free(vm);
+       return supported;
+}
+
+static inline bool vcpu_has_el2(struct kvm_vcpu *vcpu)
 {
        return vcpu->init.features[0] & BIT(KVM_ARM_VCPU_HAS_EL2);
 }