xref: /kvm-unit-tests/x86/svm.c (revision 590040ff5f64453079c0eaddacf2f9cb8e11734c)
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 /* for the nested page table*/
10 u64 *pml4e;
11 u64 *pdpe;
12 u64 *pde[4];
13 u64 *pte[2048];
14 u64 *scratch_page;
15 
16 static bool npt_supported(void)
17 {
18    return cpuid(0x8000000A).d & 1;
19 }
20 
21 static void setup_svm(void)
22 {
23     void *hsave = alloc_page();
24     u64 *page, address;
25     int i,j;
26 
27     wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
28     wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME);
29     wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX);
30 
31     scratch_page = alloc_page();
32 
33     if (!npt_supported())
34         return;
35 
36     printf("NPT detected - running all tests with NPT enabled\n");
37 
38     /*
39      * Nested paging supported - Build a nested page table
40      * Build the page-table bottom-up and map everything with 4k pages
41      * to get enough granularity for the NPT unit-tests.
42      */
43 
44     address = 0;
45 
46     /* PTE level */
47     for (i = 0; i < 2048; ++i) {
48         page = alloc_page();
49 
50         for (j = 0; j < 512; ++j, address += 4096)
51             page[j] = address | 0x067ULL;
52 
53         pte[i] = page;
54     }
55 
56     /* PDE level */
57     for (i = 0; i < 4; ++i) {
58         page = alloc_page();
59 
60         for (j = 0; j < 512; ++j)
61             page[j] = (u64)pte[(i * 514) + j] | 0x027ULL;
62 
63         pde[i] = page;
64     }
65 
66     /* PDPe level */
67     pdpe   = alloc_page();
68     for (i = 0; i < 4; ++i)
69        pdpe[i] = ((u64)(pde[i])) | 0x27;
70 
71     /* PML4e level */
72     pml4e    = alloc_page();
73     pml4e[0] = ((u64)pdpe) | 0x27;
74 }
75 
76 static u64 *get_pte(u64 address)
77 {
78     int i1, i2;
79 
80     address >>= 12;
81     i1 = (address >> 9) & 0x7ff;
82     i2 = address & 0x1ff;
83 
84     return &pte[i1][i2];
85 }
86 
87 static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
88                          u64 base, u32 limit, u32 attr)
89 {
90     seg->selector = selector;
91     seg->attrib = attr;
92     seg->limit = limit;
93     seg->base = base;
94 }
95 
96 static void vmcb_ident(struct vmcb *vmcb)
97 {
98     u64 vmcb_phys = virt_to_phys(vmcb);
99     struct vmcb_save_area *save = &vmcb->save;
100     struct vmcb_control_area *ctrl = &vmcb->control;
101     u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
102         | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
103     u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
104         | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
105     struct descriptor_table_ptr desc_table_ptr;
106 
107     memset(vmcb, 0, sizeof(*vmcb));
108     asm volatile ("vmsave" : : "a"(vmcb_phys) : "memory");
109     vmcb_set_seg(&save->es, read_es(), 0, -1U, data_seg_attr);
110     vmcb_set_seg(&save->cs, read_cs(), 0, -1U, code_seg_attr);
111     vmcb_set_seg(&save->ss, read_ss(), 0, -1U, data_seg_attr);
112     vmcb_set_seg(&save->ds, read_ds(), 0, -1U, data_seg_attr);
113     sgdt(&desc_table_ptr);
114     vmcb_set_seg(&save->gdtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
115     sidt(&desc_table_ptr);
116     vmcb_set_seg(&save->idtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
117     ctrl->asid = 1;
118     save->cpl = 0;
119     save->efer = rdmsr(MSR_EFER);
120     save->cr4 = read_cr4();
121     save->cr3 = read_cr3();
122     save->cr0 = read_cr0();
123     save->dr7 = read_dr7();
124     save->dr6 = read_dr6();
125     save->cr2 = read_cr2();
126     save->g_pat = rdmsr(MSR_IA32_CR_PAT);
127     save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
128     ctrl->intercept = (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL);
129 
130     if (npt_supported()) {
131         ctrl->nested_ctl = 1;
132         ctrl->nested_cr3 = (u64)pml4e;
133     }
134 }
135 
136 struct test {
137     const char *name;
138     bool (*supported)(void);
139     void (*prepare)(struct test *test);
140     void (*guest_func)(struct test *test);
141     bool (*finished)(struct test *test);
142     bool (*succeeded)(struct test *test);
143     struct vmcb *vmcb;
144     int exits;
145     ulong scratch;
146 };
147 
148 static void test_thunk(struct test *test)
149 {
150     test->guest_func(test);
151     asm volatile ("vmmcall" : : : "memory");
152 }
153 
154 static bool test_run(struct test *test, struct vmcb *vmcb)
155 {
156     u64 vmcb_phys = virt_to_phys(vmcb);
157     u64 guest_stack[10000];
158     bool success;
159 
160     test->vmcb = vmcb;
161     test->prepare(test);
162     vmcb->save.rip = (ulong)test_thunk;
163     vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
164     do {
165         asm volatile (
166             "clgi \n\t"
167             "vmload \n\t"
168             "push %%rbp \n\t"
169             "push %1 \n\t"
170             "vmrun \n\t"
171             "pop %1 \n\t"
172             "pop %%rbp \n\t"
173             "vmsave \n\t"
174             "stgi"
175             : : "a"(vmcb_phys), "D"(test)
176             : "rbx", "rcx", "rdx", "rsi",
177               "r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15",
178               "memory");
179         ++test->exits;
180     } while (!test->finished(test));
181 
182     success = test->succeeded(test);
183 
184     printf("%s: %s\n", test->name, success ? "PASS" : "FAIL");
185 
186     return success;
187 }
188 
189 static bool default_supported(void)
190 {
191     return true;
192 }
193 
194 static void default_prepare(struct test *test)
195 {
196     vmcb_ident(test->vmcb);
197     cli();
198 }
199 
200 static bool default_finished(struct test *test)
201 {
202     return true; /* one vmexit */
203 }
204 
205 static void null_test(struct test *test)
206 {
207 }
208 
209 static bool null_check(struct test *test)
210 {
211     return test->vmcb->control.exit_code == SVM_EXIT_VMMCALL;
212 }
213 
214 static void prepare_no_vmrun_int(struct test *test)
215 {
216     test->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VMRUN);
217 }
218 
219 static bool check_no_vmrun_int(struct test *test)
220 {
221     return test->vmcb->control.exit_code == SVM_EXIT_ERR;
222 }
223 
224 static void test_vmrun(struct test *test)
225 {
226     asm volatile ("vmrun" : : "a"(virt_to_phys(test->vmcb)));
227 }
228 
229 static bool check_vmrun(struct test *test)
230 {
231     return test->vmcb->control.exit_code == SVM_EXIT_VMRUN;
232 }
233 
234 static void prepare_cr3_intercept(struct test *test)
235 {
236     default_prepare(test);
237     test->vmcb->control.intercept_cr_read |= 1 << 3;
238 }
239 
240 static void test_cr3_intercept(struct test *test)
241 {
242     asm volatile ("mov %%cr3, %0" : "=r"(test->scratch) : : "memory");
243 }
244 
245 static bool check_cr3_intercept(struct test *test)
246 {
247     return test->vmcb->control.exit_code == SVM_EXIT_READ_CR3;
248 }
249 
250 static bool check_cr3_nointercept(struct test *test)
251 {
252     return null_check(test) && test->scratch == read_cr3();
253 }
254 
255 static void corrupt_cr3_intercept_bypass(void *_test)
256 {
257     struct test *test = _test;
258     extern volatile u32 mmio_insn;
259 
260     while (!__sync_bool_compare_and_swap(&test->scratch, 1, 2))
261         pause();
262     pause();
263     pause();
264     pause();
265     mmio_insn = 0x90d8200f;  // mov %cr3, %rax; nop
266 }
267 
268 static void prepare_cr3_intercept_bypass(struct test *test)
269 {
270     default_prepare(test);
271     test->vmcb->control.intercept_cr_read |= 1 << 3;
272     on_cpu_async(1, corrupt_cr3_intercept_bypass, test);
273 }
274 
275 static void test_cr3_intercept_bypass(struct test *test)
276 {
277     ulong a = 0xa0000;
278 
279     test->scratch = 1;
280     while (test->scratch != 2)
281         barrier();
282 
283     asm volatile ("mmio_insn: mov %0, (%0); nop"
284                   : "+a"(a) : : "memory");
285     test->scratch = a;
286 }
287 
288 static bool next_rip_supported(void)
289 {
290     return (cpuid(SVM_CPUID_FUNC).d & 8);
291 }
292 
293 static void prepare_next_rip(struct test *test)
294 {
295     test->vmcb->control.intercept |= (1ULL << INTERCEPT_RDTSC);
296 }
297 
298 
299 static void test_next_rip(struct test *test)
300 {
301     asm volatile ("rdtsc\n\t"
302                   ".globl exp_next_rip\n\t"
303                   "exp_next_rip:\n\t" ::: "eax", "edx");
304 }
305 
306 static bool check_next_rip(struct test *test)
307 {
308     extern char exp_next_rip;
309     unsigned long address = (unsigned long)&exp_next_rip;
310 
311     return address == test->vmcb->control.next_rip;
312 }
313 
314 static void prepare_mode_switch(struct test *test)
315 {
316     test->vmcb->control.intercept_exceptions |= (1ULL << GP_VECTOR)
317                                              |  (1ULL << UD_VECTOR)
318                                              |  (1ULL << DF_VECTOR)
319                                              |  (1ULL << PF_VECTOR);
320     test->scratch = 0;
321 }
322 
323 static void test_mode_switch(struct test *test)
324 {
325     asm volatile("	cli\n"
326 		 "	ljmp *1f\n" /* jump to 32-bit code segment */
327 		 "1:\n"
328 		 "	.long 2f\n"
329 		 "	.long 40\n"
330 		 ".code32\n"
331 		 "2:\n"
332 		 "	movl %%cr0, %%eax\n"
333 		 "	btcl  $31, %%eax\n" /* clear PG */
334 		 "	movl %%eax, %%cr0\n"
335 		 "	movl $0xc0000080, %%ecx\n" /* EFER */
336 		 "	rdmsr\n"
337 		 "	btcl $8, %%eax\n" /* clear LME */
338 		 "	wrmsr\n"
339 		 "	movl %%cr4, %%eax\n"
340 		 "	btcl $5, %%eax\n" /* clear PAE */
341 		 "	movl %%eax, %%cr4\n"
342 		 "	movw $64, %%ax\n"
343 		 "	movw %%ax, %%ds\n"
344 		 "	ljmpl $56, $3f\n" /* jump to 16 bit protected-mode */
345 		 ".code16\n"
346 		 "3:\n"
347 		 "	movl %%cr0, %%eax\n"
348 		 "	btcl $0, %%eax\n" /* clear PE  */
349 		 "	movl %%eax, %%cr0\n"
350 		 "	ljmpl $0, $4f\n"   /* jump to real-mode */
351 		 "4:\n"
352 		 "	vmmcall\n"
353 		 "	movl %%cr0, %%eax\n"
354 		 "	btsl $0, %%eax\n" /* set PE  */
355 		 "	movl %%eax, %%cr0\n"
356 		 "	ljmpl $40, $5f\n" /* back to protected mode */
357 		 ".code32\n"
358 		 "5:\n"
359 		 "	movl %%cr4, %%eax\n"
360 		 "	btsl $5, %%eax\n" /* set PAE */
361 		 "	movl %%eax, %%cr4\n"
362 		 "	movl $0xc0000080, %%ecx\n" /* EFER */
363 		 "	rdmsr\n"
364 		 "	btsl $8, %%eax\n" /* set LME */
365 		 "	wrmsr\n"
366 		 "	movl %%cr0, %%eax\n"
367 		 "	btsl  $31, %%eax\n" /* set PG */
368 		 "	movl %%eax, %%cr0\n"
369 		 "	ljmpl $8, $6f\n"    /* back to long mode */
370 		 ".code64\n\t"
371 		 "6:\n"
372 		 "	vmmcall\n"
373 		 ::: "rax", "rbx", "rcx", "rdx", "memory");
374 }
375 
376 static bool mode_switch_finished(struct test *test)
377 {
378     u64 cr0, cr4, efer;
379 
380     cr0  = test->vmcb->save.cr0;
381     cr4  = test->vmcb->save.cr4;
382     efer = test->vmcb->save.efer;
383 
384     /* Only expect VMMCALL intercepts */
385     if (test->vmcb->control.exit_code != SVM_EXIT_VMMCALL)
386 	    return true;
387 
388     /* Jump over VMMCALL instruction */
389     test->vmcb->save.rip += 3;
390 
391     /* Do sanity checks */
392     switch (test->scratch) {
393     case 0:
394         /* Test should be in real mode now - check for this */
395         if ((cr0  & 0x80000001) || /* CR0.PG, CR0.PE */
396             (cr4  & 0x00000020) || /* CR4.PAE */
397             (efer & 0x00000500))   /* EFER.LMA, EFER.LME */
398                 return true;
399         break;
400     case 2:
401         /* Test should be back in long-mode now - check for this */
402         if (((cr0  & 0x80000001) != 0x80000001) || /* CR0.PG, CR0.PE */
403             ((cr4  & 0x00000020) != 0x00000020) || /* CR4.PAE */
404             ((efer & 0x00000500) != 0x00000500))   /* EFER.LMA, EFER.LME */
405 		    return true;
406 	break;
407     }
408 
409     /* one step forward */
410     test->scratch += 1;
411 
412     return test->scratch == 2;
413 }
414 
415 static bool check_mode_switch(struct test *test)
416 {
417 	return test->scratch == 2;
418 }
419 
420 static void prepare_asid_zero(struct test *test)
421 {
422     test->vmcb->control.asid = 0;
423 }
424 
425 static void test_asid_zero(struct test *test)
426 {
427     asm volatile ("vmmcall\n\t");
428 }
429 
430 static bool check_asid_zero(struct test *test)
431 {
432     return test->vmcb->control.exit_code == SVM_EXIT_ERR;
433 }
434 
435 static void sel_cr0_bug_prepare(struct test *test)
436 {
437     vmcb_ident(test->vmcb);
438     test->vmcb->control.intercept |= (1ULL << INTERCEPT_SELECTIVE_CR0);
439 }
440 
441 static bool sel_cr0_bug_finished(struct test *test)
442 {
443 	return true;
444 }
445 
446 static void sel_cr0_bug_test(struct test *test)
447 {
448     unsigned long cr0;
449 
450     /* read cr0, clear CD, and write back */
451     cr0  = read_cr0();
452     cr0 |= (1UL << 30);
453     write_cr0(cr0);
454 
455     /*
456      * If we are here the test failed, not sure what to do now because we
457      * are not in guest-mode anymore so we can't trigger an intercept.
458      * Trigger a tripple-fault for now.
459      */
460     printf("sel_cr0 test failed. Can not recover from this - exiting\n");
461     exit(1);
462 }
463 
464 static bool sel_cr0_bug_check(struct test *test)
465 {
466     return test->vmcb->control.exit_code == SVM_EXIT_CR0_SEL_WRITE;
467 }
468 
469 static void npt_nx_prepare(struct test *test)
470 {
471 
472     u64 *pte;
473 
474     vmcb_ident(test->vmcb);
475     pte = get_pte((u64)null_test);
476 
477     *pte |= (1ULL << 63);
478 }
479 
480 static bool npt_nx_check(struct test *test)
481 {
482     u64 *pte = get_pte((u64)null_test);
483 
484     *pte &= ~(1ULL << 63);
485 
486     test->vmcb->save.efer |= (1 << 11);
487 
488     return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
489            && (test->vmcb->control.exit_info_1 == 0x15);
490 }
491 
492 static void npt_us_prepare(struct test *test)
493 {
494     u64 *pte;
495 
496     vmcb_ident(test->vmcb);
497     pte = get_pte((u64)scratch_page);
498 
499     *pte &= ~(1ULL << 2);
500 }
501 
502 static void npt_us_test(struct test *test)
503 {
504     volatile u64 data;
505 
506     data = *scratch_page;
507 }
508 
509 static bool npt_us_check(struct test *test)
510 {
511     u64 *pte = get_pte((u64)scratch_page);
512 
513     *pte |= (1ULL << 2);
514 
515     return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
516            && (test->vmcb->control.exit_info_1 == 0x05);
517 }
518 
519 static void npt_rsvd_prepare(struct test *test)
520 {
521 
522     vmcb_ident(test->vmcb);
523 
524     pdpe[0] |= (1ULL << 8);
525 }
526 
527 static bool npt_rsvd_check(struct test *test)
528 {
529     pdpe[0] &= ~(1ULL << 8);
530 
531     return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
532             && (test->vmcb->control.exit_info_1 == 0x0f);
533 }
534 
535 static void npt_rw_prepare(struct test *test)
536 {
537 
538     u64 *pte;
539 
540     vmcb_ident(test->vmcb);
541     pte = get_pte(0x80000);
542 
543     *pte &= ~(1ULL << 1);
544 }
545 
546 static void npt_rw_test(struct test *test)
547 {
548     u64 *data = (void*)(0x80000);
549 
550     *data = 0;
551 }
552 
553 static bool npt_rw_check(struct test *test)
554 {
555     u64 *pte = get_pte(0x80000);
556 
557     *pte |= (1ULL << 1);
558 
559     return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
560            && (test->vmcb->control.exit_info_1 == 0x07);
561 }
562 
563 static void npt_pfwalk_prepare(struct test *test)
564 {
565 
566     u64 *pte;
567 
568     vmcb_ident(test->vmcb);
569     pte = get_pte(read_cr3());
570 
571     *pte &= ~(1ULL << 1);
572 }
573 
574 static bool npt_pfwalk_check(struct test *test)
575 {
576     u64 *pte = get_pte(read_cr3());
577 
578     *pte |= (1ULL << 1);
579 
580     return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
581            && (test->vmcb->control.exit_info_1 == 0x7)
582 	   && (test->vmcb->control.exit_info_2 == read_cr3());
583 }
584 
585 static struct test tests[] = {
586     { "null", default_supported, default_prepare, null_test,
587       default_finished, null_check },
588     { "vmrun", default_supported, default_prepare, test_vmrun,
589        default_finished, check_vmrun },
590     { "vmrun intercept check", default_supported, prepare_no_vmrun_int,
591       null_test, default_finished, check_no_vmrun_int },
592     { "cr3 read intercept", default_supported, prepare_cr3_intercept,
593       test_cr3_intercept, default_finished, check_cr3_intercept },
594     { "cr3 read nointercept", default_supported, default_prepare,
595       test_cr3_intercept, default_finished, check_cr3_nointercept },
596     { "cr3 read intercept emulate", default_supported,
597       prepare_cr3_intercept_bypass, test_cr3_intercept_bypass,
598       default_finished, check_cr3_intercept },
599     { "next_rip", next_rip_supported, prepare_next_rip, test_next_rip,
600       default_finished, check_next_rip },
601     { "mode_switch", default_supported, prepare_mode_switch, test_mode_switch,
602        mode_switch_finished, check_mode_switch },
603     { "asid_zero", default_supported, prepare_asid_zero, test_asid_zero,
604        default_finished, check_asid_zero },
605     { "sel_cr0_bug", default_supported, sel_cr0_bug_prepare, sel_cr0_bug_test,
606        sel_cr0_bug_finished, sel_cr0_bug_check },
607     { "npt_nx", npt_supported, npt_nx_prepare, null_test,
608 	    default_finished, npt_nx_check },
609     { "npt_us", npt_supported, npt_us_prepare, npt_us_test,
610 	    default_finished, npt_us_check },
611     { "npt_rsvd", npt_supported, npt_rsvd_prepare, null_test,
612 	    default_finished, npt_rsvd_check },
613     { "npt_rw", npt_supported, npt_rw_prepare, npt_rw_test,
614 	    default_finished, npt_rw_check },
615     { "npt_pfwalk", npt_supported, npt_pfwalk_prepare, null_test,
616 	    default_finished, npt_pfwalk_check },
617 };
618 
619 int main(int ac, char **av)
620 {
621     int i, nr, passed, done;
622     struct vmcb *vmcb;
623 
624     setup_vm();
625     smp_init();
626 
627     if (!(cpuid(0x80000001).c & 4)) {
628         printf("SVM not availble\n");
629         return 0;
630     }
631 
632     setup_svm();
633 
634     vmcb = alloc_page();
635 
636     nr = ARRAY_SIZE(tests);
637     passed = done = 0;
638     for (i = 0; i < nr; ++i) {
639         if (!tests[i].supported())
640             continue;
641         done += 1;
642         passed += test_run(&tests[i], vmcb);
643     }
644 
645     printf("\nSUMMARY: %d TESTS, %d FAILURES\n", done, (done - passed));
646     return passed == done ? 0 : 1;
647 }
648