1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * XCR0 cpuid test
4 *
5 * Copyright (C) 2022, Google LLC.
6 */
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/ioctl.h>
12
13 #include "test_util.h"
14
15 #include "kvm_util.h"
16 #include "processor.h"
17
18 /*
19 * Assert that architectural dependency rules are satisfied, e.g. that AVX is
20 * supported if and only if SSE is supported.
21 */
22 #define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \
23 do { \
24 uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \
25 \
26 __GUEST_ASSERT((__supported & (xfeatures)) != (xfeatures) || \
27 __supported == ((xfeatures) | (dependencies)), \
28 "supported = 0x%llx, xfeatures = 0x%llx, dependencies = 0x%llx", \
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(!__supported || __supported == (xfeatures), \
45 "supported = 0x%llx, xfeatures = 0x%llx", \
46 __supported, (xfeatures)); \
47 } while (0)
48
guest_code(void)49 static void guest_code(void)
50 {
51 uint64_t xcr0_reset;
52 uint64_t supported_xcr0;
53 int i, vector;
54
55 set_cr4(get_cr4() | X86_CR4_OSXSAVE);
56
57 xcr0_reset = xgetbv(0);
58 supported_xcr0 = this_cpu_supported_xcr0();
59
60 GUEST_ASSERT(xcr0_reset == XFEATURE_MASK_FP);
61
62 /* Check AVX */
63 ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
64 XFEATURE_MASK_YMM,
65 XFEATURE_MASK_SSE);
66
67 /* Check MPX */
68 ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
69 XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
70
71 /* Check AVX-512 */
72 ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
73 XFEATURE_MASK_AVX512,
74 XFEATURE_MASK_SSE | XFEATURE_MASK_YMM);
75 ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
76 XFEATURE_MASK_AVX512);
77
78 /* Check AMX */
79 ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
80 XFEATURE_MASK_XTILE);
81
82 vector = xsetbv_safe(0, supported_xcr0);
83 __GUEST_ASSERT(!vector,
84 "Expected success on XSETBV(0x%llx), got vector '0x%x'",
85 supported_xcr0, vector);
86
87 for (i = 0; i < 64; i++) {
88 if (supported_xcr0 & BIT_ULL(i))
89 continue;
90
91 vector = xsetbv_safe(0, supported_xcr0 | BIT_ULL(i));
92 __GUEST_ASSERT(vector == GP_VECTOR,
93 "Expected #GP on XSETBV(0x%llx), supported XCR0 = %llx, got vector '0x%x'",
94 BIT_ULL(i), supported_xcr0, vector);
95 }
96
97 GUEST_DONE();
98 }
99
main(int argc,char * argv[])100 int main(int argc, char *argv[])
101 {
102 struct kvm_vcpu *vcpu;
103 struct kvm_run *run;
104 struct kvm_vm *vm;
105 struct ucall uc;
106
107 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
108
109 vm = vm_create_with_one_vcpu(&vcpu, guest_code);
110 run = vcpu->run;
111
112 vm_init_descriptor_tables(vm);
113 vcpu_init_descriptor_tables(vcpu);
114
115 while (1) {
116 vcpu_run(vcpu);
117
118 TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
119 "Unexpected exit reason: %u (%s),\n",
120 run->exit_reason,
121 exit_reason_str(run->exit_reason));
122
123 switch (get_ucall(vcpu, &uc)) {
124 case UCALL_ABORT:
125 REPORT_GUEST_ASSERT(uc);
126 break;
127 case UCALL_DONE:
128 goto done;
129 default:
130 TEST_FAIL("Unknown ucall %lu", uc.cmd);
131 }
132 }
133
134 done:
135 kvm_vm_free(vm);
136 return 0;
137 }
138