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