x86/entry: Convert NMI to IDTENTRY_NMI
[linux-2.6-microblaze.git] / arch / x86 / kernel / traps.c
index 4cc5410..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;
@@ -201,30 +201,78 @@ static void do_error_trap(struct pt_regs *regs, long error_code, char *str,
                        NOTIFY_STOP) {
                cond_local_irq_enable(regs);
                do_trap(trapnr, signr, str, regs, error_code, sicode, addr);
+               cond_local_irq_disable(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); \
+/*
+ * Posix requires to provide the address of the faulting instruction for
+ * SIGILL (#UD) and SIGFPE (#DE) in the si_addr member of siginfo_t.
+ *
+ * This address is usually regs->ip, but when an uprobe moved the code out
+ * of line then regs->ip points to the XOL code which would confuse
+ * anything which analyzes the fault address vs. the unmodified binary. If
+ * a trap happened in XOL code then uprobe maps regs->ip back to the
+ * original instruction address.
+ */
+static __always_inline void __user *error_get_trap_addr(struct pt_regs *regs)
+{
+       return (void __user *)uprobe_get_trap_addr(regs);
 }
 
-DO_ERROR(X86_TRAP_DE,     SIGFPE,  FPE_INTDIV,   IP, "divide error",        divide_error)
-DO_ERROR(X86_TRAP_OF,     SIGSEGV,          0, NULL, "overflow",            overflow)
-DO_ERROR(X86_TRAP_UD,     SIGILL,  ILL_ILLOPN,   IP, "invalid opcode",      invalid_op)
-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(exc_divide_error)
+{
+       do_error_trap(regs, 0, "divide_error", X86_TRAP_DE, SIGFPE,
+                     FPE_INTDIV, error_get_trap_addr(regs));
+}
 
-dotraplinkage void do_alignment_check(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY(exc_overflow)
 {
-       char *str = "alignment check";
+       do_error_trap(regs, 0, "overflow", X86_TRAP_OF, SIGSEGV, 0, NULL);
+}
 
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
+#ifdef CONFIG_X86_F00F_BUG
+void handle_invalid_op(struct pt_regs *regs)
+#else
+static inline void handle_invalid_op(struct pt_regs *regs)
+#endif
+{
+       do_error_trap(regs, 0, "invalid opcode", X86_TRAP_UD, SIGILL,
+                     ILL_ILLOPN, error_get_trap_addr(regs));
+}
+
+DEFINE_IDTENTRY(exc_invalid_op)
+{
+       handle_invalid_op(regs);
+}
+
+DEFINE_IDTENTRY(exc_coproc_segment_overrun)
+{
+       do_error_trap(regs, 0, "coprocessor segment overrun",
+                     X86_TRAP_OLD_MF, SIGFPE, 0, NULL);
+}
+
+DEFINE_IDTENTRY_ERRORCODE(exc_invalid_tss)
+{
+       do_error_trap(regs, error_code, "invalid TSS", X86_TRAP_TS, SIGSEGV,
+                     0, NULL);
+}
+
+DEFINE_IDTENTRY_ERRORCODE(exc_segment_not_present)
+{
+       do_error_trap(regs, error_code, "segment not present", X86_TRAP_NP,
+                     SIGBUS, 0, NULL);
+}
+
+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;
@@ -299,6 +347,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
                regs->ip == (unsigned long)native_irq_return_iret)
        {
                struct pt_regs *gpregs = (struct pt_regs *)this_cpu_read(cpu_tss_rw.x86_tss.sp0) - 1;
+               unsigned long *p = (unsigned long *)regs->sp;
 
                /*
                 * regs->sp points to the failing IRET frame on the
@@ -306,7 +355,11 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
                 * in gpregs->ss through gpregs->ip.
                 *
                 */
-               memmove(&gpregs->ip, (void *)regs->sp, 5*8);
+               gpregs->ip      = p[0];
+               gpregs->cs      = p[1];
+               gpregs->flags   = p[2];
+               gpregs->sp      = p[3];
+               gpregs->ss      = p[4];
                gpregs->orig_ax = 0;  /* Missing (lost) #GP error code */
 
                /*
@@ -320,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;
@@ -380,18 +433,19 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
        panic("Machine halted.");
 }
 
-dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY(exc_bounds)
 {
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
-       if (notify_die(DIE_TRAP, "bounds", regs, error_code,
+       if (notify_die(DIE_TRAP, "bounds", regs, 0,
                        X86_TRAP_BR, SIGSEGV) == NOTIFY_STOP)
                return;
        cond_local_irq_enable(regs);
 
        if (!user_mode(regs))
-               die("bounds", regs, error_code);
+               die("bounds", regs, 0);
+
+       do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, 0, 0, NULL);
 
-       do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, 0, NULL);
+       cond_local_irq_disable(regs);
 }
 
 enum kernel_gp_hint {
@@ -438,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;
@@ -446,17 +500,17 @@ 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)) {
                if (user_mode(regs) && fixup_umip_exception(regs))
-                       return;
+                       goto exit;
        }
 
        if (v8086_mode(regs)) {
                local_irq_enable();
                handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
+               local_irq_disable();
                return;
        }
 
@@ -468,12 +522,11 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
 
                show_signal(tsk, SIGSEGV, "", desc, regs, error_code);
                force_sig(SIGSEGV);
-
-               return;
+               goto exit;
        }
 
        if (fixup_exception(regs, X86_TRAP_GP, error_code, 0))
-               return;
+               goto exit;
 
        tsk->thread.error_code = error_code;
        tsk->thread.trap_nr = X86_TRAP_GP;
@@ -485,11 +538,11 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
        if (!preemptible() &&
            kprobe_running() &&
            kprobe_fault_handler(regs, X86_TRAP_GP))
-               return;
+               goto exit;
 
        ret = notify_die(DIE_GPF, desc, regs, error_code, X86_TRAP_GP, SIGSEGV);
        if (ret == NOTIFY_STOP)
-               return;
+               goto exit;
 
        if (error_code)
                snprintf(desc, sizeof(desc), "segment-related " GPFSTR);
@@ -511,47 +564,71 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
 
        die_addr(desc, regs, error_code, gp_addr);
 
+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
 /*
@@ -559,21 +636,20 @@ NOKPROBE_SYMBOL(do_int3);
  * to switch to the normal thread stack if the interrupted code was in
  * user mode. The actual stack switch is done in entry_64.S
  */
-asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs)
+asmlinkage __visible noinstr struct pt_regs *sync_regs(struct pt_regs *eregs)
 {
        struct pt_regs *regs = (struct pt_regs *)this_cpu_read(cpu_current_top_of_stack) - 1;
        if (regs != eregs)
                *regs = *eregs;
        return regs;
 }
-NOKPROBE_SYMBOL(sync_regs);
 
 struct bad_iret_stack {
        void *error_entry_ret;
        struct pt_regs regs;
 };
 
-asmlinkage __visible notrace
+asmlinkage __visible noinstr
 struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
 {
        /*
@@ -584,19 +660,21 @@ struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
         * just below the IRET frame) and we want to pretend that the
         * exception came from the IRET target.
         */
-       struct bad_iret_stack *new_stack =
-               (struct bad_iret_stack *)this_cpu_read(cpu_tss_rw.x86_tss.sp0) - 1;
+       struct bad_iret_stack tmp, *new_stack =
+               (struct bad_iret_stack *)__this_cpu_read(cpu_tss_rw.x86_tss.sp0) - 1;
 
-       /* Copy the IRET target to the new stack. */
-       memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
+       /* Copy the IRET target to the temporary storage. */
+       memcpy(&tmp.regs.ip, (void *)s->regs.sp, 5*8);
 
        /* Copy the remainder of the stack from the current stack. */
-       memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
+       memcpy(&tmp, s, offsetof(struct bad_iret_stack, regs.ip));
+
+       /* Update the entry stack */
+       memcpy(new_stack, &tmp, sizeof(tmp));
 
        BUG_ON(!user_mode(&new_stack->regs));
        return new_stack;
 }
-NOKPROBE_SYMBOL(fixup_bad_iret);
 #endif
 
 static bool is_sysenter_singlestep(struct pt_regs *regs)
@@ -755,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;
@@ -766,16 +844,16 @@ 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))
-                       return;
+               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);
-               return;
+               if (notify_die(DIE_TRAP, str, regs, 0, trapnr,
+                              SIGFPE) != NOTIFY_STOP)
+                       die(str, regs, 0);
+               goto exit;
        }
 
        /*
@@ -784,32 +862,37 @@ 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: */
        if (!si_code)
