x86/entry: Convert NMI to IDTENTRY_NMI
[linux-2.6-microblaze.git] / arch / x86 / kernel / traps.c
index b8af5eb..21c8cfc 100644 (file)
@@ -145,7 +145,7 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
         * process no chance to handle the signal and notice the
         * kernel fault information, so that won't result in polluting
         * the information about previously queued, but not yet
-        * delivered, faults.  See also do_general_protection below.
+        * delivered, faults.  See also exc_general_protection below.
         */
        tsk->thread.error_code = error_code;
        tsk->thread.trap_nr = trapnr;
@@ -246,24 +246,33 @@ DEFINE_IDTENTRY(exc_invalid_op)
        handle_invalid_op(regs);
 }
 
-#define IP ((void __user *)uprobe_get_trap_addr(regs))
-#define DO_ERROR(trapnr, signr, sicode, addr, str, name)                  \
-dotraplinkage void do_##name(struct pt_regs *regs, long error_code)       \
-{                                                                         \
-       do_error_trap(regs, error_code, str, trapnr, signr, sicode, addr); \
+DEFINE_IDTENTRY(exc_coproc_segment_overrun)
+{
+       do_error_trap(regs, 0, "coprocessor segment overrun",
+                     X86_TRAP_OLD_MF, SIGFPE, 0, NULL);
 }
 
-DO_ERROR(X86_TRAP_OLD_MF, SIGFPE,           0, NULL, "coprocessor segment overrun", coprocessor_segment_overrun)
-DO_ERROR(X86_TRAP_TS,     SIGSEGV,          0, NULL, "invalid TSS",         invalid_TSS)
-DO_ERROR(X86_TRAP_NP,     SIGBUS,           0, NULL, "segment not present", segment_not_present)
-DO_ERROR(X86_TRAP_SS,     SIGBUS,           0, NULL, "stack segment",       stack_segment)
-#undef IP
+DEFINE_IDTENTRY_ERRORCODE(exc_invalid_tss)
+{
+       do_error_trap(regs, error_code, "invalid TSS", X86_TRAP_TS, SIGSEGV,
+                     0, NULL);
+}
 
-dotraplinkage void do_alignment_check(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY_ERRORCODE(exc_segment_not_present)
 {
-       char *str = "alignment check";
+       do_error_trap(regs, error_code, "segment not present", X86_TRAP_NP,
+                     SIGBUS, 0, NULL);
+}
 
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
+DEFINE_IDTENTRY_ERRORCODE(exc_stack_segment)
+{
+       do_error_trap(regs, error_code, "stack segment", X86_TRAP_SS, SIGBUS,
+                     0, NULL);
+}
+
+DEFINE_IDTENTRY_ERRORCODE(exc_alignment_check)
+{
+       char *str = "alignment check";
 
        if (notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_AC, SIGBUS) == NOTIFY_STOP)
                return;
@@ -364,7 +373,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
                 * which is what the stub expects, given that the faulting
                 * RIP will be the IRET instruction.
                 */
-               regs->ip = (unsigned long)general_protection;
+               regs->ip = (unsigned long)asm_exc_general_protection;
                regs->sp = (unsigned long)&gpregs->orig_ax;
 
                return;
@@ -483,7 +492,7 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
 
 #define GPFSTR "general protection fault"
 
-dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
 {
        char desc[sizeof(GPFSTR) + 50 + 2*sizeof(unsigned long) + 1] = GPFSTR;
        enum kernel_gp_hint hint = GP_NO_HINT;
@@ -491,7 +500,6 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
        unsigned long gp_addr;
        int ret;
 
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
        cond_local_irq_enable(regs);
 
        if (static_cpu_has(X86_FEATURE_UMIP)) {
@@ -559,46 +567,68 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
 exit:
        cond_local_irq_disable(regs);
 }
-NOKPROBE_SYMBOL(do_general_protection);
 
-dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
+static bool do_int3(struct pt_regs *regs)
 {
-       if (poke_int3_handler(regs))
-               return;
-
-       /*
-        * Unlike any other non-IST entry, we can be called from pretty much
-        * any location in the kernel through kprobes -- text_poke() will most
-        * likely be handled by poke_int3_handler() above. This means this
-        * handler is effectively NMI-like.
-        */
-       if (!user_mode(regs))
-               nmi_enter();
+       int res;
 
 #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
-       if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
-                               SIGTRAP) == NOTIFY_STOP)
-               goto exit;
+       if (kgdb_ll_trap(DIE_INT3, "int3", regs, 0, X86_TRAP_BP,
+                        SIGTRAP) == NOTIFY_STOP)
+               return true;
 #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
 
 #ifdef CONFIG_KPROBES
        if (kprobe_int3_handler(regs))
-               goto exit;
+               return true;
 #endif
+       res = notify_die(DIE_INT3, "int3", regs, 0, X86_TRAP_BP, SIGTRAP);
 
-       if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
-                       SIGTRAP) == NOTIFY_STOP)
-               goto exit;
+       return res == NOTIFY_STOP;
+}
+
+static void do_int3_user(struct pt_regs *regs)
+{
+       if (do_int3(regs))
+               return;
 
        cond_local_irq_enable(regs);
-       do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, 0, NULL);
+       do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, 0, 0, NULL);
        cond_local_irq_disable(regs);
