aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2011-07-22 01:09:49 -0400
committerRusty Russell <rusty@rustcorp.com.au>2011-07-22 01:09:49 -0400
commit6d7a5d1ea34495ecb1d608f0e40afba7776ee408 (patch)
tree23213877e952ad122888a502d07ed112263069d6
parent7e1941444f808d8001aa3b63588150c516321a3c (diff)
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>
-rw-r--r--drivers/lguest/interrupts_and_traps.c6
-rw-r--r--drivers/lguest/x86/core.c77
2 files changed, 2 insertions, 81 deletions
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c
index daaf86631647..f0c171506371 100644
--- a/drivers/lguest/interrupts_and_traps.c
+++ b/drivers/lguest/interrupts_and_traps.c
@@ -375,11 +375,9 @@ static bool direct_trap(unsigned int num)
375 /* 375 /*
376 * The Host needs to see page faults (for shadow paging and to save the 376 * The Host needs to see page faults (for shadow paging and to save the
377 * fault address), general protection faults (in/out emulation) and 377 * fault address), general protection faults (in/out emulation) and
378 * device not available (TS handling), invalid opcode fault (kvm hcall), 378 * device not available (TS handling) and of course, the hypercall trap.
379 * and of course, the hypercall trap.
380 */ 379 */
381 return num != 14 && num != 13 && num != 7 && 380 return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY;
382 num != 6 && num != LGUEST_TRAP_ENTRY;
383} 381}
384/*:*/ 382/*:*/
385 383
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index 9f1659c3d1f3..ec0cdfc04e78 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -352,69 +352,6 @@ static int emulate_insn(struct lg_cpu *cpu)
352 return 1; 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 */
382static 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
401static 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/*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ 355/*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */
419void lguest_arch_handle_trap(struct lg_cpu *cpu) 356void lguest_arch_handle_trap(struct lg_cpu *cpu)
420{ 357{
@@ -429,20 +366,6 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu)
429 if (emulate_insn(cpu)) 366 if (emulate_insn(cpu))
430 return; 367 return;
431 } 368 }
432 /*
433 * If KVM is active, the vmcall instruction triggers a General
434 * Protection Fault. Normally it triggers an invalid opcode
435 * fault (6):
436 */
437 case 6:
438 /*
439 * We need to check if ring == GUEST_PL and faulting
440 * instruction == vmcall.
441 */
442 if (is_hypercall(cpu)) {
443 rewrite_hypercall(cpu);
444 return;
445 }
446 break; 369 break;
447 case 14: /* We've intercepted a Page Fault. */ 370 case 14: /* We've intercepted a Page Fault. */
448 /* 371 /*