mirror of
https://github.com/torvalds/linux.git
synced 2026-05-22 14:12:07 +02:00
KVM: selftests: Add test to verify KVM's supported XCR0
Check both architectural rules and KVM's ABI for KVM_GET_SUPPORTED_CPUID
to ensure the supported xfeatures[1] don't violate any of them.
The architectural rules[2] and KVM's contract with userspace ensure for a
given feature, e.g. sse, avx, amx, etc... their associated xfeatures are
either all sets or none of them are set, and any dependencies are enabled
if needed.
[1] EDX:EAX of CPUID.(EAX=0DH,ECX=0)
[2] SDM vol 1, 13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED
FEATURES
Cc: Mingwei Zhang <mizhang@google.com>
Signed-off-by: Aaron Lewis <aaronlewis@google.com>
[sean: expand comments, use a fancy X86_PROPERTY]
Reviewed-by: Aaron Lewis <aaronlewis@google.com>
Tested-by: Aaron Lewis <aaronlewis@google.com>
Link: https://lore.kernel.org/r/20230405004520.421768-7-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
parent
28f2302584
commit
03a405b7a5
|
|
@ -105,6 +105,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
|
|||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_nested_tsc_scaling_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xapic_ipi_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xapic_state_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xcr0_cpuid_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/debug_regs
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test
|
||||
|
|
|
|||
|
|
@ -241,8 +241,11 @@ struct kvm_x86_cpu_property {
|
|||
#define X86_PROPERTY_PMU_NR_GP_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 8, 15)
|
||||
#define X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 24, 31)
|
||||
|
||||
#define X86_PROPERTY_SUPPORTED_XCR0_LO KVM_X86_CPU_PROPERTY(0xd, 0, EAX, 0, 31)
|
||||
#define X86_PROPERTY_XSTATE_MAX_SIZE_XCR0 KVM_X86_CPU_PROPERTY(0xd, 0, EBX, 0, 31)
|
||||
#define X86_PROPERTY_XSTATE_MAX_SIZE KVM_X86_CPU_PROPERTY(0xd, 0, ECX, 0, 31)
|
||||
#define X86_PROPERTY_SUPPORTED_XCR0_HI KVM_X86_CPU_PROPERTY(0xd, 0, EDX, 0, 31)
|
||||
|
||||
#define X86_PROPERTY_XSTATE_TILE_SIZE KVM_X86_CPU_PROPERTY(0xd, 18, EAX, 0, 31)
|
||||
#define X86_PROPERTY_XSTATE_TILE_OFFSET KVM_X86_CPU_PROPERTY(0xd, 18, EBX, 0, 31)
|
||||
#define X86_PROPERTY_AMX_MAX_PALETTE_TABLES KVM_X86_CPU_PROPERTY(0x1d, 0, EAX, 0, 31)
|
||||
|
|
@ -681,6 +684,15 @@ static inline bool this_pmu_has(struct kvm_x86_pmu_feature feature)
|
|||
!this_cpu_has(feature.anti_feature);
|
||||
}
|
||||
|
||||
static __always_inline uint64_t this_cpu_supported_xcr0(void)
|
||||
{
|
||||
if (!this_cpu_has_p(X86_PROPERTY_SUPPORTED_XCR0_LO))
|
||||
return 0;
|
||||
|
||||
return this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_LO) |
|
||||
((uint64_t)this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32);
|
||||
}
|
||||
|
||||
typedef u32 __attribute__((vector_size(16))) sse128_t;
|
||||
#define __sse128_u union { sse128_t vec; u64 as_u64[2]; u32 as_u32[4]; }
|
||||
#define sse128_lo(x) ({ __sse128_u t; t.vec = x; t.as_u64[0]; })
|
||||
|
|
@ -1104,6 +1116,14 @@ static inline uint8_t wrmsr_safe(uint32_t msr, uint64_t val)
|
|||
return kvm_asm_safe("wrmsr", "a"(val & -1u), "d"(val >> 32), "c"(msr));
|
||||
}
|
||||
|
||||
static inline uint8_t xsetbv_safe(uint32_t index, uint64_t value)
|
||||
{
|
||||
u32 eax = value;
|
||||
u32 edx = value >> 32;
|
||||
|
||||
return kvm_asm_safe("xsetbv", "a" (eax), "d" (edx), "c" (index));
|
||||
}
|
||||
|
||||
bool kvm_is_tdp_enabled(void);
|
||||
|
||||
uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr,
|
||||
|
|
|
|||
132
tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c
Normal file
132
tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// 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_3((__supported & (xfeatures)) != (xfeatures) || \
|
||||
__supported == ((xfeatures) | (dependencies)), \
|
||||
__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_2(!__supported || __supported == (xfeatures), \
|
||||
__supported, (xfeatures)); \
|
||||
} while (0)
|
||||
|
||||
static void guest_code(void)
|
||||
{
|
||||
uint64_t xcr0_reset;
|
||||
uint64_t supported_xcr0;
|
||||
int i, vector;
|
||||
|
||||
set_cr4(get_cr4() | X86_CR4_OSXSAVE);
|
||||
|
||||
xcr0_reset = xgetbv(0);
|
||||
supported_xcr0 = this_cpu_supported_xcr0();
|
||||
|
||||
GUEST_ASSERT(xcr0_reset == XFEATURE_MASK_FP);
|
||||
|
||||
/* 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, supported_xcr0);
|
||||
GUEST_ASSERT_2(!vector, 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_3(vector == GP_VECTOR, supported_xcr0, vector, BIT_ULL(i));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
vm_init_descriptor_tables(vm);
|
||||
vcpu_init_descriptor_tables(vcpu);
|
||||
|
||||
while (1) {
|
||||
vcpu_run(vcpu);
|
||||
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
|
||||
"Unexpected exit reason: %u (%s),\n",
|
||||
run->exit_reason,
|
||||
exit_reason_str(run->exit_reason));
|
||||
|
||||
switch (get_ucall(vcpu, &uc)) {
|
||||
case UCALL_ABORT:
|
||||
REPORT_GUEST_ASSERT_3(uc, "0x%lx 0x%lx 0x%lx");
|
||||
break;
|
||||
case UCALL_DONE:
|
||||
goto done;
|
||||
default:
|
||||
TEST_FAIL("Unknown ucall %lu", uc.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
kvm_vm_free(vm);
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user