Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lguest: don't rewrite vmcall instructions

Now we no longer use vmcall, we don't need to rewrite it in the Guest.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>

+2 -81
+2 -4
drivers/lguest/interrupts_and_traps.c
··· 375 375 /* 376 376 * The Host needs to see page faults (for shadow paging and to save the 377 377 * fault address), general protection faults (in/out emulation) and 378 - * device not available (TS handling), invalid opcode fault (kvm hcall), 379 - * and of course, the hypercall trap. 378 + * device not available (TS handling) and of course, the hypercall trap. 380 379 */ 381 - return num != 14 && num != 13 && num != 7 && 382 - num != 6 && num != LGUEST_TRAP_ENTRY; 380 + return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY; 383 381 } 384 382 /*:*/ 385 383
-77
drivers/lguest/x86/core.c
··· 352 352 return 1; 353 353 } 354 354 355 - /* 356 - * Our hypercalls mechanism used to be based on direct software interrupts. 357 - * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to 358 - * change over to using kvm hypercalls. 359 - * 360 - * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid 361 - * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be 362 - * an *emulation approach*: if the fault was really produced by an hypercall 363 - * (is_hypercall() does exactly this check), we can just call the corresponding 364 - * hypercall host implementation function. 365 - * 366 - * But these invalid opcode faults are notably slower than software interrupts. 367 - * So we implemented the *patching (or rewriting) approach*: every time we hit 368 - * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f" 369 - * opcode, so next time the Guest calls this hypercall it will use the 370 - * faster trap mechanism. 371 - * 372 - * Matias even benchmarked it to convince you: this shows the average cycle 373 - * cost of a hypercall. For each alternative solution mentioned above we've 374 - * made 5 runs of the benchmark: 375 - * 376 - * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898 377 - * 2) emulation technique: 3410, 3681, 3466, 3392, 3780 378 - * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884 379 - * 380 - * One two-line function is worth a 20% hypercall speed boost! 381 - */ 382 - static void rewrite_hypercall(struct lg_cpu *cpu) 383 - { 384 - /* 385 - * This are the opcodes we use to patch the Guest. The opcode for "int 386 - * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we 387 - * complete the sequence with a NOP (0x90). 388 - */ 389 - u8 insn[3] = {0xcd, 0x1f, 0x90}; 390 - 391 - __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn)); 392 - /* 393 - * The above write might have caused a copy of that page to be made 394 - * (if it was read-only). We need to make sure the Guest has 395 - * up-to-date pagetables. As this doesn't happen often, we can just 396 - * drop them all. 397 - */ 398 - guest_pagetable_clear_all(cpu); 399 - } 400 - 401 - static bool is_hypercall(struct lg_cpu *cpu) 402 - { 403 - u8 insn[3]; 404 - 405 - /* 406 - * This must be the Guest kernel trying to do something. 407 - * The bottom two bits of the CS segment register are the privilege 408 - * level. 409 - */ 410 - if ((cpu->regs->cs & 3) != GUEST_PL) 411 - return false; 412 - 413 - /* Is it a vmcall? */ 414 - __lgread(cpu, insn, guest_pa(cpu, cpu->regs->eip), sizeof(insn)); 415 - return insn[0] == 0x0f && insn[1] == 0x01 && insn[2] == 0xc1; 416 - } 417 - 418 355 /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ 419 356 void lguest_arch_handle_trap(struct lg_cpu *cpu) 420 357 { ··· 365 428 if (cpu->regs->errcode == 0) { 366 429 if (emulate_insn(cpu)) 367 430 return; 368 - } 369 - /* 370 - * If KVM is active, the vmcall instruction triggers a General 371 - * Protection Fault. Normally it triggers an invalid opcode 372 - * fault (6): 373 - */ 374 - case 6: 375 - /* 376 - * We need to check if ring == GUEST_PL and faulting 377 - * instruction == vmcall. 378 - */ 379 - if (is_hypercall(cpu)) { 380 - rewrite_hypercall(cpu); 381 - return; 382 431 } 383 432 break; 384 433 case 14: /* We've intercepted a Page Fault. */