aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2015-03-31 23:02:20 -0400
committerRusty Russell <rusty@rustcorp.com.au>2015-04-01 00:07:15 -0400
commit3eebd233fcb392286d385e764eb91e90d6218cdf (patch)
treee3e1eb107605792e28ddb26db668bab580b9c625
parent012665391dfe12bf8a88d1000e627be012c39dbf (diff)
lguest: handle traps on the "interrupt suppressed" iret instruction.
Lguest's "iret" is non-atomic, as it needs to restore the interrupt state before the real iret (the guest can't actually suppress interrupts). For this reason, the host discards an interrupt if it occurs in this (1-instruction) window. We can do better, by emulating the iret execution, then immediately setting up the interrupt handler. In fact, we don't need to do much, as emulating the iret and setting up th stack for the interrupt handler basically cancel each other out. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r--drivers/lguest/interrupts_and_traps.c99
1 files changed, 72 insertions, 27 deletions
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c
index 6d4c072b61e1..5e7559be222a 100644
--- a/drivers/lguest/interrupts_and_traps.c
+++ b/drivers/lguest/interrupts_and_traps.c
@@ -56,21 +56,16 @@ static void push_guest_stack(struct lg_cpu *cpu, unsigned long *gstack, u32 val)
56} 56}
57 57
58/*H:210 58/*H:210
59 * The set_guest_interrupt() routine actually delivers the interrupt or 59 * The push_guest_interrupt_stack() routine saves Guest state on the stack for
60 * trap. The mechanics of delivering traps and interrupts to the Guest are the 60 * an interrupt or trap. The mechanics of delivering traps and interrupts to
61 * same, except some traps have an "error code" which gets pushed onto the 61 * the Guest are the same, except some traps have an "error code" which gets
62 * stack as well: the caller tells us if this is one. 62 * pushed onto the stack as well: the caller tells us if this is one.
63 *
64 * "lo" and "hi" are the two parts of the Interrupt Descriptor Table for this
65 * interrupt or trap. It's split into two parts for traditional reasons: gcc
66 * on i386 used to be frightened by 64 bit numbers.
67 * 63 *
68 * We set up the stack just like the CPU does for a real interrupt, so it's 64 * We set up the stack just like the CPU does for a real interrupt, so it's
69 * identical for the Guest (and the standard "iret" instruction will undo 65 * identical for the Guest (and the standard "iret" instruction will undo
70 * it). 66 * it).
71 */ 67 */
72static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, 68static void push_guest_interrupt_stack(struct lg_cpu *cpu, bool has_err)
73 bool has_err)
74{ 69{
75 unsigned long gstack, origstack; 70 unsigned long gstack, origstack;
76 u32 eflags, ss, irq_enable; 71 u32 eflags, ss, irq_enable;
@@ -130,12 +125,28 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi,
130 if (has_err) 125 if (has_err)
131 push_guest_stack(cpu, &gstack, cpu->regs->errcode); 126 push_guest_stack(cpu, &gstack, cpu->regs->errcode);
132 127
133 /* 128 /* Adjust the stack pointer and stack segment. */
134 * Now we've pushed all the old state, we change the stack, the code
135 * segment and the address to execute.
136 */
137 cpu->regs->ss = ss; 129 cpu->regs->ss = ss;
138 cpu->regs->esp = virtstack + (gstack - origstack); 130 cpu->regs->esp = virtstack + (gstack - origstack);
131}
132
133/*
134 * This actually makes the Guest start executing the given interrupt/trap
135 * handler.
136 *
137 * "lo" and "hi" are the two parts of the Interrupt Descriptor Table for this
138 * interrupt or trap. It's split into two parts for traditional reasons: gcc
139 * on i386 used to be frightened by 64 bit numbers.
140 */
141static void guest_run_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi)
142{
143 /* If we're already in the kernel, we don't change stacks. */
144 if ((cpu->regs->ss&0x3) != GUEST_PL)
145 cpu->regs->ss = cpu->esp1;
146
147 /*
148 * Set the code segment and the address to execute.
149 */
139 cpu->regs->cs = (__KERNEL_CS|GUEST_PL); 150 cpu->regs->cs = (__KERNEL_CS|GUEST_PL);
140 cpu->regs->eip = idt_address(lo, hi); 151 cpu->regs->eip = idt_address(lo, hi);
141 152
@@ -158,6 +169,24 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi,
158 kill_guest(cpu, "Disabling interrupts"); 169 kill_guest(cpu, "Disabling interrupts");
159} 170}
160 171
172/* This restores the eflags word which was pushed on the stack by a trap */
173static void restore_eflags(struct lg_cpu *cpu)
174{
175 /* This is the physical address of the stack. */
176 unsigned long stack_pa = guest_pa(cpu, cpu->regs->esp);
177
178 /*
179 * Stack looks like this:
180 * Address Contents
181 * esp EIP
182 * esp + 4 CS
183 * esp + 8 EFLAGS
184 */
185 cpu->regs->eflags = lgread(cpu, stack_pa + 8, u32);
186 cpu->regs->eflags &=
187 ~(X86_EFLAGS_TF|X86_EFLAGS_VM|X86_EFLAGS_RF|X86_EFLAGS_NT);
188}
189
161/*H:205 190/*H:205
162 * Virtual Interrupts. 191 * Virtual Interrupts.
163 * 192 *
@@ -200,13 +229,6 @@ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more)
200 229
201 BUG_ON(irq >= LGUEST_IRQS); 230 BUG_ON(irq >= LGUEST_IRQS);
202 231
203 /*
204 * They may be in the middle of an iret, where they asked us never to
205 * deliver interrupts.
206 */
207 if (cpu->regs->eip == cpu->lg->noirq_iret)
208 return;
209
210 /* If they're halted, interrupts restart them. */ 232 /* If they're halted, interrupts restart them. */
211 if (cpu->halted) { 233 if (cpu->halted) {
212 /* Re-enable interrupts. */ 234 /* Re-enable interrupts. */
@@ -236,12 +258,34 @@ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more)
236 if (idt_present(idt->a, idt->b)) { 258 if (idt_present(idt->a, idt->b)) {
237 /* OK, mark it no longer pending and deliver it. */ 259 /* OK, mark it no longer pending and deliver it. */
238 clear_bit(irq, cpu->irqs_pending); 260 clear_bit(irq, cpu->irqs_pending);
261
239 /* 262 /*
240 * set_guest_interrupt() takes the interrupt descriptor and a 263 * They may be about to iret, where they asked us never to
241 * flag to say whether this interrupt pushes an error code onto 264 * deliver interrupts. In this case, we can emulate that iret
242 * the stack as well: virtual interrupts never do. 265 * then immediately deliver the interrupt. This is basically
266 * a noop: the iret would pop the interrupt frame and restore
267 * eflags, and then we'd set it up again. So just restore the
268 * eflags word and jump straight to the handler in this case.
269 *
270 * Denys Vlasenko points out that this isn't quite right: if
271 * the iret was returning to userspace, then that interrupt
272 * would reset the stack pointer (which the Guest told us
273 * about via LHCALL_SET_STACK). But unless the Guest is being
274 * *really* weird, that will be the same as the current stack
275 * anyway.
243 */ 276 */
244 set_guest_interrupt(cpu, idt->a, idt->b, false); 277 if (cpu->regs->eip == cpu->lg->noirq_iret) {
278 restore_eflags(cpu);
279 } else {
280 /*
281 * set_guest_interrupt() takes a flag to say whether
282 * this interrupt pushes an error code onto the stack
283 * as well: virtual interrupts never do.
284 */
285 push_guest_interrupt_stack(cpu, false);
286 }
287 /* Actually make Guest cpu jump to handler. */
288 guest_run_interrupt(cpu, idt->a, idt->b);
245 } 289 }
246 290
247 /* 291 /*
@@ -352,8 +396,9 @@ bool deliver_trap(struct lg_cpu *cpu, unsigned int num)
352 */ 396 */
353 if (!idt_present(cpu->arch.idt[num].a, cpu->arch.idt[num].b)) 397 if (!idt_present(cpu->arch.idt[num].a, cpu->arch.idt[num].b))
354 return false; 398 return false;
355 set_guest_interrupt(cpu, cpu->arch.idt[num].a, 399 push_guest_interrupt_stack(cpu, has_err(num));
356 cpu->arch.idt[num].b, has_err(num)); 400 guest_run_interrupt(cpu, cpu->arch.idt[num].a,
401 cpu->arch.idt[num].b);
357 return true; 402 return true;
358} 403}
359 404