xref: /kvm-unit-tests/x86/pcid.c (revision 6c8ef44f32334172b92116732fe2fc9d758f5dd7)
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     write_cr3(cr3);
83 
84     passed = 1;
85 
86 report:
87     report("Test on PCID when enabled", passed);
88 }
89 
90 void test_pcid_disabled(void)
91 {
92     int passed = 0;
93     ulong cr4 = read_cr4();
94 
95     /* try setting CR4.PCIDE, #GP expected */
96     if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR)
97         goto report;
98 
99     passed = 1;
100 
101 report:
102     report("Test on PCID when disabled", passed);
103 }
104 
105 void test_invpcid_enabled(void)
106 {
107     int passed = 0;
108     ulong cr4 = read_cr4();
109     struct invpcid_desc desc;
110     desc.rsv = 0;
111 
112     /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=1
113      * no exception expected
114      */
115     desc.pcid = 0;
116     if (invpcid_checking(1, &desc) != 0)
117         goto report;
118 
119     /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=1
120      * #GP expected
121      */
122     desc.pcid = 1;
123     if (invpcid_checking(1, &desc) != GP_VECTOR)
124         goto report;
125 
126     if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0)
127         goto report;
128 
129     /* try executing invpcid when CR4.PCIDE=1
130      * no exception expected
131      */
132     desc.pcid = 10;
133     if (invpcid_checking(2, &desc) != 0)
134         goto report;
135 
136     passed = 1;
137 
138 report:
139     report("Test on INVPCID when enabled", passed);
140 }
141 
142 void test_invpcid_disabled(void)
143 {
144     int passed = 0;
145     struct invpcid_desc desc;
146 
147     /* try executing invpcid, #UD expected */
148     if (invpcid_checking(2, &desc) != UD_VECTOR)
149         goto report;
150 
151     passed = 1;
152 
153 report:
154     report("Test on INVPCID when disabled", passed);
155 }
156 
157 int main(int ac, char **av)
158 {
159     struct cpuid _cpuid;
160     int pcid_enabled = 0, invpcid_enabled = 0;
161 
162     setup_idt();
163 
164     _cpuid = cpuid(1);
165     if (_cpuid.c & X86_FEATURE_PCID)
166         pcid_enabled = 1;
167     _cpuid = cpuid_indexed(7, 0);
168     if (_cpuid.b & X86_FEATURE_INVPCID)
169         invpcid_enabled = 1;
170 
171     test_cpuid_consistency(pcid_enabled, invpcid_enabled);
172 
173     if (pcid_enabled)
174         test_pcid_enabled();
175     else
176         test_pcid_disabled();
177 
178     if (invpcid_enabled)
179         test_invpcid_enabled();
180     else
181         test_invpcid_disabled();
182 
183     printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed);
184 
185     return nr_passed == nr_tests ? 0 : 1;
186 }
187