diff options
Diffstat (limited to 'drivers/lguest')
-rw-r--r-- | drivers/lguest/interrupts_and_traps.c | 6 | ||||
-rw-r--r-- | drivers/lguest/x86/core.c | 77 |
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 | */ | ||
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 | /*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. */ |
419 | void lguest_arch_handle_trap(struct lg_cpu *cpu) | 356 | void 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 | /* |