i387: re-introduce FPU state preloading at context switch time
[pandora-kernel.git] / arch / x86 / kernel / traps.c
index fa1191f..77da5b4 100644 (file)
@@ -311,9 +311,15 @@ dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
                        == NOTIFY_STOP)
                return;
 
+       /*
+        * Let others (NMI) know that the debug stack is in use
+        * as we may switch to the interrupt stack.
+        */
+       debug_stack_usage_inc();
        preempt_conditional_sti(regs);
        do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
        preempt_conditional_cli(regs);
+       debug_stack_usage_dec();
 }
 
 #ifdef CONFIG_X86_64
@@ -406,6 +412,12 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
                                                        SIGTRAP) == NOTIFY_STOP)
                return;
 
+       /*
+        * Let others (NMI) know that the debug stack is in use
+        * as we may switch to the interrupt stack.
+        */
+       debug_stack_usage_inc();
+
        /* It's safe to allow irq's after DR6 has been saved */
        preempt_conditional_sti(regs);
 
@@ -413,6 +425,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
                handle_vm86_trap((struct kernel_vm86_regs *) regs,
                                error_code, 1);
                preempt_conditional_cli(regs);
+               debug_stack_usage_dec();
                return;
        }
 
@@ -432,6 +445,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
        if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS) || user_icebp)
                send_sigtrap(tsk, regs, error_code, si_code);
        preempt_conditional_cli(regs);
+       debug_stack_usage_dec();
 
        return;
 }
@@ -557,25 +571,34 @@ asmlinkage void __attribute__((weak)) smp_threshold_interrupt(void)
 }
 
 /*
- * __math_state_restore assumes that cr0.TS is already clear and the
- * fpu state is all ready for use.  Used during context switch.
+ * This gets called with the process already owning the
+ * FPU state, and with CR0.TS cleared. It just needs to
+ * restore the FPU register state.
  */
-void __math_state_restore(void)
+void __math_state_restore(struct task_struct *tsk)
 {
-       struct thread_info *thread = current_thread_info();
-       struct task_struct *tsk = thread->task;
+       /* We need a safe address that is cheap to find and that is already
+          in L1. We've just brought in "tsk->thread.has_fpu", so use that */
+#define safe_address (tsk->thread.has_fpu)
+
+       /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
+          is pending.  Clear the x87 state here by setting it to fixed
+          values. safe_address is a random variable that should be in L1 */
+       alternative_input(
+               ASM_NOP8 ASM_NOP2,
+               "emms\n\t"              /* clear stack tags */
+               "fildl %P[addr]",       /* set F?P to defined value */
+               X86_FEATURE_FXSAVE_LEAK,
+               [addr] "m" (safe_address));
 
        /*
         * Paranoid restore. send a SIGSEGV if we fail to restore the state.
         */
        if (unlikely(restore_fpu_checking(tsk))) {
-               stts();
+               __thread_fpu_end(tsk);
                force_sig(SIGSEGV, tsk);
                return;
        }
-
-       thread->status |= TS_USEDFPU;   /* So we fnsave on switch_to() */
-       tsk->fpu_counter++;
 }
 
 /*
@@ -585,13 +608,12 @@ void __math_state_restore(void)
  * Careful.. There are problems with IBM-designed IRQ13 behaviour.
  * Don't touch unless you *really* know how it works.
  *
- * Must be called with kernel preemption disabled (in this case,
- * local interrupts are disabled at the call-site in entry.S).
+ * Must be called with kernel preemption disabled (eg with local
+ * local interrupts as in the case of do_device_not_available).
  */
-asmlinkage void math_state_restore(void)
+void math_state_restore(void)
 {
-       struct thread_info *thread = current_thread_info();
-       struct task_struct *tsk = thread->task;
+       struct task_struct *tsk = current;
 
        if (!tsk_used_math(tsk)) {
                local_irq_enable();
@@ -608,9 +630,10 @@ asmlinkage void math_state_restore(void)
                local_irq_disable();
        }
 
-       clts();                         /* Allow maths ops (or we recurse) */
+       __thread_fpu_begin(tsk);
+       __math_state_restore(tsk);
 
-       __math_state_restore();
+       tsk->fpu_counter++;
 }
 EXPORT_SYMBOL_GPL(math_state_restore);
 
@@ -718,4 +741,10 @@ void __init trap_init(void)
        cpu_init();
 
        x86_init.irqs.trap_init();
+
+#ifdef CONFIG_X86_64
+       memcpy(&nmi_idt_table, &idt_table, IDT_ENTRIES * 16);
+       set_nmi_gate(1, &debug);
+       set_nmi_gate(3, &int3);
+#endif
 }