diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2007-10-21 21:01:54 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2007-10-23 01:49:50 -0400 |
commit | 34b8867a034364ca33d0adb3a1c5b9982903c719 (patch) | |
tree | 7b6385b3985e7bdcca91103d01dea9f707e8b567 /arch/x86/lguest/i386_head.S | |
parent | c37ae93d597fc63bae979db76b527dcc7740dc9d (diff) |
Move lguest guest support to arch/x86.
Lguest has two sides: host support (to launch guests) and guest
support (replacement boot path and paravirt_ops). This moves the
guest side to arch/x86/lguest where it's closer to related code.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Andi Kleen <ak@suse.de>
Diffstat (limited to 'arch/x86/lguest/i386_head.S')
-rw-r--r-- | arch/x86/lguest/i386_head.S | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/arch/x86/lguest/i386_head.S b/arch/x86/lguest/i386_head.S new file mode 100644 index 000000000000..6d7a74f07c41 --- /dev/null +++ b/arch/x86/lguest/i386_head.S | |||
@@ -0,0 +1,93 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | #include <linux/lguest.h> | ||
3 | #include <asm/asm-offsets.h> | ||
4 | #include <asm/thread_info.h> | ||
5 | #include <asm/processor-flags.h> | ||
6 | |||
7 | /*G:020 This is where we begin: we have a magic signature which the launcher | ||
8 | * looks for. The plan is that the Linux boot protocol will be extended with a | ||
9 | * "platform type" field which will guide us here from the normal entry point, | ||
10 | * but for the moment this suffices. The normal boot code uses %esi for the | ||
11 | * boot header, so we do too. We convert it to a virtual address by adding | ||
12 | * PAGE_OFFSET, and hand it to lguest_init() as its argument (ie. %eax). | ||
13 | * | ||
14 | * The .section line puts this code in .init.text so it will be discarded after | ||
15 | * boot. */ | ||
16 | .section .init.text, "ax", @progbits | ||
17 | .ascii "GenuineLguest" | ||
18 | /* Set up initial stack. */ | ||
19 | movl $(init_thread_union+THREAD_SIZE),%esp | ||
20 | movl %esi, %eax | ||
21 | addl $__PAGE_OFFSET, %eax | ||
22 | jmp lguest_init | ||
23 | |||
24 | /*G:055 We create a macro which puts the assembler code between lgstart_ and | ||
25 | * lgend_ markers. These templates are put in the .text section: they can't be | ||
26 | * discarded after boot as we may need to patch modules, too. */ | ||
27 | .text | ||
28 | #define LGUEST_PATCH(name, insns...) \ | ||
29 | lgstart_##name: insns; lgend_##name:; \ | ||
30 | .globl lgstart_##name; .globl lgend_##name | ||
31 | |||
32 | LGUEST_PATCH(cli, movl $0, lguest_data+LGUEST_DATA_irq_enabled) | ||
33 | LGUEST_PATCH(sti, movl $X86_EFLAGS_IF, lguest_data+LGUEST_DATA_irq_enabled) | ||
34 | LGUEST_PATCH(popf, movl %eax, lguest_data+LGUEST_DATA_irq_enabled) | ||
35 | LGUEST_PATCH(pushf, movl lguest_data+LGUEST_DATA_irq_enabled, %eax) | ||
36 | /*:*/ | ||
37 | |||
38 | /* These demark the EIP range where host should never deliver interrupts. */ | ||
39 | .global lguest_noirq_start | ||
40 | .global lguest_noirq_end | ||
41 | |||
42 | /*M:004 When the Host reflects a trap or injects an interrupt into the Guest, | ||
43 | * it sets the eflags interrupt bit on the stack based on | ||
44 | * lguest_data.irq_enabled, so the Guest iret logic does the right thing when | ||
45 | * restoring it. However, when the Host sets the Guest up for direct traps, | ||
46 | * such as system calls, the processor is the one to push eflags onto the | ||
47 | * stack, and the interrupt bit will be 1 (in reality, interrupts are always | ||
48 | * enabled in the Guest). | ||
49 | * | ||
50 | * This turns out to be harmless: the only trap which should happen under Linux | ||
51 | * with interrupts disabled is Page Fault (due to our lazy mapping of vmalloc | ||
52 | * regions), which has to be reflected through the Host anyway. If another | ||
53 | * trap *does* go off when interrupts are disabled, the Guest will panic, and | ||
54 | * we'll never get to this iret! :*/ | ||
55 | |||
56 | /*G:045 There is one final paravirt_op that the Guest implements, and glancing | ||
57 | * at it you can see why I left it to last. It's *cool*! It's in *assembler*! | ||
58 | * | ||
59 | * The "iret" instruction is used to return from an interrupt or trap. The | ||
60 | * stack looks like this: | ||
61 | * old address | ||
62 | * old code segment & privilege level | ||
63 | * old processor flags ("eflags") | ||
64 | * | ||
65 | * The "iret" instruction pops those values off the stack and restores them all | ||
66 | * at once. The only problem is that eflags includes the Interrupt Flag which | ||
67 | * the Guest can't change: the CPU will simply ignore it when we do an "iret". | ||
68 | * So we have to copy eflags from the stack to lguest_data.irq_enabled before | ||
69 | * we do the "iret". | ||
70 | * | ||
71 | * There are two problems with this: firstly, we need to use a register to do | ||
72 | * the copy and secondly, the whole thing needs to be atomic. The first | ||
73 | * problem is easy to solve: push %eax on the stack so we can use it, and then | ||
74 | * restore it at the end just before the real "iret". | ||
75 | * | ||
76 | * The second is harder: copying eflags to lguest_data.irq_enabled will turn | ||
77 | * interrupts on before we're finished, so we could be interrupted before we | ||
78 | * return to userspace or wherever. Our solution to this is to surround the | ||
79 | * code with lguest_noirq_start: and lguest_noirq_end: labels. We tell the | ||
80 | * Host that it is *never* to interrupt us there, even if interrupts seem to be | ||
81 | * enabled. */ | ||
82 | ENTRY(lguest_iret) | ||
83 | pushl %eax | ||
84 | movl 12(%esp), %eax | ||
85 | lguest_noirq_start: | ||
86 | /* Note the %ss: segment prefix here. Normal data accesses use the | ||
87 | * "ds" segment, but that will have already been restored for whatever | ||
88 | * we're returning to (such as userspace): we can't trust it. The %ss: | ||
89 | * prefix makes sure we use the stack segment, which is still valid. */ | ||
90 | movl %eax,%ss:lguest_data+LGUEST_DATA_irq_enabled | ||
91 | popl %eax | ||
92 | iret | ||
93 | lguest_noirq_end: | ||