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