diff options
author | Matias Zabaljauregui <zabaljauregui@gmail.com> | 2009-03-14 11:37:52 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2009-03-30 07:25:24 -0400 |
commit | 4cd8b5e2a159f18a1507f1187b44a1acbfa6341b (patch) | |
tree | 8a774c377e2c798e119af4b3664453c6f0d1d94d /drivers/lguest/x86 | |
parent | b7ff99ea53cd16de8f6166c0e98f19a7c6ca67ee (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')
-rw-r--r-- | drivers/lguest/x86/core.c | 62 |
1 files changed, 61 insertions, 1 deletions
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index bf7942327bda..a6b717644be0 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 | */ | ||
319 | static 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 | |||
329 | static 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. */ |
294 | void lguest_arch_handle_trap(struct lg_cpu *cpu) | 345 | void 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. */ |