]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: selftests: Add test for SVE host corruption
authorMark Brown <broonie@kernel.org>
Wed, 16 Apr 2025 23:32:49 +0000 (00:32 +0100)
committerMarc Zyngier <maz@kernel.org>
Tue, 6 May 2025 08:50:56 +0000 (09:50 +0100)
Until recently, the kernel could unexpectedly discard SVE state for a
period after a KVM_RUN ioctl, when the guest did not execute any
FPSIMD/SVE/SME instructions. We fixed that issue in commit:

  fbc7e61195e2 ("KVM: arm64: Unconditionally save+flush host FPSIMD/SVE/SME state")

Add a test which tries to provoke that issue by manipulating SVE state
before/after running a guest which does not execute any FPSIMD/SVE/SME
instructions. The test executes a handful of iterations to miminize
the risk that the issue is masked by preemption.

Signed-off--by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Link: https://lore.kernel.org/r/20250417-kvm-selftest-sve-signal-v1-1-6330c2f3da0c@kernel.org
[maz: Restored MR's SoB, fixed commit message according to MR's write-up]
Signed-off-by: Marc Zyngier <maz@kernel.org>
tools/testing/selftests/kvm/Makefile.kvm
tools/testing/selftests/kvm/arm64/host_sve.c [new file with mode: 0644]

index f62b0a5aba35a06a1845e9dfc49064431791853d..d37072054a3d028e08734ad7b838db6027af71d4 100644 (file)
@@ -147,6 +147,7 @@ TEST_GEN_PROGS_arm64 = $(TEST_GEN_PROGS_COMMON)
 TEST_GEN_PROGS_arm64 += arm64/aarch32_id_regs
 TEST_GEN_PROGS_arm64 += arm64/arch_timer_edge_cases
 TEST_GEN_PROGS_arm64 += arm64/debug-exceptions
+TEST_GEN_PROGS_arm64 += arm64/host_sve
 TEST_GEN_PROGS_arm64 += arm64/hypercalls
 TEST_GEN_PROGS_arm64 += arm64/mmio_abort
 TEST_GEN_PROGS_arm64 += arm64/page_fault_test
diff --git a/tools/testing/selftests/kvm/arm64/host_sve.c b/tools/testing/selftests/kvm/arm64/host_sve.c
new file mode 100644 (file)
index 0000000..3826772
--- /dev/null
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Host SVE: Check FPSIMD/SVE/SME save/restore over KVM_RUN ioctls.
+ *
+ * Copyright 2025 Arm, Ltd
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/auxv.h>
+#include <asm/kvm.h>
+#include <kvm_util.h>
+
+#include "ucall_common.h"
+
+static void guest_code(void)
+{
+       for (int i = 0; i < 10; i++) {
+               GUEST_UCALL_NONE();
+       }
+
+       GUEST_DONE();
+}
+
+void handle_sigill(int sig, siginfo_t *info, void *ctx)
+{
+       ucontext_t *uctx = ctx;
+
+       printf("  < host signal %d >\n", sig);
+
+       /*
+        * Skip the UDF
+        */
+       uctx->uc_mcontext.pc += 4;
+}
+
+void register_sigill_handler(void)
+{
+       struct sigaction sa = {
+               .sa_sigaction = handle_sigill,
+               .sa_flags = SA_SIGINFO,
+       };
+       sigaction(SIGILL, &sa, NULL);
+}
+
+static void do_sve_roundtrip(void)
+{
+       unsigned long before, after;
+
+       /*
+        * Set all bits in a predicate register, force a save/restore via a
+        * SIGILL (which handle_sigill() will recover from), then report
+        * whether the value has changed.
+        */
+       asm volatile(
+       "       .arch_extension sve\n"
+       "       ptrue   p0.B\n"
+       "       cntp    %[before], p0, p0.B\n"
+       "       udf #0\n"
+       "       cntp    %[after], p0, p0.B\n"
+       : [before] "=r" (before),
+         [after] "=r" (after)
+       :
+       : "p0"
+       );
+
+       if (before != after) {
+               TEST_FAIL("Signal roundtrip discarded predicate bits (%ld => %ld)\n",
+                         before, after);
+       } else {
+               printf("Signal roundtrip preserved predicate bits (%ld => %ld)\n",
+                      before, after);
+       }
+}
+
+static void test_run(void)
+{
+       struct kvm_vcpu *vcpu;
+       struct kvm_vm *vm;
+       struct ucall uc;
+       bool guest_done = false;
+
+       register_sigill_handler();
+
+       vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+
+       do_sve_roundtrip();
+
+       while (!guest_done) {
+
+               printf("Running VCPU...\n");
+               vcpu_run(vcpu);
+
+               switch (get_ucall(vcpu, &uc)) {
+               case UCALL_NONE:
+                       do_sve_roundtrip();
+                       do_sve_roundtrip();
+                       break;
+               case UCALL_DONE:
+                       guest_done = true;
+                       break;
+               case UCALL_ABORT:
+                       REPORT_GUEST_ASSERT(uc);
+                       break;
+               default:
+                       TEST_FAIL("Unexpected guest exit");
+               }
+       }
+
+       kvm_vm_free(vm);
+}
+
+int main(void)
+{
+       /*
+        * This is testing the host environment, we don't care about
+        * guest SVE support.
+        */
+       if (!(getauxval(AT_HWCAP) & HWCAP_SVE)) {
+               printf("SVE not supported\n");
+               return KSFT_SKIP;
+       }
+
+       test_run();
+       return 0;
+}