xref: /kvm-unit-tests/x86/xsave.c (revision 7d36db351752e29ad27eaafe3f102de7064e429b)
1*7d36db35SAvi Kivity #include "libcflat.h"
2*7d36db35SAvi Kivity #include "idt.h"
3*7d36db35SAvi Kivity 
4*7d36db35SAvi Kivity #ifdef __x86_64__
5*7d36db35SAvi Kivity #define uint64_t unsigned long
6*7d36db35SAvi Kivity #else
7*7d36db35SAvi Kivity #define uint64_t unsigned long long
8*7d36db35SAvi Kivity #endif
9*7d36db35SAvi Kivity 
10*7d36db35SAvi Kivity static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
11*7d36db35SAvi Kivity         unsigned int *ecx, unsigned int *edx)
12*7d36db35SAvi Kivity {
13*7d36db35SAvi Kivity     /* ecx is often an input as well as an output. */
14*7d36db35SAvi Kivity     asm volatile("cpuid"
15*7d36db35SAvi Kivity             : "=a" (*eax),
16*7d36db35SAvi Kivity             "=b" (*ebx),
17*7d36db35SAvi Kivity             "=c" (*ecx),
18*7d36db35SAvi Kivity             "=d" (*edx)
19*7d36db35SAvi Kivity             : "0" (*eax), "2" (*ecx));
20*7d36db35SAvi Kivity }
21*7d36db35SAvi Kivity 
22*7d36db35SAvi Kivity /*
23*7d36db35SAvi Kivity  * Generic CPUID function
24*7d36db35SAvi Kivity  * clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx
25*7d36db35SAvi Kivity  * resulting in stale register contents being returned.
26*7d36db35SAvi Kivity  */
27*7d36db35SAvi Kivity void cpuid(unsigned int op,
28*7d36db35SAvi Kivity         unsigned int *eax, unsigned int *ebx,
29*7d36db35SAvi Kivity         unsigned int *ecx, unsigned int *edx)
30*7d36db35SAvi Kivity {
31*7d36db35SAvi Kivity     *eax = op;
32*7d36db35SAvi Kivity     *ecx = 0;
33*7d36db35SAvi Kivity     __cpuid(eax, ebx, ecx, edx);
34*7d36db35SAvi Kivity }
35*7d36db35SAvi Kivity 
36*7d36db35SAvi Kivity /* Some CPUID calls want 'count' to be placed in ecx */
37*7d36db35SAvi Kivity void cpuid_count(unsigned int op, int count,
38*7d36db35SAvi Kivity         unsigned int *eax, unsigned int *ebx,
39*7d36db35SAvi Kivity         unsigned int *ecx, unsigned int *edx)
40*7d36db35SAvi Kivity {
41*7d36db35SAvi Kivity     *eax = op;
42*7d36db35SAvi Kivity     *ecx = count;
43*7d36db35SAvi Kivity     __cpuid(eax, ebx, ecx, edx);
44*7d36db35SAvi Kivity }
45*7d36db35SAvi Kivity 
46*7d36db35SAvi Kivity int xgetbv_checking(u32 index, u64 *result)
47*7d36db35SAvi Kivity {
48*7d36db35SAvi Kivity     u32 eax, edx;
49*7d36db35SAvi Kivity 
50*7d36db35SAvi Kivity     asm volatile(ASM_TRY("1f")
51*7d36db35SAvi Kivity             ".byte 0x0f,0x01,0xd0\n\t" /* xgetbv */
52*7d36db35SAvi Kivity             "1:"
53*7d36db35SAvi Kivity             : "=a" (eax), "=d" (edx)
54*7d36db35SAvi Kivity             : "c" (index));
55*7d36db35SAvi Kivity     *result = eax + ((u64)edx << 32);
56*7d36db35SAvi Kivity     return exception_vector();
57*7d36db35SAvi Kivity }
58*7d36db35SAvi Kivity 
59*7d36db35SAvi Kivity int xsetbv_checking(u32 index, u64 value)
60*7d36db35SAvi Kivity {
61*7d36db35SAvi Kivity     u32 eax = value;
62*7d36db35SAvi Kivity     u32 edx = value >> 32;
63*7d36db35SAvi Kivity 
64*7d36db35SAvi Kivity     asm volatile(ASM_TRY("1f")
65*7d36db35SAvi Kivity             ".byte 0x0f,0x01,0xd1\n\t" /* xsetbv */
66*7d36db35SAvi Kivity             "1:"
67*7d36db35SAvi Kivity             : : "a" (eax), "d" (edx), "c" (index));
68*7d36db35SAvi Kivity     return exception_vector();
69*7d36db35SAvi Kivity }
70*7d36db35SAvi Kivity 
71*7d36db35SAvi Kivity unsigned long read_cr4(void)
72*7d36db35SAvi Kivity {
73*7d36db35SAvi Kivity     unsigned long val;
74*7d36db35SAvi Kivity     asm volatile("mov %%cr4,%0" : "=r" (val));
75*7d36db35SAvi Kivity     return val;
76*7d36db35SAvi Kivity }
77*7d36db35SAvi Kivity 
78*7d36db35SAvi Kivity int write_cr4_checking(unsigned long val)
79*7d36db35SAvi Kivity {
80*7d36db35SAvi Kivity     asm volatile(ASM_TRY("1f")
81*7d36db35SAvi Kivity             "mov %0,%%cr4\n\t"
82*7d36db35SAvi Kivity             "1:": : "r" (val));
83*7d36db35SAvi Kivity     return exception_vector();
84*7d36db35SAvi Kivity }
85*7d36db35SAvi Kivity 
86*7d36db35SAvi Kivity #define CPUID_1_ECX_XSAVE	    (1 << 26)
87*7d36db35SAvi Kivity #define CPUID_1_ECX_OSXSAVE	    (1 << 27)
88*7d36db35SAvi Kivity int check_cpuid_1_ecx(unsigned int bit)
89*7d36db35SAvi Kivity {
90*7d36db35SAvi Kivity     unsigned int eax, ebx, ecx, edx;
91*7d36db35SAvi Kivity     cpuid(1, &eax, &ebx, &ecx, &edx);
92*7d36db35SAvi Kivity     if (ecx & bit)
93*7d36db35SAvi Kivity         return 1;
94*7d36db35SAvi Kivity     return 0;
95*7d36db35SAvi Kivity }
96*7d36db35SAvi Kivity 
97*7d36db35SAvi Kivity uint64_t get_supported_xcr0(void)
98*7d36db35SAvi Kivity {
99*7d36db35SAvi Kivity     unsigned int eax, ebx, ecx, edx;
100*7d36db35SAvi Kivity     cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
101*7d36db35SAvi Kivity     printf("eax %x, ebx %x, ecx %x, edx %x\n",
102*7d36db35SAvi Kivity             eax, ebx, ecx, edx);
103*7d36db35SAvi Kivity     return eax + ((u64)edx << 32);
104*7d36db35SAvi Kivity }
105*7d36db35SAvi Kivity 
106*7d36db35SAvi Kivity #define X86_CR4_OSXSAVE			0x00040000
107*7d36db35SAvi Kivity #define XCR_XFEATURE_ENABLED_MASK       0x00000000
108*7d36db35SAvi Kivity #define XCR_XFEATURE_ILLEGAL_MASK       0x00000010
109*7d36db35SAvi Kivity 
110*7d36db35SAvi Kivity #define XSTATE_FP       0x1
111*7d36db35SAvi Kivity #define XSTATE_SSE      0x2
112*7d36db35SAvi Kivity #define XSTATE_YMM      0x4
113*7d36db35SAvi Kivity 
114*7d36db35SAvi Kivity static int total_tests, fail_tests;
115*7d36db35SAvi Kivity 
116*7d36db35SAvi Kivity void pass_if(int condition)
117*7d36db35SAvi Kivity {
118*7d36db35SAvi Kivity     total_tests ++;
119*7d36db35SAvi Kivity     if (condition)
120*7d36db35SAvi Kivity         printf("Pass!\n");
121*7d36db35SAvi Kivity     else {
122*7d36db35SAvi Kivity         printf("Fail!\n");
123*7d36db35SAvi Kivity         fail_tests ++;
124*7d36db35SAvi Kivity     }
125*7d36db35SAvi Kivity }
126*7d36db35SAvi Kivity 
127*7d36db35SAvi Kivity void test_xsave(void)
128*7d36db35SAvi Kivity {
129*7d36db35SAvi Kivity     unsigned long cr4;
130*7d36db35SAvi Kivity     uint64_t supported_xcr0;
131*7d36db35SAvi Kivity     uint64_t test_bits;
132*7d36db35SAvi Kivity     u64 xcr0;
133*7d36db35SAvi Kivity     int r;
134*7d36db35SAvi Kivity 
135*7d36db35SAvi Kivity     printf("Legal instruction testing:\n");
136*7d36db35SAvi Kivity     supported_xcr0 = get_supported_xcr0();
137*7d36db35SAvi Kivity     printf("Supported XCR0 bits: 0x%x\n", supported_xcr0);
138*7d36db35SAvi Kivity 
139*7d36db35SAvi Kivity     printf("Check minimal XSAVE required bits: ");
140*7d36db35SAvi Kivity     test_bits = XSTATE_FP | XSTATE_SSE;
141*7d36db35SAvi Kivity     pass_if((supported_xcr0 & test_bits) == test_bits);
142*7d36db35SAvi Kivity 
143*7d36db35SAvi Kivity     printf("Set CR4 OSXSAVE: ");
144*7d36db35SAvi Kivity     cr4 = read_cr4();
145*7d36db35SAvi Kivity     r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
146*7d36db35SAvi Kivity     pass_if(r == 0);
147*7d36db35SAvi Kivity 
148*7d36db35SAvi Kivity     printf("Check CPUID.1.ECX.OSXSAVE - expect 1: ");
149*7d36db35SAvi Kivity     pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE));
150*7d36db35SAvi Kivity 
151*7d36db35SAvi Kivity     printf("    Legal tests\n");
152*7d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP): ");
153*7d36db35SAvi Kivity     test_bits = XSTATE_FP;
154*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
155*7d36db35SAvi Kivity     pass_if(r == 0);
156*7d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
157*7d36db35SAvi Kivity             "XSTATE_FP | XSTATE_SSE): ");
158*7d36db35SAvi Kivity     test_bits = XSTATE_FP | XSTATE_SSE;
159*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
160*7d36db35SAvi Kivity     pass_if(r == 0);
161*7d36db35SAvi Kivity     printf("        xgetbv(XCR_XFEATURE_ENABLED_MASK): ");
162*7d36db35SAvi Kivity     r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
163*7d36db35SAvi Kivity     pass_if(r == 0);
164*7d36db35SAvi Kivity     printf("    Illegal tests\n");
165*7d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, 0) - expect #GP: ");
166*7d36db35SAvi Kivity     test_bits = 0;
167*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
168*7d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
169*7d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_SSE) "
170*7d36db35SAvi Kivity             "- expect #GP: ");
171*7d36db35SAvi Kivity     test_bits = XSTATE_SSE;
172*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
173*7d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
174*7d36db35SAvi Kivity     if (supported_xcr0 & XSTATE_YMM) {
175*7d36db35SAvi Kivity         printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
176*7d36db35SAvi Kivity                 "XSTATE_YMM) - expect #GP: ");
177*7d36db35SAvi Kivity         test_bits = XSTATE_YMM;
178*7d36db35SAvi Kivity         r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
179*7d36db35SAvi Kivity         pass_if(r == GP_VECTOR);
180*7d36db35SAvi Kivity         printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
181*7d36db35SAvi Kivity                 "XSTATE_FP | XSTATE_YMM) - expect #GP: ");
182*7d36db35SAvi Kivity         test_bits = XSTATE_FP | XSTATE_YMM;
183*7d36db35SAvi Kivity         r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
184*7d36db35SAvi Kivity         pass_if(r == GP_VECTOR);
185*7d36db35SAvi Kivity     }
186*7d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
187*7d36db35SAvi Kivity             "- expect #GP: ");
188*7d36db35SAvi Kivity     test_bits = XSTATE_SSE;
189*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
190*7d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
191*7d36db35SAvi Kivity     printf("        xgetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
192*7d36db35SAvi Kivity             "- expect #GP: ");
193*7d36db35SAvi Kivity     test_bits = XSTATE_SSE;
194*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
195*7d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
196*7d36db35SAvi Kivity 
197*7d36db35SAvi Kivity     printf("Unset CR4 OSXSAVE: ");
198*7d36db35SAvi Kivity     cr4 &= ~X86_CR4_OSXSAVE;
199*7d36db35SAvi Kivity     r = write_cr4_checking(cr4);
200*7d36db35SAvi Kivity     pass_if(r == 0);
201*7d36db35SAvi Kivity     printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
202*7d36db35SAvi Kivity     pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
203*7d36db35SAvi Kivity     printf("    Illegal tests:\n");
204*7d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP) - expect #UD: ");
205*7d36db35SAvi Kivity     test_bits = XSTATE_FP;
206*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
207*7d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
208*7d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
209*7d36db35SAvi Kivity             "XSTATE_FP | XSTATE_SSE) - expect #UD: ");
210*7d36db35SAvi Kivity     test_bits = XSTATE_FP | XSTATE_SSE;
211*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
212*7d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
213*7d36db35SAvi Kivity     printf("    Illegal tests:\n");
214*7d36db35SAvi Kivity     printf("	xgetbv(XCR_XFEATURE_ENABLED_MASK) - expect #UD: ");
215*7d36db35SAvi Kivity     r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
216*7d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
217*7d36db35SAvi Kivity }
218*7d36db35SAvi Kivity 
219*7d36db35SAvi Kivity void test_no_xsave(void)
220*7d36db35SAvi Kivity {
221*7d36db35SAvi Kivity     unsigned long cr4;
222*7d36db35SAvi Kivity     u64 xcr0;
223*7d36db35SAvi Kivity     int r;
224*7d36db35SAvi Kivity 
225*7d36db35SAvi Kivity     printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
226*7d36db35SAvi Kivity     pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
227*7d36db35SAvi Kivity 
228*7d36db35SAvi Kivity     printf("Illegal instruction testing:\n");
229*7d36db35SAvi Kivity 
230*7d36db35SAvi Kivity     printf("Set OSXSAVE in CR4 - expect #GP: ");
231*7d36db35SAvi Kivity     cr4 = read_cr4();
232*7d36db35SAvi Kivity     r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
233*7d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
234*7d36db35SAvi Kivity 
235*7d36db35SAvi Kivity     printf("Execute xgetbv - expect #UD: ");
236*7d36db35SAvi Kivity     r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
237*7d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
238*7d36db35SAvi Kivity 
239*7d36db35SAvi Kivity     printf("Execute xsetbv - expect #UD: ");
240*7d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, 0x3);
241*7d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
242*7d36db35SAvi Kivity }
243*7d36db35SAvi Kivity 
244*7d36db35SAvi Kivity int main(void)
245*7d36db35SAvi Kivity {
246*7d36db35SAvi Kivity     setup_idt();
247*7d36db35SAvi Kivity     if (check_cpuid_1_ecx(CPUID_1_ECX_XSAVE)) {
248*7d36db35SAvi Kivity         printf("CPU has XSAVE feature\n");
249*7d36db35SAvi Kivity         test_xsave();
250*7d36db35SAvi Kivity     } else {
251*7d36db35SAvi Kivity         printf("CPU don't has XSAVE feature\n");
252*7d36db35SAvi Kivity         test_no_xsave();
253*7d36db35SAvi Kivity     }
254*7d36db35SAvi Kivity     printf("Total test: %d\n", total_tests);
255*7d36db35SAvi Kivity     if (fail_tests == 0)
256*7d36db35SAvi Kivity         printf("ALL PASS!\n");
257*7d36db35SAvi Kivity     else {
258*7d36db35SAvi Kivity         printf("Fail %d tests.\n", fail_tests);
259*7d36db35SAvi Kivity         return 1;
260*7d36db35SAvi Kivity     }
261*7d36db35SAvi Kivity     return 0;
262*7d36db35SAvi Kivity }
263