Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / lguest / interrupts_and_traps.c
index 70dfcdc..5e7559b 100644 (file)
@@ -56,21 +56,16 @@ static void push_guest_stack(struct lg_cpu *cpu, unsigned long *gstack, u32 val)
 }
 
 /*H:210
- * The set_guest_interrupt() routine actually delivers the interrupt or
- * trap.  The mechanics of delivering traps and interrupts to the Guest are the
- * same, except some traps have an "error code" which gets pushed onto the
- * stack as well: the caller tells us if this is one.
- *
- * "lo" and "hi" are the two parts of the Interrupt Descriptor Table for this
- * interrupt or trap.  It's split into two parts for traditional reasons: gcc
- * on i386 used to be frightened by 64 bit numbers.
+ * The push_guest_interrupt_stack() routine saves Guest state on the stack for
+ * an interrupt or trap.  The mechanics of delivering traps and interrupts to
+ * the Guest are the same, except some traps have an "error code" which gets
+ * pushed onto the stack as well: the caller tells us if this is one.
  *
  * We set up the stack just like the CPU does for a real interrupt, so it's
  * identical for the Guest (and the standard "iret" instruction will undo
  * it).
  */
-static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi,
-                               bool has_err)
+static void push_guest_interrupt_stack(struct lg_cpu *cpu, bool has_err)
 {
        unsigned long gstack, origstack;
        u32 eflags, ss, irq_enable;
@@ -130,12 +125,28 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi,
        if (has_err)
                push_guest_stack(cpu, &gstack, cpu->regs->errcode);
 
-       /*
-        * Now we've pushed all the old state, we change the stack, the code
-        * segment and the address to execute.
-        */
+       /* Adjust the stack pointer and stack segment. */
        cpu->regs->ss = ss;
        cpu->regs->esp = virtstack + (gstack - origstack);
+}
+
+/*
+ * This actually makes the Guest start executing the given interrupt/trap
+ * handler.
+ *
+ * "lo" and "hi" are the two parts of the Interrupt Descriptor Table for this
+ * interrupt or trap.  It's split into two parts for traditional reasons: gcc
+ * on i386 used to be frightened by 64 bit numbers.
+ */
+static void guest_run_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi)
+{
+       /* If we're already in the kernel, we don't change stacks. */
+       if ((cpu->regs->ss&0x3) != GUEST_PL)
+               cpu->regs->ss = cpu->esp1;
+
+       /*
+        * Set the code segment and the address to execute.
+        */
        cpu->regs->cs = (__KERNEL_CS|GUEST_PL);
        cpu->regs->eip = idt_address(lo, hi);
 
@@ -158,6 +169,24 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi,
                        kill_guest(cpu, "Disabling interrupts");
 }
 
+/* This restores the eflags word which was pushed on the stack by a trap */
+static void restore_eflags(struct lg_cpu *cpu)
+{
+       /* This is the physical address of the stack. */
+       unsigned long stack_pa = guest_pa(cpu, cpu->regs->esp);
+
+       /*
+        * Stack looks like this:
+        * Address      Contents
+        * esp          EIP
+        * esp + 4      CS
+        * esp + 8      EFLAGS
+        */
+       cpu->regs->eflags = lgread(cpu, stack_pa + 8, u32);
+       cpu->regs->eflags &=
+               ~(X86_EFLAGS_TF|X86_EFLAGS_VM|X86_EFLAGS_RF|X86_EFLAGS_NT);
+}
+
 /*H:205
  * Virtual Interrupts.
  *
@@ -200,14 +229,6 @@ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more)
 
        BUG_ON(irq >= LGUEST_IRQS);
 
-       /*
-        * They may be in the middle of an iret, where they asked us never to
-        * deliver interrupts.
-        */
-       if (cpu->regs->eip >= cpu->lg->noirq_start &&
-          (cpu->regs->eip < cpu->lg->noirq_end))
-               return;
-
        /* If they're halted, interrupts restart them. */
        if (cpu->halted) {
                /* Re-enable interrupts. */
@@ -237,12 +258,34 @@ void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more)
        if (idt_present(idt->a, idt->b)) {
                /* OK, mark it no longer pending and deliver it. */
                clear_bit(irq, cpu->irqs_pending);
+
                /*
-                * set_guest_interrupt() takes the interrupt descriptor and a
-                * flag to say whether this interrupt pushes an error code onto
-                * the stack as well: virtual interrupts never do.
+                * They may be about to iret, where they asked us never to
+                * deliver interrupts.  In this case, we can emulate that iret
+                * then immediately deliver the interrupt.  This is basically
+                * a noop: the iret would pop the interrupt frame and restore
+                * eflags, and then we'd set it up again.  So just restore the
+                * eflags word and jump straight to the handler in this case.
+                *
+                * Denys Vlasenko points out that this isn't quite right: if
+                * the iret was returning to userspace, then that interrupt
+                * would reset the stack pointer (which the Guest told us
+                * about via LHCALL_SET_STACK).  But unless the Guest is being
+                * *really* weird, that will be the same as the current stack
+                * anyway.
                 */
-               set_guest_interrupt(cpu, idt->a, idt->b, false);
+               if (cpu->regs->eip == cpu->lg->noirq_iret) {
+                       restore_eflags(cpu);
+               } else {
+                       /*
+                        * set_guest_interrupt() takes a flag to say whether
+                        * this interrupt pushes an error code onto the stack
+                        * as well: virtual interrupts never do.
+                        */
+                       push_guest_interrupt_stack(cpu, false);
+               }
+               /* Actually make Guest cpu jump to handler. */
+               guest_run_interrupt(cpu, idt->a, idt->b);
        }
 
        /*
@@ -353,8 +396,9 @@ bool deliver_trap(struct lg_cpu *cpu, unsigned int num)
         */
        if (!idt_present(cpu->arch.idt[num].a, cpu->arch.idt[num].b))
                return false;
-       set_guest_interrupt(cpu, cpu->arch.idt[num].a,
-                           cpu->arch.idt[num].b, has_err(num));
+       push_guest_interrupt_stack(cpu, has_err(num));
+       guest_run_interrupt(cpu, cpu->arch.idt[num].a,
+                           cpu->arch.idt[num].b);
        return true;
 }
 
@@ -395,8 +439,9 @@ static bool direct_trap(unsigned int num)
  * The Guest has the ability to turn its interrupt gates into trap gates,
  * if it is careful.  The Host will let trap gates can go directly to the
  * Guest, but the Guest needs the interrupts atomically disabled for an
- * interrupt gate.  It can do this by pointing the trap gate at instructions
- * within noirq_start and noirq_end, where it can safely disable interrupts.
+ * interrupt gate.  The Host could provide a mechanism to register more
+ * "no-interrupt" regions, and the Guest could point the trap gate at
+ * instructions within that region, where it can safely disable interrupts.
  */
 
 /*M:006