aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/lguest/x86/core.c
diff options
context:
space:
mode:
authorMatias Zabaljauregui <zabaljauregui@gmail.com>2009-03-14 11:37:52 -0400
committerRusty Russell <rusty@rustcorp.com.au>2009-03-30 07:25:24 -0400
commit4cd8b5e2a159f18a1507f1187b44a1acbfa6341b (patch)
tree8a774c377e2c798e119af4b3664453c6f0d1d94d /drivers/lguest/x86/core.c
parentb7ff99ea53cd16de8f6166c0e98f19a7c6ca67ee (diff)
lguest: use KVM hypercalls
Impact: cleanup This patch allow us to use KVM hypercalls Signed-off-by: Matias Zabaljauregui <zabaljauregui at gmail.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/lguest/x86/core.c')
-rw-r--r--drivers/lguest/x86/core.c62
1 files changed, 61 insertions, 1 deletions
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index bf7942327bd..a6b717644be 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -290,6 +290,57 @@ static int emulate_insn(struct lg_cpu *cpu)
290 return 1; 290 return 1;
291} 291}
292 292
293/* Our hypercalls mechanism used to be based on direct software interrupts.
294 * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to
295 * change over to using kvm hypercalls.
296 *
297 * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid
298 * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be
299 * an *emulation approach*: if the fault was really produced by an hypercall
300 * (is_hypercall() does exactly this check), we can just call the corresponding
301 * hypercall host implementation function.
302 *
303 * But these invalid opcode faults are notably slower than software interrupts.
304 * So we implemented the *patching (or rewriting) approach*: every time we hit
305 * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f"
306 * opcode, so next time the Guest calls this hypercall it will use the
307 * faster trap mechanism.
308 *
309 * Matias even benchmarked it to convince you: this shows the average cycle
310 * cost of a hypercall. For each alternative solution mentioned above we've
311 * made 5 runs of the benchmark:
312 *
313 * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898
314 * 2) emulation technique: 3410, 3681, 3466, 3392, 3780
315 * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884
316 *
317 * One two-line function is worth a 20% hypercall speed boost!
318 */
319static void rewrite_hypercall(struct lg_cpu *cpu)
320{
321 /* This are the opcodes we use to patch the Guest. The opcode for "int
322 * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we
323 * complete the sequence with a NOP (0x90). */
324 u8 insn[3] = {0xcd, 0x1f, 0x90};
325
326 __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn));
327}
328
329static bool is_hypercall(struct lg_cpu *cpu)
330{
331 u8 insn[3];
332
333 /* This must be the Guest kernel trying to do something.
334 * The bottom two bits of the CS segment register are the privilege
335 * level. */
336 if ((cpu->regs->cs & 3) != GUEST_PL)
337 return false;
338
339 /* Is it a vmcall? */
340 __lgread(cpu, insn, guest_pa(cpu, cpu->regs->eip), sizeof(insn));
341 return insn[0] == 0x0f && insn[1] == 0x01 && insn[2] == 0xc1;
342}
343
293/*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ 344/*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */
294void lguest_arch_handle_trap(struct lg_cpu *cpu) 345void lguest_arch_handle_trap(struct lg_cpu *cpu)
295{ 346{
@@ -337,7 +388,7 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu)
337 break; 388 break;
338 case 32 ... 255: 389 case 32 ... 255:
339 /* These values mean a real interrupt occurred, in which case 390 /* These values mean a real interrupt occurred, in which case
340 * the Host handler has already been run. We just do a 391 * the Host handler has already been run. We just do a
341 * friendly check if another process should now be run, then 392 * friendly check if another process should now be run, then
342 * return to run the Guest again */ 393 * return to run the Guest again */
343 cond_resched(); 394 cond_resched();
@@ -347,6 +398,15 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu)
347 * up the pointer now to indicate a hypercall is pending. */ 398 * up the pointer now to indicate a hypercall is pending. */
348 cpu->hcall = (struct hcall_args *)cpu->regs; 399 cpu->hcall = (struct hcall_args *)cpu->regs;
349 return; 400 return;
401 case 6:
402 /* kvm hypercalls trigger an invalid opcode fault (6).
403 * We need to check if ring == GUEST_PL and
404 * faulting instruction == vmcall. */
405 if (is_hypercall(cpu)) {
406 rewrite_hypercall(cpu);
407 return;
408 }
409 break;
350 } 410 }
351 411
352 /* We didn't handle the trap, so it needs to go to the Guest. */ 412 /* We didn't handle the trap, so it needs to go to the Guest. */