xref: /kvm-unit-tests/x86/pcid.c (revision b44d84dae10c1c835e2bb8e8ec4a308dea869c1e)
1 /* Basic PCID & INVPCID functionality test */
2 
3 #include "libcflat.h"
4 #include "processor.h"
5 #include "desc.h"
6 
7 int nr_passed, nr_tests;
8 
9 #define X86_FEATURE_PCID       (1 << 17)
10 #define X86_FEATURE_INVPCID    (1 << 10)
11 
12 #define X86_CR0_PG             (1 << 31)
13 #define X86_CR3_PCID_MASK      0x00000fff
14 #define X86_CR4_PCIDE          (1 << 17)
15 
16 #define X86_IA32_EFER          0xc0000080
17 #define X86_EFER_LMA           (1UL << 8)
18 
19 struct invpcid_desc {
20     unsigned long pcid : 12;
21     unsigned long rsv  : 52;
22     unsigned long addr : 64;
23 };
24 
25 static void report(const char *name, int passed)
26 {
27     ++nr_tests;
28     if (passed)
29         ++nr_passed;
30     printf("%s: %s\n", name, passed ? "PASS" : "FAIL");
31 }
32 
33 int write_cr0_checking(unsigned long val)
34 {
35     asm volatile(ASM_TRY("1f")
36                  "mov %0, %%cr0\n\t"
37                  "1:": : "r" (val));
38     return exception_vector();
39 }
40 
41 int write_cr4_checking(unsigned long val)
42 {
43     asm volatile(ASM_TRY("1f")
44                  "mov %0, %%cr4\n\t"
45                  "1:": : "r" (val));
46     return exception_vector();
47 }
48 
49 int invpcid_checking(unsigned long type, void *desc)
50 {
51     asm volatile (ASM_TRY("1f")
52                   ".byte 0x66,0x0f,0x38,0x82,0x18 \n\t" /* invpcid (%rax), %rbx */
53                   "1:" : : "a" (desc), "b" (type));
54     return exception_vector();
55 }
56 
57 void test_cpuid_consistency(int pcid_enabled, int invpcid_enabled)
58 {
59     int passed = !(!pcid_enabled && invpcid_enabled);
60     report("CPUID consistency", passed);
61 }
62 
63 void test_pcid_enabled(void)
64 {
65     int passed = 0;
66     ulong cr0 = read_cr0(), cr3 = read_cr3(), cr4 = read_cr4();
67 
68     /* try setting CR4.PCIDE, no exception expected */
69     if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0)
70         goto report;
71 
72     /* try clearing CR0.PG when CR4.PCIDE=1, #GP expected */
73     if (write_cr0_checking(cr0 | X86_CR0_PG) != GP_VECTOR)
74         goto report;
75 
76     write_cr4(cr4);
77 
78     /* try setting CR4.PCIDE when CR3[11:0] != 0 , #GP expected */
79     write_cr3(cr3 | 0x001);
80     if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR)
81         goto report;
82 
83     passed = 1;
84 
85 report:
86     report("Test on PCID when enabled", passed);
87 }
88 
89 void test_pcid_disabled(void)
90 {
91     int passed = 0;
92     ulong cr4 = read_cr4();
93 
94     /* try setting CR4.PCIDE, #GP expected */
95     if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR)
96         goto report;
97 
98     passed = 1;
99 
100 report:
101     report("Test on PCID when disabled", passed);
102 }
103 
104 void test_invpcid_enabled(void)
105 {
106     int passed = 0;
107     ulong cr4 = read_cr4();
108     struct invpcid_desc desc;
109     desc.rsv = 0;
110 
111     /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=1
112      * no exception expected
113      */
114     desc.pcid = 0;
115     if (invpcid_checking(1, &desc) != 0)
116         goto report;
117 
118     /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=1
119      * #GP expected
120      */
121     desc.pcid = 1;
122     if (invpcid_checking(1, &desc) != GP_VECTOR)
123         goto report;
124 
125     if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0)
126         goto report;
127 
128     /* try executing invpcid when CR4.PCIDE=1
129      * no exception expected
130      */
131     desc.pcid = 10;
132     if (invpcid_checking(2, &desc) != 0)
133         goto report;
134 
135     passed = 1;
136 
137 report:
138     report("Test on INVPCID when enabled", passed);
139 }
140 
141 void test_invpcid_disabled(void)
142 {
143     int passed = 0;
144     struct invpcid_desc desc;
145 
146     /* try executing invpcid, #UD expected */
147     if (invpcid_checking(2, &desc) != UD_VECTOR)
148         goto report;
149 
150     passed = 1;
151 
152 report:
153     report("Test on INVPCID when disabled", passed);
154 }
155 
156 int main(int ac, char **av)
157 {
158     struct cpuid _cpuid;
159     int pcid_enabled = 0, invpcid_enabled = 0;
160 
161     setup_idt();
162 
163     _cpuid = cpuid(1);
164     if (_cpuid.c & X86_FEATURE_PCID)
165         pcid_enabled = 1;
166     _cpuid = cpuid_indexed(7, 0);
167     if (_cpuid.b & X86_FEATURE_INVPCID)
168         invpcid_enabled = 1;
169 
170     test_cpuid_consistency(pcid_enabled, invpcid_enabled);
171 
172     if (pcid_enabled)
173         test_pcid_enabled();
174     else
175         test_pcid_disabled();
176 
177     if (invpcid_enabled)
178         test_invpcid_enabled();
179     else
180         test_invpcid_disabled();
181 
182     printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed);
183 
184     return nr_passed == nr_tests ? 0 : 1;
185 }
186