xref: /kvm-unit-tests/x86/xsave.c (revision e7c37968d5f9fd45e6e2f88b82562fd89128e365)
17d36db35SAvi Kivity #include "libcflat.h"
2*e7c37968SGleb Natapov #include "desc.h"
37d36db35SAvi Kivity 
47d36db35SAvi Kivity #ifdef __x86_64__
57d36db35SAvi Kivity #define uint64_t unsigned long
67d36db35SAvi Kivity #else
77d36db35SAvi Kivity #define uint64_t unsigned long long
87d36db35SAvi Kivity #endif
97d36db35SAvi Kivity 
107d36db35SAvi Kivity static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
117d36db35SAvi Kivity         unsigned int *ecx, unsigned int *edx)
127d36db35SAvi Kivity {
137d36db35SAvi Kivity     /* ecx is often an input as well as an output. */
147d36db35SAvi Kivity     asm volatile("cpuid"
157d36db35SAvi Kivity             : "=a" (*eax),
167d36db35SAvi Kivity             "=b" (*ebx),
177d36db35SAvi Kivity             "=c" (*ecx),
187d36db35SAvi Kivity             "=d" (*edx)
197d36db35SAvi Kivity             : "0" (*eax), "2" (*ecx));
207d36db35SAvi Kivity }
217d36db35SAvi Kivity 
227d36db35SAvi Kivity /*
237d36db35SAvi Kivity  * Generic CPUID function
247d36db35SAvi Kivity  * clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx
257d36db35SAvi Kivity  * resulting in stale register contents being returned.
267d36db35SAvi Kivity  */
277d36db35SAvi Kivity void cpuid(unsigned int op,
287d36db35SAvi Kivity         unsigned int *eax, unsigned int *ebx,
297d36db35SAvi Kivity         unsigned int *ecx, unsigned int *edx)
307d36db35SAvi Kivity {
317d36db35SAvi Kivity     *eax = op;
327d36db35SAvi Kivity     *ecx = 0;
337d36db35SAvi Kivity     __cpuid(eax, ebx, ecx, edx);
347d36db35SAvi Kivity }
357d36db35SAvi Kivity 
367d36db35SAvi Kivity /* Some CPUID calls want 'count' to be placed in ecx */
377d36db35SAvi Kivity void cpuid_count(unsigned int op, int count,
387d36db35SAvi Kivity         unsigned int *eax, unsigned int *ebx,
397d36db35SAvi Kivity         unsigned int *ecx, unsigned int *edx)
407d36db35SAvi Kivity {
417d36db35SAvi Kivity     *eax = op;
427d36db35SAvi Kivity     *ecx = count;
437d36db35SAvi Kivity     __cpuid(eax, ebx, ecx, edx);
447d36db35SAvi Kivity }
457d36db35SAvi Kivity 
467d36db35SAvi Kivity int xgetbv_checking(u32 index, u64 *result)
477d36db35SAvi Kivity {
487d36db35SAvi Kivity     u32 eax, edx;
497d36db35SAvi Kivity 
507d36db35SAvi Kivity     asm volatile(ASM_TRY("1f")
517d36db35SAvi Kivity             ".byte 0x0f,0x01,0xd0\n\t" /* xgetbv */
527d36db35SAvi Kivity             "1:"
537d36db35SAvi Kivity             : "=a" (eax), "=d" (edx)
547d36db35SAvi Kivity             : "c" (index));
557d36db35SAvi Kivity     *result = eax + ((u64)edx << 32);
567d36db35SAvi Kivity     return exception_vector();
577d36db35SAvi Kivity }
587d36db35SAvi Kivity 
597d36db35SAvi Kivity int xsetbv_checking(u32 index, u64 value)
607d36db35SAvi Kivity {
617d36db35SAvi Kivity     u32 eax = value;
627d36db35SAvi Kivity     u32 edx = value >> 32;
637d36db35SAvi Kivity 
647d36db35SAvi Kivity     asm volatile(ASM_TRY("1f")
657d36db35SAvi Kivity             ".byte 0x0f,0x01,0xd1\n\t" /* xsetbv */
667d36db35SAvi Kivity             "1:"
677d36db35SAvi Kivity             : : "a" (eax), "d" (edx), "c" (index));
687d36db35SAvi Kivity     return exception_vector();
697d36db35SAvi Kivity }
707d36db35SAvi Kivity 
717d36db35SAvi Kivity unsigned long read_cr4(void)
727d36db35SAvi Kivity {
737d36db35SAvi Kivity     unsigned long val;
747d36db35SAvi Kivity     asm volatile("mov %%cr4,%0" : "=r" (val));
757d36db35SAvi Kivity     return val;
767d36db35SAvi Kivity }
777d36db35SAvi Kivity 
787d36db35SAvi Kivity int write_cr4_checking(unsigned long val)
797d36db35SAvi Kivity {
807d36db35SAvi Kivity     asm volatile(ASM_TRY("1f")
817d36db35SAvi Kivity             "mov %0,%%cr4\n\t"
827d36db35SAvi Kivity             "1:": : "r" (val));
837d36db35SAvi Kivity     return exception_vector();
847d36db35SAvi Kivity }
857d36db35SAvi Kivity 
867d36db35SAvi Kivity #define CPUID_1_ECX_XSAVE	    (1 << 26)
877d36db35SAvi Kivity #define CPUID_1_ECX_OSXSAVE	    (1 << 27)
887d36db35SAvi Kivity int check_cpuid_1_ecx(unsigned int bit)
897d36db35SAvi Kivity {
907d36db35SAvi Kivity     unsigned int eax, ebx, ecx, edx;
917d36db35SAvi Kivity     cpuid(1, &eax, &ebx, &ecx, &edx);
927d36db35SAvi Kivity     if (ecx & bit)
937d36db35SAvi Kivity         return 1;
947d36db35SAvi Kivity     return 0;
957d36db35SAvi Kivity }
967d36db35SAvi Kivity 
977d36db35SAvi Kivity uint64_t get_supported_xcr0(void)
987d36db35SAvi Kivity {
997d36db35SAvi Kivity     unsigned int eax, ebx, ecx, edx;
1007d36db35SAvi Kivity     cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
1017d36db35SAvi Kivity     printf("eax %x, ebx %x, ecx %x, edx %x\n",
1027d36db35SAvi Kivity             eax, ebx, ecx, edx);
1037d36db35SAvi Kivity     return eax + ((u64)edx << 32);
1047d36db35SAvi Kivity }
1057d36db35SAvi Kivity 
1067d36db35SAvi Kivity #define X86_CR4_OSXSAVE			0x00040000
1077d36db35SAvi Kivity #define XCR_XFEATURE_ENABLED_MASK       0x00000000
1087d36db35SAvi Kivity #define XCR_XFEATURE_ILLEGAL_MASK       0x00000010
1097d36db35SAvi Kivity 
1107d36db35SAvi Kivity #define XSTATE_FP       0x1
1117d36db35SAvi Kivity #define XSTATE_SSE      0x2
1127d36db35SAvi Kivity #define XSTATE_YMM      0x4
1137d36db35SAvi Kivity 
1147d36db35SAvi Kivity static int total_tests, fail_tests;
1157d36db35SAvi Kivity 
1167d36db35SAvi Kivity void pass_if(int condition)
1177d36db35SAvi Kivity {
1187d36db35SAvi Kivity     total_tests ++;
1197d36db35SAvi Kivity     if (condition)
1207d36db35SAvi Kivity         printf("Pass!\n");
1217d36db35SAvi Kivity     else {
1227d36db35SAvi Kivity         printf("Fail!\n");
1237d36db35SAvi Kivity         fail_tests ++;
1247d36db35SAvi Kivity     }
1257d36db35SAvi Kivity }
1267d36db35SAvi Kivity 
1277d36db35SAvi Kivity void test_xsave(void)
1287d36db35SAvi Kivity {
1297d36db35SAvi Kivity     unsigned long cr4;
1307d36db35SAvi Kivity     uint64_t supported_xcr0;
1317d36db35SAvi Kivity     uint64_t test_bits;
1327d36db35SAvi Kivity     u64 xcr0;
1337d36db35SAvi Kivity     int r;
1347d36db35SAvi Kivity 
1357d36db35SAvi Kivity     printf("Legal instruction testing:\n");
1367d36db35SAvi Kivity     supported_xcr0 = get_supported_xcr0();
1377d36db35SAvi Kivity     printf("Supported XCR0 bits: 0x%x\n", supported_xcr0);
1387d36db35SAvi Kivity 
1397d36db35SAvi Kivity     printf("Check minimal XSAVE required bits: ");
1407d36db35SAvi Kivity     test_bits = XSTATE_FP | XSTATE_SSE;
1417d36db35SAvi Kivity     pass_if((supported_xcr0 & test_bits) == test_bits);
1427d36db35SAvi Kivity 
1437d36db35SAvi Kivity     printf("Set CR4 OSXSAVE: ");
1447d36db35SAvi Kivity     cr4 = read_cr4();
1457d36db35SAvi Kivity     r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
1467d36db35SAvi Kivity     pass_if(r == 0);
1477d36db35SAvi Kivity 
1487d36db35SAvi Kivity     printf("Check CPUID.1.ECX.OSXSAVE - expect 1: ");
1497d36db35SAvi Kivity     pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE));
1507d36db35SAvi Kivity 
1517d36db35SAvi Kivity     printf("    Legal tests\n");
1527d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP): ");
1537d36db35SAvi Kivity     test_bits = XSTATE_FP;
1547d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
1557d36db35SAvi Kivity     pass_if(r == 0);
1567d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
1577d36db35SAvi Kivity             "XSTATE_FP | XSTATE_SSE): ");
1587d36db35SAvi Kivity     test_bits = XSTATE_FP | XSTATE_SSE;
1597d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
1607d36db35SAvi Kivity     pass_if(r == 0);
1617d36db35SAvi Kivity     printf("        xgetbv(XCR_XFEATURE_ENABLED_MASK): ");
1627d36db35SAvi Kivity     r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
1637d36db35SAvi Kivity     pass_if(r == 0);
1647d36db35SAvi Kivity     printf("    Illegal tests\n");
1657d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, 0) - expect #GP: ");
1667d36db35SAvi Kivity     test_bits = 0;
1677d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
1687d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
1697d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_SSE) "
1707d36db35SAvi Kivity             "- expect #GP: ");
1717d36db35SAvi Kivity     test_bits = XSTATE_SSE;
1727d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
1737d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
1747d36db35SAvi Kivity     if (supported_xcr0 & XSTATE_YMM) {
1757d36db35SAvi Kivity         printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
1767d36db35SAvi Kivity                 "XSTATE_YMM) - expect #GP: ");
1777d36db35SAvi Kivity         test_bits = XSTATE_YMM;
1787d36db35SAvi Kivity         r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
1797d36db35SAvi Kivity         pass_if(r == GP_VECTOR);
1807d36db35SAvi Kivity         printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
1817d36db35SAvi Kivity                 "XSTATE_FP | XSTATE_YMM) - expect #GP: ");
1827d36db35SAvi Kivity         test_bits = XSTATE_FP | XSTATE_YMM;
1837d36db35SAvi Kivity         r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
1847d36db35SAvi Kivity         pass_if(r == GP_VECTOR);
1857d36db35SAvi Kivity     }
1867d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
1877d36db35SAvi Kivity             "- expect #GP: ");
1887d36db35SAvi Kivity     test_bits = XSTATE_SSE;
1897d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
1907d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
1917d36db35SAvi Kivity     printf("        xgetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
1927d36db35SAvi Kivity             "- expect #GP: ");
1937d36db35SAvi Kivity     test_bits = XSTATE_SSE;
1947d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
1957d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
1967d36db35SAvi Kivity 
1977d36db35SAvi Kivity     printf("Unset CR4 OSXSAVE: ");
1987d36db35SAvi Kivity     cr4 &= ~X86_CR4_OSXSAVE;
1997d36db35SAvi Kivity     r = write_cr4_checking(cr4);
2007d36db35SAvi Kivity     pass_if(r == 0);
2017d36db35SAvi Kivity     printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
2027d36db35SAvi Kivity     pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
2037d36db35SAvi Kivity     printf("    Illegal tests:\n");
2047d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP) - expect #UD: ");
2057d36db35SAvi Kivity     test_bits = XSTATE_FP;
2067d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
2077d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
2087d36db35SAvi Kivity     printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
2097d36db35SAvi Kivity             "XSTATE_FP | XSTATE_SSE) - expect #UD: ");
2107d36db35SAvi Kivity     test_bits = XSTATE_FP | XSTATE_SSE;
2117d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
2127d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
2137d36db35SAvi Kivity     printf("    Illegal tests:\n");
2147d36db35SAvi Kivity     printf("	xgetbv(XCR_XFEATURE_ENABLED_MASK) - expect #UD: ");
2157d36db35SAvi Kivity     r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
2167d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
2177d36db35SAvi Kivity }
2187d36db35SAvi Kivity 
2197d36db35SAvi Kivity void test_no_xsave(void)
2207d36db35SAvi Kivity {
2217d36db35SAvi Kivity     unsigned long cr4;
2227d36db35SAvi Kivity     u64 xcr0;
2237d36db35SAvi Kivity     int r;
2247d36db35SAvi Kivity 
2257d36db35SAvi Kivity     printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
2267d36db35SAvi Kivity     pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
2277d36db35SAvi Kivity 
2287d36db35SAvi Kivity     printf("Illegal instruction testing:\n");
2297d36db35SAvi Kivity 
2307d36db35SAvi Kivity     printf("Set OSXSAVE in CR4 - expect #GP: ");
2317d36db35SAvi Kivity     cr4 = read_cr4();
2327d36db35SAvi Kivity     r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
2337d36db35SAvi Kivity     pass_if(r == GP_VECTOR);
2347d36db35SAvi Kivity 
2357d36db35SAvi Kivity     printf("Execute xgetbv - expect #UD: ");
2367d36db35SAvi Kivity     r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
2377d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
2387d36db35SAvi Kivity 
2397d36db35SAvi Kivity     printf("Execute xsetbv - expect #UD: ");
2407d36db35SAvi Kivity     r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, 0x3);
2417d36db35SAvi Kivity     pass_if(r == UD_VECTOR);
2427d36db35SAvi Kivity }
2437d36db35SAvi Kivity 
2447d36db35SAvi Kivity int main(void)
2457d36db35SAvi Kivity {
2467d36db35SAvi Kivity     setup_idt();
2477d36db35SAvi Kivity     if (check_cpuid_1_ecx(CPUID_1_ECX_XSAVE)) {
2487d36db35SAvi Kivity         printf("CPU has XSAVE feature\n");
2497d36db35SAvi Kivity         test_xsave();
2507d36db35SAvi Kivity     } else {
2517d36db35SAvi Kivity         printf("CPU don't has XSAVE feature\n");
2527d36db35SAvi Kivity         test_no_xsave();
2537d36db35SAvi Kivity     }
2547d36db35SAvi Kivity     printf("Total test: %d\n", total_tests);
2557d36db35SAvi Kivity     if (fail_tests == 0)
2567d36db35SAvi Kivity         printf("ALL PASS!\n");
2577d36db35SAvi Kivity     else {
2587d36db35SAvi Kivity         printf("Fail %d tests.\n", fail_tests);
2597d36db35SAvi Kivity         return 1;
2607d36db35SAvi Kivity     }
2617d36db35SAvi Kivity     return 0;
2627d36db35SAvi Kivity }
263