diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2007-07-19 04:49:23 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 13:04:52 -0400 |
commit | d7e28ffe6c74416b54345d6004fd0964c115b12c (patch) | |
tree | 844beb4f400d5400098538e0c1e5f12d20a9504a /drivers/lguest/interrupts_and_traps.c | |
parent | 07ad157f6e5d228be78acd5cea0291e5d0360398 (diff) |
lguest: the host code
This is the code for the "lg.ko" module, which allows lguest guests to
be launched.
[akpm@linux-foundation.org: update for futex-new-private-futexes]
[akpm@linux-foundation.org: build fix]
[jmorris@namei.org: lguest: use hrtimers]
[akpm@linux-foundation.org: x86_64 build fix]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Andi Kleen <ak@suse.de>
Cc: Eric Dumazet <dada1@cosmosbay.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/lguest/interrupts_and_traps.c')
-rw-r--r-- | drivers/lguest/interrupts_and_traps.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c new file mode 100644 index 000000000000..d9de5bbc613f --- /dev/null +++ b/drivers/lguest/interrupts_and_traps.c | |||
@@ -0,0 +1,268 @@ | |||
1 | #include <linux/uaccess.h> | ||
2 | #include "lg.h" | ||
3 | |||
4 | static unsigned long idt_address(u32 lo, u32 hi) | ||
5 | { | ||
6 | return (lo & 0x0000FFFF) | (hi & 0xFFFF0000); | ||
7 | } | ||
8 | |||
9 | static int idt_type(u32 lo, u32 hi) | ||
10 | { | ||
11 | return (hi >> 8) & 0xF; | ||
12 | } | ||
13 | |||
14 | static int idt_present(u32 lo, u32 hi) | ||
15 | { | ||
16 | return (hi & 0x8000); | ||
17 | } | ||
18 | |||
19 | static void push_guest_stack(struct lguest *lg, unsigned long *gstack, u32 val) | ||
20 | { | ||
21 | *gstack -= 4; | ||
22 | lgwrite_u32(lg, *gstack, val); | ||
23 | } | ||
24 | |||
25 | static void set_guest_interrupt(struct lguest *lg, u32 lo, u32 hi, int has_err) | ||
26 | { | ||
27 | unsigned long gstack; | ||
28 | u32 eflags, ss, irq_enable; | ||
29 | |||
30 | /* If they want a ring change, we use new stack and push old ss/esp */ | ||
31 | if ((lg->regs->ss&0x3) != GUEST_PL) { | ||
32 | gstack = guest_pa(lg, lg->esp1); | ||
33 | ss = lg->ss1; | ||
34 | push_guest_stack(lg, &gstack, lg->regs->ss); | ||
35 | push_guest_stack(lg, &gstack, lg->regs->esp); | ||
36 | } else { | ||
37 | gstack = guest_pa(lg, lg->regs->esp); | ||
38 | ss = lg->regs->ss; | ||
39 | } | ||
40 | |||
41 | /* We use IF bit in eflags to indicate whether irqs were disabled | ||
42 | (it's always 0, since irqs are enabled when guest is running). */ | ||
43 | eflags = lg->regs->eflags; | ||
44 | if (get_user(irq_enable, &lg->lguest_data->irq_enabled)) | ||
45 | irq_enable = 0; | ||
46 | eflags |= (irq_enable & X86_EFLAGS_IF); | ||
47 | |||
48 | push_guest_stack(lg, &gstack, eflags); | ||
49 | push_guest_stack(lg, &gstack, lg->regs->cs); | ||
50 | push_guest_stack(lg, &gstack, lg->regs->eip); | ||
51 | |||
52 | if (has_err) | ||
53 | push_guest_stack(lg, &gstack, lg->regs->errcode); | ||
54 | |||
55 | /* Change the real stack so switcher returns to trap handler */ | ||
56 | lg->regs->ss = ss; | ||
57 | lg->regs->esp = gstack + lg->page_offset; | ||
58 | lg->regs->cs = (__KERNEL_CS|GUEST_PL); | ||
59 | lg->regs->eip = idt_address(lo, hi); | ||
60 | |||
61 | /* Disable interrupts for an interrupt gate. */ | ||
62 | if (idt_type(lo, hi) == 0xE) | ||
63 | if (put_user(0, &lg->lguest_data->irq_enabled)) | ||
64 | kill_guest(lg, "Disabling interrupts"); | ||
65 | } | ||
66 | |||
67 | void maybe_do_interrupt(struct lguest *lg) | ||
68 | { | ||
69 | unsigned int irq; | ||
70 | DECLARE_BITMAP(blk, LGUEST_IRQS); | ||
71 | struct desc_struct *idt; | ||
72 | |||
73 | if (!lg->lguest_data) | ||
74 | return; | ||
75 | |||
76 | /* Mask out any interrupts they have blocked. */ | ||
77 | if (copy_from_user(&blk, lg->lguest_data->blocked_interrupts, | ||
78 | sizeof(blk))) | ||
79 | return; | ||
80 | |||
81 | bitmap_andnot(blk, lg->irqs_pending, blk, LGUEST_IRQS); | ||
82 | |||
83 | irq = find_first_bit(blk, LGUEST_IRQS); | ||
84 | if (irq >= LGUEST_IRQS) | ||
85 | return; | ||
86 | |||
87 | if (lg->regs->eip >= lg->noirq_start && lg->regs->eip < lg->noirq_end) | ||
88 | return; | ||
89 | |||
90 | /* If they're halted, we re-enable interrupts. */ | ||
91 | if (lg->halted) { | ||
92 | /* Re-enable interrupts. */ | ||
93 | if (put_user(X86_EFLAGS_IF, &lg->lguest_data->irq_enabled)) | ||
94 | kill_guest(lg, "Re-enabling interrupts"); | ||
95 | lg->halted = 0; | ||
96 | } else { | ||
97 | /* Maybe they have interrupts disabled? */ | ||
98 | u32 irq_enabled; | ||
99 | if (get_user(irq_enabled, &lg->lguest_data->irq_enabled)) | ||
100 | irq_enabled = 0; | ||
101 | if (!irq_enabled) | ||
102 | return; | ||
103 | } | ||
104 | |||
105 | idt = &lg->idt[FIRST_EXTERNAL_VECTOR+irq]; | ||
106 | if (idt_present(idt->a, idt->b)) { | ||
107 | clear_bit(irq, lg->irqs_pending); | ||
108 | set_guest_interrupt(lg, idt->a, idt->b, 0); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | static int has_err(unsigned int trap) | ||
113 | { | ||
114 | return (trap == 8 || (trap >= 10 && trap <= 14) || trap == 17); | ||
115 | } | ||
116 | |||
117 | int deliver_trap(struct lguest *lg, unsigned int num) | ||
118 | { | ||
119 | u32 lo = lg->idt[num].a, hi = lg->idt[num].b; | ||
120 | |||
121 | if (!idt_present(lo, hi)) | ||
122 | return 0; | ||
123 | set_guest_interrupt(lg, lo, hi, has_err(num)); | ||
124 | return 1; | ||
125 | } | ||
126 | |||
127 | static int direct_trap(const struct lguest *lg, | ||
128 | const struct desc_struct *trap, | ||
129 | unsigned int num) | ||
130 | { | ||
131 | /* Hardware interrupts don't go to guest (except syscall). */ | ||
132 | if (num >= FIRST_EXTERNAL_VECTOR && num != SYSCALL_VECTOR) | ||
133 | return 0; | ||
134 | |||
135 | /* We intercept page fault (demand shadow paging & cr2 saving) | ||
136 | protection fault (in/out emulation) and device not | ||
137 | available (TS handling), and hypercall */ | ||
138 | if (num == 14 || num == 13 || num == 7 || num == LGUEST_TRAP_ENTRY) | ||
139 | return 0; | ||
140 | |||
141 | /* Interrupt gates (0xE) or not present (0x0) can't go direct. */ | ||
142 | return idt_type(trap->a, trap->b) == 0xF; | ||
143 | } | ||
144 | |||
145 | void pin_stack_pages(struct lguest *lg) | ||
146 | { | ||
147 | unsigned int i; | ||
148 | |||
149 | for (i = 0; i < lg->stack_pages; i++) | ||
150 | pin_page(lg, lg->esp1 - i * PAGE_SIZE); | ||
151 | } | ||
152 | |||
153 | void guest_set_stack(struct lguest *lg, u32 seg, u32 esp, unsigned int pages) | ||
154 | { | ||
155 | /* You cannot have a stack segment with priv level 0. */ | ||
156 | if ((seg & 0x3) != GUEST_PL) | ||
157 | kill_guest(lg, "bad stack segment %i", seg); | ||
158 | if (pages > 2) | ||
159 | kill_guest(lg, "bad stack pages %u", pages); | ||
160 | lg->ss1 = seg; | ||
161 | lg->esp1 = esp; | ||
162 | lg->stack_pages = pages; | ||
163 | pin_stack_pages(lg); | ||
164 | } | ||
165 | |||
166 | /* Set up trap in IDT. */ | ||
167 | static void set_trap(struct lguest *lg, struct desc_struct *trap, | ||
168 | unsigned int num, u32 lo, u32 hi) | ||
169 | { | ||
170 | u8 type = idt_type(lo, hi); | ||
171 | |||
172 | if (!idt_present(lo, hi)) { | ||
173 | trap->a = trap->b = 0; | ||
174 | return; | ||
175 | } | ||
176 | |||
177 | if (type != 0xE && type != 0xF) | ||
178 | kill_guest(lg, "bad IDT type %i", type); | ||
179 | |||
180 | trap->a = ((__KERNEL_CS|GUEST_PL)<<16) | (lo&0x0000FFFF); | ||
181 | trap->b = (hi&0xFFFFEF00); | ||
182 | } | ||
183 | |||
184 | void load_guest_idt_entry(struct lguest *lg, unsigned int num, u32 lo, u32 hi) | ||
185 | { | ||
186 | /* Guest never handles: NMI, doublefault, hypercall, spurious irq. */ | ||
187 | if (num == 2 || num == 8 || num == 15 || num == LGUEST_TRAP_ENTRY) | ||
188 | return; | ||
189 | |||
190 | lg->changed |= CHANGED_IDT; | ||
191 | if (num < ARRAY_SIZE(lg->idt)) | ||
192 | set_trap(lg, &lg->idt[num], num, lo, hi); | ||
193 | else if (num == SYSCALL_VECTOR) | ||
194 | set_trap(lg, &lg->syscall_idt, num, lo, hi); | ||
195 | } | ||
196 | |||
197 | static void default_idt_entry(struct desc_struct *idt, | ||
198 | int trap, | ||
199 | const unsigned long handler) | ||
200 | { | ||
201 | u32 flags = 0x8e00; | ||
202 | |||
203 | /* They can't "int" into any of them except hypercall. */ | ||
204 | if (trap == LGUEST_TRAP_ENTRY) | ||
205 | flags |= (GUEST_PL << 13); | ||
206 | |||
207 | idt->a = (LGUEST_CS<<16) | (handler&0x0000FFFF); | ||
208 | idt->b = (handler&0xFFFF0000) | flags; | ||
209 | } | ||
210 | |||
211 | void setup_default_idt_entries(struct lguest_ro_state *state, | ||
212 | const unsigned long *def) | ||
213 | { | ||
214 | unsigned int i; | ||
215 | |||
216 | for (i = 0; i < ARRAY_SIZE(state->guest_idt); i++) | ||
217 | default_idt_entry(&state->guest_idt[i], i, def[i]); | ||
218 | } | ||
219 | |||
220 | void copy_traps(const struct lguest *lg, struct desc_struct *idt, | ||
221 | const unsigned long *def) | ||
222 | { | ||
223 | unsigned int i; | ||
224 | |||
225 | /* All hardware interrupts are same whatever the guest: only the | ||
226 | * traps might be different. */ | ||
227 | for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++) { | ||
228 | if (direct_trap(lg, &lg->idt[i], i)) | ||
229 | idt[i] = lg->idt[i]; | ||
230 | else | ||
231 | default_idt_entry(&idt[i], i, def[i]); | ||
232 | } | ||
233 | i = SYSCALL_VECTOR; | ||
234 | if (direct_trap(lg, &lg->syscall_idt, i)) | ||
235 | idt[i] = lg->syscall_idt; | ||
236 | else | ||
237 | default_idt_entry(&idt[i], i, def[i]); | ||
238 | } | ||
239 | |||
240 | void guest_set_clockevent(struct lguest *lg, unsigned long delta) | ||
241 | { | ||
242 | ktime_t expires; | ||
243 | |||
244 | if (unlikely(delta == 0)) { | ||
245 | /* Clock event device is shutting down. */ | ||
246 | hrtimer_cancel(&lg->hrt); | ||
247 | return; | ||
248 | } | ||
249 | |||
250 | expires = ktime_add_ns(ktime_get_real(), delta); | ||
251 | hrtimer_start(&lg->hrt, expires, HRTIMER_MODE_ABS); | ||
252 | } | ||
253 | |||
254 | static enum hrtimer_restart clockdev_fn(struct hrtimer *timer) | ||
255 | { | ||
256 | struct lguest *lg = container_of(timer, struct lguest, hrt); | ||
257 | |||
258 | set_bit(0, lg->irqs_pending); | ||
259 | if (lg->halted) | ||
260 | wake_up_process(lg->tsk); | ||
261 | return HRTIMER_NORESTART; | ||
262 | } | ||
263 | |||
264 | void init_clockdev(struct lguest *lg) | ||
265 | { | ||
266 | hrtimer_init(&lg->hrt, CLOCK_REALTIME, HRTIMER_MODE_ABS); | ||
267 | lg->hrt.function = clockdev_fn; | ||
268 | } | ||