-               return;
+               goto exit;
 
        force_sig_fault(SIGFPE, si_code,
                        (void __user *)uprobe_get_trap_addr(regs));
+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:
@@ -832,13 +915,10 @@ do_spurious_interrupt_bug(struct pt_regs *regs, long error_code)
         */
 }
 
-dotraplinkage void
-do_device_not_available(struct pt_regs *regs, long error_code)
+DEFINE_IDTENTRY(exc_device_not_available)
 {
        unsigned long cr0 = read_cr0();
 
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
-
 #ifdef CONFIG_MATH_EMULATION
        if (!boot_cpu_has(X86_FEATURE_FPU) && (cr0 & X86_CR0_EM)) {
                struct math_emu_info info = { };
@@ -847,6 +927,8 @@ do_device_not_available(struct pt_regs *regs, long error_code)
 
                info.regs = regs;
                math_emulate(&info);
+
+               cond_local_irq_disable(regs);
                return;
        }
 #endif
@@ -861,22 +943,20 @@ do_device_not_available(struct pt_regs *regs, long error_code)
                 * to kill the task than getting stuck in a never-ending
                 * loop of #NM faults.
                 */
-               die("unexpected #NM exception", regs, error_code);
+               die("unexpected #NM exception", regs, 0);
        }
 }
-NOKPROBE_SYMBOL(do_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();
 }
 #endif