]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/lguest/switcher.S
Fix lguest misannotation
[linux-2.6-omap-h63xx.git] / drivers / lguest / switcher.S
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)