Skip to content

Commit 03a405b

Browse files
suomilewissean-jc
authored andcommitted
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>
1 parent 28f2302 commit 03a405b

3 files changed

Lines changed: 153 additions & 0 deletions

File tree

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
105105
TEST_GEN_PROGS_x86_64 += x86_64/vmx_nested_tsc_scaling_test
106106
TEST_GEN_PROGS_x86_64 += x86_64/xapic_ipi_test
107107
TEST_GEN_PROGS_x86_64 += x86_64/xapic_state_test
108+
TEST_GEN_PROGS_x86_64 += x86_64/xcr0_cpuid_test
108109
TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test
109110
TEST_GEN_PROGS_x86_64 += x86_64/debug_regs
110111
TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test

tools/testing/selftests/kvm/include/x86_64/processor.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,11 @@ struct kvm_x86_cpu_property {
241241
#define X86_PROPERTY_PMU_NR_GP_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 8, 15)
242242
#define X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 24, 31)
243243

244+
#define X86_PROPERTY_SUPPORTED_XCR0_LO KVM_X86_CPU_PROPERTY(0xd, 0, EAX, 0, 31)
244245
#define X86_PROPERTY_XSTATE_MAX_SIZE_XCR0 KVM_X86_CPU_PROPERTY(0xd, 0, EBX, 0, 31)
245246
#define X86_PROPERTY_XSTATE_MAX_SIZE KVM_X86_CPU_PROPERTY(0xd, 0, ECX, 0, 31)
247+
#define X86_PROPERTY_SUPPORTED_XCR0_HI KVM_X86_CPU_PROPERTY(0xd, 0, EDX, 0, 31)
248+
246249
#define X86_PROPERTY_XSTATE_TILE_SIZE KVM_X86_CPU_PROPERTY(0xd, 18, EAX, 0, 31)
247250
#define X86_PROPERTY_XSTATE_TILE_OFFSET KVM_X86_CPU_PROPERTY(0xd, 18, EBX, 0, 31)
248251
#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)
681684
!this_cpu_has(feature.anti_feature);
682685
}
683686

687+
static __always_inline uint64_t this_cpu_supported_xcr0(void)
688+
{
689+
if (!this_cpu_has_p(X86_PROPERTY_SUPPORTED_XCR0_LO))
690+
return 0;
691+
692+
return this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_LO) |
693+
((uint64_t)this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32);
694+
}
695+
684696
typedef u32 __attribute__((vector_size(16))) sse128_t;
685697
#define __sse128_u union { sse128_t vec; u64 as_u64[2]; u32 as_u32[4]; }
686698
#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)
11041116
return kvm_asm_safe("wrmsr", "a"(val & -1u), "d"(val >> 32), "c"(msr));
11051117
}
11061118

1119+
static inline uint8_t xsetbv_safe(uint32_t index, uint64_t value)
1120+
{
1121+
u32 eax = value;
1122+
u32 edx = value >> 32;
1123+
1124+
return kvm_asm_safe("xsetbv", "a" (eax), "d" (edx), "c" (index));
1125+
}
1126+
11071127
bool kvm_is_tdp_enabled(void);
11081128

11091129
uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr,
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* XCR0 cpuid test
4+
*
5+
* Copyright (C) 2022, Google LLC.
6+
*/
7+
8+
#include <fcntl.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <sys/ioctl.h>
13+
14+
#include "test_util.h"
15+
16+
#include "kvm_util.h"
17+
#include "processor.h"
18+
19+
/*
20+
* Assert that architectural dependency rules are satisfied, e.g. that AVX is
21+
* supported if and only if SSE is supported.
22+
*/
23+
#define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \
24+
do { \
25+
uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \
26+
\
27+
GUEST_ASSERT_3((__supported & (xfeatures)) != (xfeatures) || \
28+
__supported == ((xfeatures) | (dependencies)), \
29+
__supported, (xfeatures), (dependencies)); \
30+
} while (0)
31+
32+
/*
33+
* Assert that KVM reports a sane, usable as-is XCR0. Architecturally, a CPU
34+
* isn't strictly required to _support_ all XFeatures related to a feature, but
35+
* at the same time XSETBV will #GP if bundled XFeatures aren't enabled and
36+
* disabled coherently. E.g. a CPU can technically enumerate supported for
37+
* XTILE_CFG but not XTILE_DATA, but attempting to enable XTILE_CFG without
38+
* XTILE_DATA will #GP.
39+
*/
40+
#define ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0, xfeatures) \
41+
do { \
42+
uint64_t __supported = (supported_xcr0) & (xfeatures); \
43+
\
44+
GUEST_ASSERT_2(!__supported || __supported == (xfeatures), \
45+
__supported, (xfeatures)); \
46+
} while (0)
47+
48+
static void guest_code(void)
49+
{
50+
uint64_t xcr0_reset;
51+
uint64_t supported_xcr0;
52+
int i, vector;
53+
54+
set_cr4(get_cr4() | X86_CR4_OSXSAVE);
55+
56+
xcr0_reset = xgetbv(0);
57+
supported_xcr0 = this_cpu_supported_xcr0();
58+
59+
GUEST_ASSERT(xcr0_reset == XFEATURE_MASK_FP);
60+
61+
/* Check AVX */
62+
ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
63+
XFEATURE_MASK_YMM,
64+
XFEATURE_MASK_SSE);
65+
66+
/* Check MPX */
67+
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
68+
XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
69+
70+
/* Check AVX-512 */
71+
ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
72+
XFEATURE_MASK_AVX512,
73+
XFEATURE_MASK_SSE | XFEATURE_MASK_YMM);
74+
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
75+
XFEATURE_MASK_AVX512);
76+
77+
/* Check AMX */
78+
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
79+
XFEATURE_MASK_XTILE);
80+
81+
vector = xsetbv_safe(0, supported_xcr0);
82+
GUEST_ASSERT_2(!vector, supported_xcr0, vector);
83+
84+
for (i = 0; i < 64; i++) {
85+
if (supported_xcr0 & BIT_ULL(i))
86+
continue;
87+
88+
vector = xsetbv_safe(0, supported_xcr0 | BIT_ULL(i));
89+
GUEST_ASSERT_3(vector == GP_VECTOR, supported_xcr0, vector, BIT_ULL(i));
90+
}
91+
92+
GUEST_DONE();
93+
}
94+
95+
int main(int argc, char *argv[])
96+
{
97+
struct kvm_vcpu *vcpu;
98+
struct kvm_run *run;
99+
struct kvm_vm *vm;
100+
struct ucall uc;
101+
102+
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
103+
104+
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
105+
run = vcpu->run;
106+
107+
vm_init_descriptor_tables(vm);
108+
vcpu_init_descriptor_tables(vcpu);
109+
110+
while (1) {
111+
vcpu_run(vcpu);
112+
113+
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
114+
"Unexpected exit reason: %u (%s),\n",
115+
run->exit_reason,
116+
exit_reason_str(run->exit_reason));
117+
118+
switch (get_ucall(vcpu, &uc)) {
119+
case UCALL_ABORT:
120+
REPORT_GUEST_ASSERT_3(uc, "0x%lx 0x%lx 0x%lx");
121+
break;
122+
case UCALL_DONE:
123+
goto done;
124+
default:
125+
TEST_FAIL("Unknown ucall %lu", uc.cmd);
126+
}
127+
}
128+
129+
done:
130+
kvm_vm_free(vm);
131+
return 0;
132+
}

0 commit comments

Comments
 (0)