xref: /kvm-unit-tests/x86/svm.c (revision 7d36db351752e29ad27eaafe3f102de7064e429b)
1 #include "svm.h"
2 #include "libcflat.h"
3 #include "processor.h"
4 #include "msr.h"
5 #include "vm.h"
6 #include "smp.h"
7 #include "types.h"
8 
9 static void setup_svm(void)
10 {
11     void *hsave = alloc_page();
12 
13     wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
14     wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME);
15 }
16 
17 static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
18                          u64 base, u32 limit, u32 attr)
19 {
20     seg->selector = selector;
21     seg->attrib = attr;
22     seg->limit = limit;
23     seg->base = base;
24 }
25 
26 static void vmcb_ident(struct vmcb *vmcb)
27 {
28     u64 vmcb_phys = virt_to_phys(vmcb);
29     struct vmcb_save_area *save = &vmcb->save;
30     struct vmcb_control_area *ctrl = &vmcb->control;
31     u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
32         | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
33     u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
34         | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
35     struct descriptor_table_ptr desc_table_ptr;
36 
37     memset(vmcb, 0, sizeof(*vmcb));
38     asm volatile ("vmsave" : : "a"(vmcb_phys) : "memory");
39     vmcb_set_seg(&save->es, read_es(), 0, -1U, data_seg_attr);
40     vmcb_set_seg(&save->cs, read_cs(), 0, -1U, code_seg_attr);
41     vmcb_set_seg(&save->ss, read_ss(), 0, -1U, data_seg_attr);
42     vmcb_set_seg(&save->ds, read_ds(), 0, -1U, data_seg_attr);
43     sgdt(&desc_table_ptr);
44     vmcb_set_seg(&save->gdtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
45     sidt(&desc_table_ptr);
46     vmcb_set_seg(&save->idtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
47     ctrl->asid = 1;
48     save->cpl = 0;
49     save->efer = rdmsr(MSR_EFER);
50     save->cr4 = read_cr4();
51     save->cr3 = read_cr3();
52     save->cr0 = read_cr0();
53     save->dr7 = read_dr7();
54     save->dr6 = read_dr6();
55     save->cr2 = read_cr2();
56     save->g_pat = rdmsr(MSR_IA32_CR_PAT);
57     save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
58     ctrl->intercept = (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL);
59 }
60 
61 struct test {
62     const char *name;
63     bool (*supported)(void);
64     void (*prepare)(struct test *test);
65     void (*guest_func)(struct test *test);
66     bool (*finished)(struct test *test);
67     bool (*succeeded)(struct test *test);
68     struct vmcb *vmcb;
69     int exits;
70     ulong scratch;
71 };
72 
73 static void test_thunk(struct test *test)
74 {
75     test->guest_func(test);
76     asm volatile ("vmmcall" : : : "memory");
77 }
78 
79 static bool test_run(struct test *test, struct vmcb *vmcb)
80 {
81     u64 vmcb_phys = virt_to_phys(vmcb);
82     u64 guest_stack[10000];
83     bool success;
84 
85     test->vmcb = vmcb;
86     test->prepare(test);
87     vmcb->save.rip = (ulong)test_thunk;
88     vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
89     do {
90         asm volatile (
91             "clgi \n\t"
92             "vmload \n\t"
93             "push %%rbp \n\t"
94             "push %1 \n\t"
95             "vmrun \n\t"
96             "pop %1 \n\t"
97             "pop %%rbp \n\t"
98             "vmsave \n\t"
99             "stgi"
100             : : "a"(vmcb_phys), "D"(test)
101             : "rbx", "rcx", "rdx", "rsi",
102               "r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15",
103               "memory");
104         ++test->exits;
105     } while (!test->finished(test));
106 
107     success = test->succeeded(test);
108 
109     printf("%s: %s\n", test->name, success ? "PASS" : "FAIL");
110 
111     return success;
112 }
113 
114 static bool default_supported(void)
115 {
116     return true;
117 }
118 
119 static void default_prepare(struct test *test)
120 {
121     vmcb_ident(test->vmcb);
122     cli();
123 }
124 
125 static bool default_finished(struct test *test)
126 {
127     return true; /* one vmexit */
128 }
129 
130 static void null_test(struct test *test)
131 {
132 }
133 
134 static bool null_check(struct test *test)
135 {
136     return test->vmcb->control.exit_code == SVM_EXIT_VMMCALL;
137 }
138 
139 static void prepare_no_vmrun_int(struct test *test)
140 {
141     test->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VMRUN);
142 }
143 
144 static bool check_no_vmrun_int(struct test *test)
145 {
146     return test->vmcb->control.exit_code == SVM_EXIT_ERR;
147 }
148 
149 static void test_vmrun(struct test *test)
150 {
151     asm volatile ("vmrun" : : "a"(virt_to_phys(test->vmcb)));
152 }
153 
154 static bool check_vmrun(struct test *test)
155 {
156     return test->vmcb->control.exit_code == SVM_EXIT_VMRUN;
157 }
158 
159 static void prepare_cr3_intercept(struct test *test)
160 {
161     default_prepare(test);
162     test->vmcb->control.intercept_cr_read |= 1 << 3;
163 }
164 
165 static void test_cr3_intercept(struct test *test)
166 {
167     asm volatile ("mov %%cr3, %0" : "=r"(test->scratch) : : "memory");
168 }
169 
170 static bool check_cr3_intercept(struct test *test)
171 {
172     return test->vmcb->control.exit_code == SVM_EXIT_READ_CR3;
173 }
174 
175 static bool check_cr3_nointercept(struct test *test)
176 {
177     return null_check(test) && test->scratch == read_cr3();
178 }
179 
180 static void corrupt_cr3_intercept_bypass(void *_test)
181 {
182     struct test *test = _test;
183     extern volatile u32 mmio_insn;
184 
185     while (!__sync_bool_compare_and_swap(&test->scratch, 1, 2))
186         pause();
187     pause();
188     pause();
189     pause();
190     mmio_insn = 0x90d8200f;  // mov %cr3, %rax; nop
191 }
192 
193 static void prepare_cr3_intercept_bypass(struct test *test)
194 {
195     default_prepare(test);
196     test->vmcb->control.intercept_cr_read |= 1 << 3;
197     on_cpu_async(1, corrupt_cr3_intercept_bypass, test);
198 }
199 
200 static void test_cr3_intercept_bypass(struct test *test)
201 {
202     ulong a = 0xa0000;
203 
204     test->scratch = 1;
205     while (test->scratch != 2)
206         barrier();
207 
208     asm volatile ("mmio_insn: mov %0, (%0); nop"
209                   : "+a"(a) : : "memory");
210     test->scratch = a;
211 }
212 
213 static bool next_rip_supported(void)
214 {
215     return (cpuid(SVM_CPUID_FUNC).d & 8);
216 }
217 
218 static void prepare_next_rip(struct test *test)
219 {
220     test->vmcb->control.intercept |= (1ULL << INTERCEPT_RDTSC);
221 }
222 
223 
224 static void test_next_rip(struct test *test)
225 {
226     asm volatile ("rdtsc\n\t"
227                   ".globl exp_next_rip\n\t"
228                   "exp_next_rip:\n\t" ::: "eax", "edx");
229 }
230 
231 static bool check_next_rip(struct test *test)
232 {
233     extern char exp_next_rip;
234     unsigned long address = (unsigned long)&exp_next_rip;
235 
236     return address == test->vmcb->control.next_rip;
237 }
238 
239 static void prepare_mode_switch(struct test *test)
240 {
241     test->vmcb->control.intercept_exceptions |= (1ULL << GP_VECTOR)
242                                              |  (1ULL << UD_VECTOR)
243                                              |  (1ULL << DF_VECTOR)
244                                              |  (1ULL << PF_VECTOR);
245     test->scratch = 0;
246 }
247 
248 static void test_mode_switch(struct test *test)
249 {
250     asm volatile("	cli\n"
251 		 "	ljmp *1f\n" /* jump to 32-bit code segment */
252 		 "1:\n"
253 		 "	.long 2f\n"
254 		 "	.long 40\n"
255 		 ".code32\n"
256 		 "2:\n"
257 		 "	movl %%cr0, %%eax\n"
258 		 "	btcl  $31, %%eax\n" /* clear PG */
259 		 "	movl %%eax, %%cr0\n"
260 		 "	movl $0xc0000080, %%ecx\n" /* EFER */
261 		 "	rdmsr\n"
262 		 "	btcl $8, %%eax\n" /* clear LME */
263 		 "	wrmsr\n"
264 		 "	movl %%cr4, %%eax\n"
265 		 "	btcl $5, %%eax\n" /* clear PAE */
266 		 "	movl %%eax, %%cr4\n"
267 		 "	movw $64, %%ax\n"
268 		 "	movw %%ax, %%ds\n"
269 		 "	ljmpl $56, $3f\n" /* jump to 16 bit protected-mode */
270 		 ".code16\n"
271 		 "3:\n"
272 		 "	movl %%cr0, %%eax\n"
273 		 "	btcl $0, %%eax\n" /* clear PE  */
274 		 "	movl %%eax, %%cr0\n"
275 		 "	ljmpl $0, $4f\n"   /* jump to real-mode */
276 		 "4:\n"
277 		 "	vmmcall\n"
278 		 "	movl %%cr0, %%eax\n"
279 		 "	btsl $0, %%eax\n" /* set PE  */
280 		 "	movl %%eax, %%cr0\n"
281 		 "	ljmpl $40, $5f\n" /* back to protected mode */
282 		 ".code32\n"
283 		 "5:\n"
284 		 "	movl %%cr4, %%eax\n"
285 		 "	btsl $5, %%eax\n" /* set PAE */
286 		 "	movl %%eax, %%cr4\n"
287 		 "	movl $0xc0000080, %%ecx\n" /* EFER */
288 		 "	rdmsr\n"
289 		 "	btsl $8, %%eax\n" /* set LME */
290 		 "	wrmsr\n"
291 		 "	movl %%cr0, %%eax\n"
292 		 "	btsl  $31, %%eax\n" /* set PG */
293 		 "	movl %%eax, %%cr0\n"
294 		 "	ljmpl $8, $6f\n"    /* back to long mode */
295 		 ".code64\n\t"
296 		 "6:\n"
297 		 "	vmmcall\n"
298 		 ::: "rax", "rbx", "rcx", "rdx", "memory");
299 }
300 
301 static bool mode_switch_finished(struct test *test)
302 {
303     u64 cr0, cr4, efer;
304 
305     cr0  = test->vmcb->save.cr0;
306     cr4  = test->vmcb->save.cr4;
307     efer = test->vmcb->save.efer;
308 
309     /* Only expect VMMCALL intercepts */
310     if (test->vmcb->control.exit_code != SVM_EXIT_VMMCALL)
311 	    return true;
312 
313     /* Jump over VMMCALL instruction */
314     test->vmcb->save.rip += 3;
315 
316     /* Do sanity checks */
317     switch (test->scratch) {
318     case 0:
319         /* Test should be in real mode now - check for this */
320         if ((cr0  & 0x80000001) || /* CR0.PG, CR0.PE */
321             (cr4  & 0x00000020) || /* CR4.PAE */
322             (efer & 0x00000500))   /* EFER.LMA, EFER.LME */
323                 return true;
324         break;
325     case 2:
326         /* Test should be back in long-mode now - check for this */
327         if (((cr0  & 0x80000001) != 0x80000001) || /* CR0.PG, CR0.PE */
328             ((cr4  & 0x00000020) != 0x00000020) || /* CR4.PAE */
329             ((efer & 0x00000500) != 0x00000500))   /* EFER.LMA, EFER.LME */
330 		    return true;
331 	break;
332     }
333 
334     /* one step forward */
335     test->scratch += 1;
336 
337     return test->scratch == 2;
338 }
339 
340 static bool check_mode_switch(struct test *test)
341 {
342 	return test->scratch == 2;
343 }
344 
345 static void prepare_asid_zero(struct test *test)
346 {
347     test->vmcb->control.asid = 0;
348 }
349 
350 static void test_asid_zero(struct test *test)
351 {
352     asm volatile ("vmmcall\n\t");
353 }
354 
355 static bool check_asid_zero(struct test *test)
356 {
357     return test->vmcb->control.exit_code == SVM_EXIT_ERR;
358 }
359 
360 static struct test tests[] = {
361     { "null", default_supported, default_prepare, null_test,
362       default_finished, null_check },
363     { "vmrun", default_supported, default_prepare, test_vmrun,
364        default_finished, check_vmrun },
365     { "vmrun intercept check", default_supported, prepare_no_vmrun_int,
366       null_test, default_finished, check_no_vmrun_int },
367     { "cr3 read intercept", default_supported, prepare_cr3_intercept,
368       test_cr3_intercept, default_finished, check_cr3_intercept },
369     { "cr3 read nointercept", default_supported, default_prepare,
370       test_cr3_intercept, default_finished, check_cr3_nointercept },
371     { "cr3 read intercept emulate", default_supported,
372       prepare_cr3_intercept_bypass, test_cr3_intercept_bypass,
373       default_finished, check_cr3_intercept },
374     { "next_rip", next_rip_supported, prepare_next_rip, test_next_rip,
375       default_finished, check_next_rip },
376     { "mode_switch", default_supported, prepare_mode_switch, test_mode_switch,
377        mode_switch_finished, check_mode_switch },
378     { "asid_zero", default_supported, prepare_asid_zero, test_asid_zero,
379        default_finished, check_asid_zero },
380 
381 };
382 
383 int main(int ac, char **av)
384 {
385     int i, nr, passed, done;
386     struct vmcb *vmcb;
387 
388     setup_vm();
389     smp_init();
390 
391     if (!(cpuid(0x80000001).c & 4)) {
392         printf("SVM not availble\n");
393         return 0;
394     }
395 
396     setup_svm();
397 
398     vmcb = alloc_page();
399 
400     nr = ARRAY_SIZE(tests);
401     passed = done = 0;
402     for (i = 0; i < nr; ++i) {
403         if (!tests[i].supported())
404             continue;
405         done += 1;
406         passed += test_run(&tests[i], vmcb);
407     }
408 
409     printf("\nSUMMARY: %d TESTS, %d FAILURES\n", done, (done - passed));
410     return passed == done ? 0 : 1;
411 }
412