Commit | Line | Data |
---|---|---|
d7e28ffe RR |
1 | /* This code sits at 0xFFC00000 to do the low-level guest<->host switch. |
2 | ||
3 | There is are two pages above us for this CPU (struct lguest_pages). | |
4 | The second page (struct lguest_ro_state) becomes read-only after the | |
5 | context switch. The first page (the stack for traps) remains writable, | |
6 | but while we're in here, the guest cannot be running. | |
7 | */ | |
8 | #include <linux/linkage.h> | |
9 | #include <asm/asm-offsets.h> | |
10 | #include "lg.h" | |
11 | ||
12 | .text | |
13 | ENTRY(start_switcher_text) | |
14 | ||
15 | /* %eax points to lguest pages for this CPU. %ebx contains cr3 value. | |
16 | All normal registers can be clobbered! */ | |
17 | ENTRY(switch_to_guest) | |
18 | /* Save host segments on host stack. */ | |
19 | pushl %es | |
20 | pushl %ds | |
21 | pushl %gs | |
22 | pushl %fs | |
23 | /* With CONFIG_FRAME_POINTER, gcc doesn't let us clobber this! */ | |
24 | pushl %ebp | |
25 | /* Save host stack. */ | |
26 | movl %esp, LGUEST_PAGES_host_sp(%eax) | |
27 | /* Switch to guest stack: if we get NMI we expect to be there. */ | |
28 | movl %eax, %edx | |
29 | addl $LGUEST_PAGES_regs, %edx | |
30 | movl %edx, %esp | |
31 | /* Switch to guest's GDT, IDT. */ | |
32 | lgdt LGUEST_PAGES_guest_gdt_desc(%eax) | |
33 | lidt LGUEST_PAGES_guest_idt_desc(%eax) | |
34 | /* Switch to guest's TSS while GDT still writable. */ | |
35 | movl $(GDT_ENTRY_TSS*8), %edx | |
36 | ltr %dx | |
37 | /* Set host's TSS GDT entry to available (clear byte 5 bit 2). */ | |
38 | movl (LGUEST_PAGES_host_gdt_desc+2)(%eax), %edx | |
39 | andb $0xFD, (GDT_ENTRY_TSS*8 + 5)(%edx) | |
40 | /* Switch to guest page tables: lguest_pages->state now read-only. */ | |
41 | movl %ebx, %cr3 | |
42 | /* Restore guest regs */ | |
43 | popl %ebx | |
44 | popl %ecx | |
45 | popl %edx | |
46 | popl %esi | |
47 | popl %edi | |
48 | popl %ebp | |
49 | popl %gs | |
50 | popl %eax | |
51 | popl %fs | |
52 | popl %ds | |
53 | popl %es | |
54 | /* Skip error code and trap number */ | |
55 | addl $8, %esp | |
56 | iret | |
57 | ||
58 | #define SWITCH_TO_HOST \ | |
59 | /* Save guest state */ \ | |
60 | pushl %es; \ | |
61 | pushl %ds; \ | |
62 | pushl %fs; \ | |
63 | pushl %eax; \ | |
64 | pushl %gs; \ | |
65 | pushl %ebp; \ | |
66 | pushl %edi; \ | |
67 | pushl %esi; \ | |
68 | pushl %edx; \ | |
69 | pushl %ecx; \ | |
70 | pushl %ebx; \ | |
71 | /* Load lguest ds segment for convenience. */ \ | |
72 | movl $(LGUEST_DS), %eax; \ | |
73 | movl %eax, %ds; \ | |
74 | /* Figure out where we are, based on stack (at top of regs). */ \ | |
75 | movl %esp, %eax; \ | |
76 | subl $LGUEST_PAGES_regs, %eax; \ | |
77 | /* Put trap number in %ebx before we switch cr3 and lose it. */ \ | |
78 | movl LGUEST_PAGES_regs_trapnum(%eax), %ebx; \ | |
79 | /* Switch to host page tables (host GDT, IDT and stack are in host \ | |
80 | mem, so need this first) */ \ | |
81 | movl LGUEST_PAGES_host_cr3(%eax), %edx; \ | |
82 | movl %edx, %cr3; \ | |
83 | /* Set guest's TSS to available (clear byte 5 bit 2). */ \ | |
84 | andb $0xFD, (LGUEST_PAGES_guest_gdt+GDT_ENTRY_TSS*8+5)(%eax); \ | |
85 | /* Switch to host's GDT & IDT. */ \ | |
86 | lgdt LGUEST_PAGES_host_gdt_desc(%eax); \ | |
87 | lidt LGUEST_PAGES_host_idt_desc(%eax); \ | |
88 | /* Switch to host's stack. */ \ | |
89 | movl LGUEST_PAGES_host_sp(%eax), %esp; \ | |
90 | /* Switch to host's TSS */ \ | |
91 | movl $(GDT_ENTRY_TSS*8), %edx; \ | |
92 | ltr %dx; \ | |
93 | popl %ebp; \ | |
94 | popl %fs; \ | |
95 | popl %gs; \ | |
96 | popl %ds; \ | |
97 | popl %es | |
98 | ||
99 | /* Return to run_guest_once. */ | |
100 | return_to_host: | |
101 | SWITCH_TO_HOST | |
102 | iret | |
103 | ||
104 | deliver_to_host: | |
105 | SWITCH_TO_HOST | |
106 | /* Decode IDT and jump to hosts' irq handler. When that does iret, it | |
107 | * will return to run_guest_once. This is a feature. */ | |
108 | movl (LGUEST_PAGES_host_idt_desc+2)(%eax), %edx | |
109 | leal (%edx,%ebx,8), %eax | |
110 | movzwl (%eax),%edx | |
111 | movl 4(%eax), %eax | |
112 | xorw %ax, %ax | |
113 | orl %eax, %edx | |
114 | jmp *%edx | |
115 | ||
116 | /* Real hardware interrupts are delivered straight to the host. Others | |
117 | cause us to return to run_guest_once so it can decide what to do. Note | |
118 | that some of these are overridden by the guest to deliver directly, and | |
119 | never enter here (see load_guest_idt_entry). */ | |
120 | .macro IRQ_STUB N TARGET | |
121 | .data; .long 1f; .text; 1: | |
122 | /* Make an error number for most traps, which don't have one. */ | |
123 | .if (\N <> 8) && (\N < 10 || \N > 14) && (\N <> 17) | |
124 | pushl $0 | |
125 | .endif | |
126 | pushl $\N | |
127 | jmp \TARGET | |
128 | ALIGN | |
129 | .endm | |
130 | ||
131 | .macro IRQ_STUBS FIRST LAST TARGET | |
132 | irq=\FIRST | |
133 | .rept \LAST-\FIRST+1 | |
134 | IRQ_STUB irq \TARGET | |
135 | irq=irq+1 | |
136 | .endr | |
137 | .endm | |
138 | ||
139 | /* We intercept every interrupt, because we may need to switch back to | |
140 | * host. Unfortunately we can't tell them apart except by entry | |
141 | * point, so we need 256 entry points. | |
142 | */ | |
143 | .data | |
144 | .global default_idt_entries | |
145 | default_idt_entries: | |
146 | .text | |
147 | IRQ_STUBS 0 1 return_to_host /* First two traps */ | |
148 | IRQ_STUB 2 handle_nmi /* NMI */ | |
149 | IRQ_STUBS 3 31 return_to_host /* Rest of traps */ | |
150 | IRQ_STUBS 32 127 deliver_to_host /* Real interrupts */ | |
151 | IRQ_STUB 128 return_to_host /* System call (overridden) */ | |
152 | IRQ_STUBS 129 255 deliver_to_host /* Other real interrupts */ | |
153 | ||
154 | /* We ignore NMI and return. */ | |
155 | handle_nmi: | |
156 | addl $8, %esp | |
157 | iret | |
158 | ||
159 | ENTRY(end_switcher_text) |