// SPDX-License-Identifier: GPL-2.0-only /* * Host SVE: Check FPSIMD/SVE/SME save/restore over KVM_RUN ioctls. * * Copyright 2025 Arm, Ltd */ #include #include #include #include #include #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; }