linux/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c

140 lines
3.6 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* XCR0 cpuid test
*
* Copyright (C) 2022, Google LLC.
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
/*
* Assert that architectural dependency rules are satisfied, e.g. that AVX is
* supported if and only if SSE is supported.
*/
#define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \
do { \
uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \
\
__GUEST_ASSERT((__supported & (xfeatures)) != (xfeatures) || \
__supported == ((xfeatures) | (dependencies)), \
"supported = 0x%lx, xfeatures = 0x%llx, dependencies = 0x%llx", \
__supported, (xfeatures), (dependencies)); \
} while (0)
/*
* Assert that KVM reports a sane, usable as-is XCR0. Architecturally, a CPU
* isn't strictly required to _support_ all XFeatures related to a feature, but
* at the same time XSETBV will #GP if bundled XFeatures aren't enabled and
* disabled coherently. E.g. a CPU can technically enumerate supported for
* XTILE_CFG but not XTILE_DATA, but attempting to enable XTILE_CFG without
* XTILE_DATA will #GP.
*/
#define ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0, xfeatures) \
do { \
uint64_t __supported = (supported_xcr0) & (xfeatures); \
\
__GUEST_ASSERT(!__supported || __supported == (xfeatures), \
"supported = 0x%lx, xfeatures = 0x%llx", \
__supported, (xfeatures)); \
} while (0)
static void guest_code(void)
{
KVM: selftests: Configure XCR0 to max supported value by default To play nice with compilers generating AVX instructions, set CR4.OSXSAVE and configure XCR0 by default when creating selftests vCPUs. Some distros have switched gcc to '-march=x86-64-v3' by default, and while it's hard to find a CPU which doesn't support AVX today, many KVM selftests fail with ==== Test Assertion Failure ==== lib/x86_64/processor.c:570: Unhandled exception in guest pid=72747 tid=72747 errno=4 - Interrupted system call Unhandled exception '0x6' at guest RIP '0x4104f7' due to selftests not enabling AVX by default for the guest. The failure is easy to reproduce elsewhere with: $ make clean && CFLAGS='-march=x86-64-v3' make -j && ./x86_64/kvm_pv_test E.g. gcc-13 with -march=x86-64-v3 compiles this chunk from selftests' kvm_fixup_exception(): regs->rip = regs->r11; regs->r9 = regs->vector; regs->r10 = regs->error_code; into this monstronsity (which is clever, but oof): 405313: c4 e1 f9 6e c8 vmovq %rax,%xmm1 405318: 48 89 68 08 mov %rbp,0x8(%rax) 40531c: 48 89 e8 mov %rbp,%rax 40531f: c4 c3 f1 22 c4 01 vpinsrq $0x1,%r12,%xmm1,%xmm0 405325: 49 89 6d 38 mov %rbp,0x38(%r13) 405329: c5 fa 7f 45 00 vmovdqu %xmm0,0x0(%rbp) Alternatively, KVM selftests could explicitly restrict the compiler to -march=x86-64-v2, but odds are very good that punting on AVX enabling will simply result in tests that "need" AVX doing their own thing, e.g. there are already three or so additional cleanups that can be done on top. Reported-by: Vitaly Kuznetsov <vkuznets@redhat.com> Closes: https://lore.kernel.org/all/20240920154422.2890096-1-vkuznets@redhat.com Reviewed-and-tested-by: Vitaly Kuznetsov <vkuznets@redhat.com> Link: https://lore.kernel.org/r/20241003234337.273364-6-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
2024-10-03 16:43:31 -07:00
uint64_t initial_xcr0;
uint64_t supported_xcr0;
int i, vector;
set_cr4(get_cr4() | X86_CR4_OSXSAVE);
KVM: selftests: Configure XCR0 to max supported value by default To play nice with compilers generating AVX instructions, set CR4.OSXSAVE and configure XCR0 by default when creating selftests vCPUs. Some distros have switched gcc to '-march=x86-64-v3' by default, and while it's hard to find a CPU which doesn't support AVX today, many KVM selftests fail with ==== Test Assertion Failure ==== lib/x86_64/processor.c:570: Unhandled exception in guest pid=72747 tid=72747 errno=4 - Interrupted system call Unhandled exception '0x6' at guest RIP '0x4104f7' due to selftests not enabling AVX by default for the guest. The failure is easy to reproduce elsewhere with: $ make clean && CFLAGS='-march=x86-64-v3' make -j && ./x86_64/kvm_pv_test E.g. gcc-13 with -march=x86-64-v3 compiles this chunk from selftests' kvm_fixup_exception(): regs->rip = regs->r11; regs->r9 = regs->vector; regs->r10 = regs->error_code; into this monstronsity (which is clever, but oof): 405313: c4 e1 f9 6e c8 vmovq %rax,%xmm1 405318: 48 89 68 08 mov %rbp,0x8(%rax) 40531c: 48 89 e8 mov %rbp,%rax 40531f: c4 c3 f1 22 c4 01 vpinsrq $0x1,%r12,%xmm1,%xmm0 405325: 49 89 6d 38 mov %rbp,0x38(%r13) 405329: c5 fa 7f 45 00 vmovdqu %xmm0,0x0(%rbp) Alternatively, KVM selftests could explicitly restrict the compiler to -march=x86-64-v2, but odds are very good that punting on AVX enabling will simply result in tests that "need" AVX doing their own thing, e.g. there are already three or so additional cleanups that can be done on top. Reported-by: Vitaly Kuznetsov <vkuznets@redhat.com> Closes: https://lore.kernel.org/all/20240920154422.2890096-1-vkuznets@redhat.com Reviewed-and-tested-by: Vitaly Kuznetsov <vkuznets@redhat.com> Link: https://lore.kernel.org/r/20241003234337.273364-6-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
2024-10-03 16:43:31 -07:00
initial_xcr0 = xgetbv(0);
supported_xcr0 = this_cpu_supported_xcr0();
KVM: selftests: Configure XCR0 to max supported value by default To play nice with compilers generating AVX instructions, set CR4.OSXSAVE and configure XCR0 by default when creating selftests vCPUs. Some distros have switched gcc to '-march=x86-64-v3' by default, and while it's hard to find a CPU which doesn't support AVX today, many KVM selftests fail with ==== Test Assertion Failure ==== lib/x86_64/processor.c:570: Unhandled exception in guest pid=72747 tid=72747 errno=4 - Interrupted system call Unhandled exception '0x6' at guest RIP '0x4104f7' due to selftests not enabling AVX by default for the guest. The failure is easy to reproduce elsewhere with: $ make clean && CFLAGS='-march=x86-64-v3' make -j && ./x86_64/kvm_pv_test E.g. gcc-13 with -march=x86-64-v3 compiles this chunk from selftests' kvm_fixup_exception(): regs->rip = regs->r11; regs->r9 = regs->vector; regs->r10 = regs->error_code; into this monstronsity (which is clever, but oof): 405313: c4 e1 f9 6e c8 vmovq %rax,%xmm1 405318: 48 89 68 08 mov %rbp,0x8(%rax) 40531c: 48 89 e8 mov %rbp,%rax 40531f: c4 c3 f1 22 c4 01 vpinsrq $0x1,%r12,%xmm1,%xmm0 405325: 49 89 6d 38 mov %rbp,0x38(%r13) 405329: c5 fa 7f 45 00 vmovdqu %xmm0,0x0(%rbp) Alternatively, KVM selftests could explicitly restrict the compiler to -march=x86-64-v2, but odds are very good that punting on AVX enabling will simply result in tests that "need" AVX doing their own thing, e.g. there are already three or so additional cleanups that can be done on top. Reported-by: Vitaly Kuznetsov <vkuznets@redhat.com> Closes: https://lore.kernel.org/all/20240920154422.2890096-1-vkuznets@redhat.com Reviewed-and-tested-by: Vitaly Kuznetsov <vkuznets@redhat.com> Link: https://lore.kernel.org/r/20241003234337.273364-6-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
2024-10-03 16:43:31 -07:00
GUEST_ASSERT(initial_xcr0 == supported_xcr0);
/* Check AVX */
ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
XFEATURE_MASK_YMM,
XFEATURE_MASK_SSE);
/* Check MPX */
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
/* Check AVX-512 */
ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
XFEATURE_MASK_AVX512,
XFEATURE_MASK_SSE | XFEATURE_MASK_YMM);
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
XFEATURE_MASK_AVX512);
/* Check AMX */
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
XFEATURE_MASK_XTILE);
vector = xsetbv_safe(0, XFEATURE_MASK_FP);
__GUEST_ASSERT(!vector,
"Expected success on XSETBV(FP), got vector '0x%x'",
vector);
vector = xsetbv_safe(0, supported_xcr0);
__GUEST_ASSERT(!vector,
"Expected success on XSETBV(0x%lx), got vector '0x%x'",
supported_xcr0, vector);
for (i = 0; i < 64; i++) {
if (supported_xcr0 & BIT_ULL(i))
continue;
vector = xsetbv_safe(0, supported_xcr0 | BIT_ULL(i));
__GUEST_ASSERT(vector == GP_VECTOR,
"Expected #GP on XSETBV(0x%llx), supported XCR0 = %lx, got vector '0x%x'",
BIT_ULL(i), supported_xcr0, vector);
}
GUEST_DONE();
}
int main(int argc, char *argv[])
{
struct kvm_vcpu *vcpu;
struct kvm_run *run;
struct kvm_vm *vm;
struct ucall uc;
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
run = vcpu->run;
while (1) {
vcpu_run(vcpu);
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
"Unexpected exit reason: %u (%s),",
run->exit_reason,
exit_reason_str(run->exit_reason));
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
REPORT_GUEST_ASSERT(uc);
break;
case UCALL_DONE:
goto done;
default:
TEST_FAIL("Unknown ucall %lu", uc.cmd);
}
}
done:
kvm_vm_free(vm);
return 0;
}