diff options
Diffstat (limited to 'drivers/lguest/interrupts_and_traps.c')
-rw-r--r-- | drivers/lguest/interrupts_and_traps.c | 115 |
1 files changed, 80 insertions, 35 deletions
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 70dfcdc29f1f..eb934b0242e0 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -20,7 +20,7 @@ #include "lg.h" /* Allow Guests to use a non-128 (ie. non-Linux) syscall trap. */ -static unsigned int syscall_vector = SYSCALL_VECTOR; +static unsigned int syscall_vector = IA32_SYSCALL_VECTOR; module_param(syscall_vector, uint, 0444); /* The address of the interrupt handler is split into two bits: */ @@ -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); } /* @@ -290,8 +333,8 @@ void set_interrupt(struct lg_cpu *cpu, unsigned int irq) */ static bool could_be_syscall(unsigned int num) { - /* Normal Linux SYSCALL_VECTOR or reserved vector? */ - return num == SYSCALL_VECTOR || num == syscall_vector; + /* Normal Linux IA32_SYSCALL_VECTOR or reserved vector? */ + return num == IA32_SYSCALL_VECTOR || num == syscall_vector; } /* The syscall vector it wants must be unused by Host. */ @@ -308,7 +351,7 @@ bool check_syscall_vector(struct lguest *lg) int init_interrupts(void) { /* If they want some strange system call vector, reserve it now */ - if (syscall_vector != SYSCALL_VECTOR) { + if (syscall_vector != IA32_SYSCALL_VECTOR) { if (test_bit(syscall_vector, used_vectors) || vector_used_by_percpu_irq(syscall_vector)) { printk(KERN_ERR "lg: couldn't reserve syscall %u\n", @@ -323,7 +366,7 @@ int init_interrupts(void) void free_interrupts(void) { - if (syscall_vector != SYSCALL_VECTOR) + if (syscall_vector != IA32_SYSCALL_VECTOR) clear_bit(syscall_vector, used_vectors); } @@ -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 |