+}
 
-exit:
-       if (!user_mode(regs))
+DEFINE_IDTENTRY_RAW(exc_int3)
+{
+       /*
+        * poke_int3_handler() is completely self contained code; it does (and
+        * must) *NOT* call out to anything, lest it hits upon yet another
+        * INT3.
+        */
+       if (poke_int3_handler(regs))
+               return;
+
+       /*
+        * idtentry_enter() uses static_branch_{,un}likely() and therefore
+        * can trigger INT3, hence poke_int3_handler() must be done
+        * before. If the entry came from kernel mode, then use nmi_enter()
+        * because the INT3 could have been hit in any context including
+        * NMI.
+        */
+       if (user_mode(regs)) {
+               idtentry_enter(regs);
+               instrumentation_begin();
+               do_int3_user(regs);
+               instrumentation_end();
+               idtentry_exit(regs);
+       } else {
+               nmi_enter();
+               instrumentation_begin();
+               if (!do_int3(regs))
+                       die("int3", regs, 0);
+               instrumentation_end();
                nmi_exit();
+       }
 }
-NOKPROBE_SYMBOL(do_int3);
 
 #ifdef CONFIG_X86_64
 /*
@@ -803,7 +833,7 @@ NOKPROBE_SYMBOL(do_debug);
  * the correct behaviour even in the presence of the asynchronous
  * IRQ13 behaviour
  */
-static void math_error(struct pt_regs *regs, int error_code, int trapnr)
+static void math_error(struct pt_regs *regs, int trapnr)
 {
        struct task_struct *task = current;
        struct fpu *fpu = &task->thread.fpu;
@@ -814,15 +844,15 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
        cond_local_irq_enable(regs);
 
        if (!user_mode(regs)) {
-               if (fixup_exception(regs, trapnr, error_code, 0))
+               if (fixup_exception(regs, trapnr, 0, 0))
                        goto exit;
 
-               task->thread.error_code = error_code;
+               task->thread.error_code = 0;
                task->thread.trap_nr = trapnr;
 
-               if (notify_die(DIE_TRAP, str, regs, error_code,
-                                       trapnr, SIGFPE) != NOTIFY_STOP)
-                       die(str, regs, error_code);
+               if (notify_die(DIE_TRAP, str, regs, 0, trapnr,
+                              SIGFPE) != NOTIFY_STOP)
+                       die(str, regs, 0);
                goto exit;
        }
 
@@ -832,7 +862,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
        fpu__save(fpu);
 
        task->thread.trap_nr    = trapnr;
-       task->thread.error_code = error_code;
+       task->thread.error_code = 0;
 
        si_code = fpu__exception_code(fpu, trapnr);
        /* Retry when we get spurious exceptions: */
@@ -845,21 +875,24 @@ exit:
        cond_local_irq_disable(regs);
 }
 
-dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY(exc_coprocessor_error)
 {
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
-       math_error(regs, error_code, X86_TRAP_MF);
+       math_error(regs, X86_TRAP_MF);
 }
 
-dotraplinkage void
-do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY(exc_simd_coprocessor_error)
 {
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
-       math_error(regs, error_code, X86_TRAP_XF);
+       if (IS_ENABLED(CONFIG_X86_INVD_BUG)) {
+               /* AMD 486 bug: INVD in CPL 0 raises #XF instead of #GP */
+               if (!static_cpu_has(X86_FEATURE_XMM)) {
+                       __exc_general_protection(regs, 0);
+                       return;
+               }
+       }
+       math_error(regs, X86_TRAP_XF);
 }
 
-dotraplinkage void
-do_spurious_interrupt_bug(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY(exc_spurious_interrupt_bug)
 {
        /*
         * This addresses a Pentium Pro Erratum:
@@ -915,14 +948,12 @@ DEFINE_IDTENTRY(exc_device_not_available)
 }
 
 #ifdef CONFIG_X86_32
-dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY_SW(iret_error)
 {
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
        local_irq_enable();
-
-       if (notify_die(DIE_TRAP, "iret exception", regs, error_code,
+       if (notify_die(DIE_TRAP, "iret exception", regs, 0,
                        X86_TRAP_IRET, SIGILL) != NOTIFY_STOP) {
-               do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code,
+               do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, 0,
                        ILL_BADSTK, (void __user *)NULL);
        }
        local_irq_disable();