powerpc/64: context tracking move to interrupt wrappers
[linux-2.6-microblaze.git] / arch / powerpc / kernel / traps.c
index 3ec7b44..9c26fb4 100644 (file)
@@ -41,6 +41,7 @@
 #include <asm/emulated_ops.h>
 #include <linux/uaccess.h>
 #include <asm/debugfs.h>
+#include <asm/interrupt.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
 #include <asm/rtas.h>
@@ -342,8 +343,8 @@ static bool exception_common(int signr, struct pt_regs *regs, int code,
 
        show_signal_msg(signr, regs, code, addr);
 
-       if (arch_irqs_disabled() && !arch_irq_disabled_regs(regs))
-               local_irq_enable();
+       if (arch_irqs_disabled())
+               interrupt_cond_local_irq_enable(regs);
 
        current->thread.trap_nr = code;
 
@@ -430,8 +431,7 @@ nonrecoverable:
        regs->msr &= ~MSR_RI;
 #endif
 }
-
-void system_reset_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception)
 {
        unsigned long hsrr0, hsrr1;
        bool saved_hsrrs = false;
@@ -503,8 +503,11 @@ out:
                die("Unrecoverable nested System Reset", regs, SIGABRT);
 #endif
        /* Must die if the interrupt is not recoverable */
-       if (!(regs->msr & MSR_RI))
+       if (!(regs->msr & MSR_RI)) {
+               /* For the reason explained in die_mce, nmi_exit before die */
+               nmi_exit();
                die("Unrecoverable System Reset", regs, SIGABRT);
+       }
 
        if (saved_hsrrs) {
                mtspr(SPRN_HSRR0, hsrr0);
@@ -516,7 +519,10 @@ out:
        this_cpu_set_ftrace_enabled(ftrace_enabled);
 
        /* What should we do here? We could issue a shutdown or hard reset. */
+
+       return 0;
 }
+NOKPROBE_SYMBOL(system_reset_exception);
 
 /*
  * I/O accesses can cause machine checks on powermacs.
@@ -788,7 +794,24 @@ int machine_check_generic(struct pt_regs *regs)
 }
 #endif /* everything else */
 
-void machine_check_exception(struct pt_regs *regs)
+void die_mce(const char *str, struct pt_regs *regs, long err)
+{
+       /*
+        * The machine check wants to kill the interrupted context, but
+        * do_exit() checks for in_interrupt() and panics in that case, so
+        * exit the irq/nmi before calling die.
+        */
+       if (!IS_ENABLED(CONFIG_PPC_BOOK3S_64))
+               nmi_exit();
+       die(str, regs, err);
+}
+NOKPROBE_SYMBOL(die_mce);
+
+#ifdef CONFIG_PPC_BOOK3S_64
+DEFINE_INTERRUPT_HANDLER_ASYNC(machine_check_exception)
+#else
+DEFINE_INTERRUPT_HANDLER_NMI(machine_check_exception)
+#endif
 {
        int recover = 0;
 
@@ -830,21 +853,24 @@ void machine_check_exception(struct pt_regs *regs)
        if (check_io_access(regs))
                goto bail;
 
-       if (nmi) nmi_exit();
-
-       die("Machine check", regs, SIGBUS);
+       die_mce("Machine check", regs, SIGBUS);
 
+bail:
        /* Must die if the interrupt is not recoverable */
        if (!(regs->msr & MSR_RI))
-               die("Unrecoverable Machine check", regs, SIGBUS);
-
-       return;
+               die_mce("Unrecoverable Machine check", regs, SIGBUS);
 
-bail:
        if (nmi) nmi_exit();
+
+#ifdef CONFIG_PPC_BOOK3S_64
+       return;
+#else
+       return 0;
+#endif
 }
+NOKPROBE_SYMBOL(machine_check_exception);
 
-void SMIException(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(SMIException) /* async? */
 {
        die("System Management Interrupt", regs, SIGABRT);
 }
@@ -1030,7 +1056,7 @@ static void p9_hmi_special_emu(struct pt_regs *regs)
 }
 #endif /* CONFIG_VSX */
 
-void handle_hmi_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER_ASYNC(handle_hmi_exception)
 {
        struct pt_regs *old_regs;
 
@@ -1059,42 +1085,39 @@ void handle_hmi_exception(struct pt_regs *regs)
        set_irq_regs(old_regs);
 }
 
-void unknown_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(unknown_exception)
 {
-       enum ctx_state prev_state = exception_enter();
-
        printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
               regs->nip, regs->msr, regs->trap);
 
        _exception(SIGTRAP, regs, TRAP_UNK, 0);
-
-       exception_exit(prev_state);
 }
 
-void instruction_breakpoint_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER_ASYNC(unknown_async_exception)
 {
-       enum ctx_state prev_state = exception_enter();
+       printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
+              regs->nip, regs->msr, regs->trap);
 
+       _exception(SIGTRAP, regs, TRAP_UNK, 0);
+}
+
+DEFINE_INTERRUPT_HANDLER(instruction_breakpoint_exception)
+{
        if (notify_die(DIE_IABR_MATCH, "iabr_match", regs, 5,
                                        5, SIGTRAP) == NOTIFY_STOP)
-               goto bail;
+               return;
        if (debugger_iabr_match(regs))
-               goto bail;
+               return;
        _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
-
-bail:
-       exception_exit(prev_state);
 }
 
-void RunModeException(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(RunModeException)
 {
        _exception(SIGTRAP, regs, TRAP_UNK, 0);
 }
 
-void single_step_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(single_step_exception)
 {
-       enum ctx_state prev_state = exception_enter();
-
        clear_single_step(regs);
        clear_br_trace(regs);
 
@@ -1103,14 +1126,11 @@ void single_step_exception(struct pt_regs *regs)
 
        if (notify_die(DIE_SSTEP, "single_step", regs, 5,
                                        5, SIGTRAP) == NOTIFY_STOP)
-               goto bail;
+               return;
        if (debugger_sstep(regs))
-               goto bail;
+               return;
 
        _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
-
-bail:
-       exception_exit(prev_state);
 }
 NOKPROBE_SYMBOL(single_step_exception);
 
@@ -1436,9 +1456,8 @@ static int emulate_math(struct pt_regs *regs)
 static inline int emulate_math(struct pt_regs *regs) { return -1; }
 #endif
 
-void program_check_exception(struct pt_regs *regs)
+static void do_program_check(struct pt_regs *regs)
 {
-       enum ctx_state prev_state = exception_enter();
        unsigned int reason = get_reason(regs);
 
        /* We can now get here via a FP Unavailable exception if the core
@@ -1447,22 +1466,22 @@ void program_check_exception(struct pt_regs *regs)
        if (reason & REASON_FP) {
                /* IEEE FP exception */
                parse_fpe(regs);
-               goto bail;
+               return;
        }
        if (reason & REASON_TRAP) {
                unsigned long bugaddr;
                /* Debugger is first in line to stop recursive faults in
                 * rcu_lock, notify_die, or atomic_notifier_call_chain */
                if (debugger_bpt(regs))
-                       goto bail;
+                       return;
 
                if (kprobe_handler(regs))
-                       goto bail;
+                       return;
 
                /* trap exception */
                if (notify_die(DIE_BPT, "breakpoint", regs, 5, 5, SIGTRAP)
                                == NOTIFY_STOP)
-                       goto bail;
+                       return;
 
                bugaddr = regs->nip;
                /*
@@ -1474,10 +1493,10 @@ void program_check_exception(struct pt_regs *regs)
                if (!(regs->msr & MSR_PR) &&  /* not user-mode */
                    report_bug(bugaddr, regs) == BUG_TRAP_TYPE_WARN) {
                        regs->nip += 4;
-                       goto bail;
+                       return;
                }
                _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
-               goto bail;
+               return;
        }
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
        if (reason & REASON_TM) {
@@ -1498,7 +1517,7 @@ void program_check_exception(struct pt_regs *regs)
                 */
                if (user_mode(regs)) {
                        _exception(SIGILL, regs, ILL_ILLOPN, regs->nip);
-                       goto bail;
+                       return;
                } else {
                        printk(KERN_EMERG "Unexpected TM Bad Thing exception "
                               "at %lx (msr 0x%lx) tm_scratch=%llx\n",
@@ -1518,9 +1537,7 @@ void program_check_exception(struct pt_regs *regs)
        if (!user_mode(regs))
                goto sigill;
 
-       /* We restore the interrupt state now */
-       if (!arch_irq_disabled_regs(regs))
-               local_irq_enable();
+       interrupt_cond_local_irq_enable(regs);
 
        /* (reason & REASON_ILLEGAL) would be the obvious thing here,
         * but there seems to be a hardware bug on the 405GP (RevD)
@@ -1531,7 +1548,7 @@ void program_check_exception(struct pt_regs *regs)
         * pattern to occurrences etc. -dgibson 31/Mar/2003
         */
        if (!emulate_math(regs))
-               goto bail;
+               return;
 
        /* Try to emulate it if we should. */
        if (reason & (REASON_ILLEGAL | REASON_PRIVILEGED)) {
@@ -1539,10 +1556,10 @@ void program_check_exception(struct pt_regs *regs)
                case 0:
                        regs->nip += 4;
                        emulate_single_step(regs);
-                       goto bail;
+                       return;
                case -EFAULT:
                        _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
-                       goto bail;
+                       return;
                }
        }
 
@@ -1552,8 +1569,11 @@ sigill:
        else
                _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
 
-bail:
-       exception_exit(prev_state);
+}
+
+DEFINE_INTERRUPT_HANDLER(program_check_exception)
+{
+       do_program_check(regs);
 }
 NOKPROBE_SYMBOL(program_check_exception);
 
@@ -1561,25 +1581,21 @@ NOKPROBE_SYMBOL(program_check_exception);
  * This occurs when running in hypervisor mode on POWER6 or later
  * and an illegal instruction is encountered.
  */
-void emulation_assist_interrupt(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(emulation_assist_interrupt)
 {
        regs->msr |= REASON_ILLEGAL;
-       program_check_exception(regs);
+       do_program_check(regs);
 }
 NOKPROBE_SYMBOL(emulation_assist_interrupt);
 
-void alignment_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(alignment_exception)
 {
-       enum ctx_state prev_state = exception_enter();
        int sig, code, fixed = 0;
        unsigned long  reason;
 
-       /* We restore the interrupt state now */
-       if (!arch_irq_disabled_regs(regs))
-               local_irq_enable();
+       interrupt_cond_local_irq_enable(regs);
 
        reason = get_reason(regs);
-
        if (reason & REASON_BOUNDARY) {
                sig = SIGBUS;
                code = BUS_ADRALN;
@@ -1587,7 +1603,7 @@ void alignment_exception(struct pt_regs *regs)
        }
 
        if (tm_abort_check(regs, TM_CAUSE_ALIGNMENT | TM_CAUSE_PERSISTENT))
-               goto bail;
+               return;
 
        /* we don't implement logging of alignment exceptions */
        if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS))
@@ -1597,7 +1613,7 @@ void alignment_exception(struct pt_regs *regs)
                /* skip over emulated instruction */
                regs->nip += inst_length(reason);
                emulate_single_step(regs);
-               goto bail;
+               return;
        }
 
        /* Operand address was bad */
@@ -1612,13 +1628,10 @@ bad:
        if (user_mode(regs))
                _exception(sig, regs, code, regs->dar);
        else
-               bad_page_fault(regs, regs->dar, sig);
-
-bail:
-       exception_exit(prev_state);
+               bad_page_fault(regs, sig);
 }
 
-void StackOverflow(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(StackOverflow)
 {
        pr_crit("Kernel stack overflow in process %s[%d], r1=%lx\n",
                current->comm, task_pid_nr(current), regs->gpr[1]);
@@ -1627,46 +1640,33 @@ void StackOverflow(struct pt_regs *regs)
        panic("kernel stack overflow");
 }
 
-void stack_overflow_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(stack_overflow_exception)
 {
-       enum ctx_state prev_state = exception_enter();
-
        die("Kernel stack overflow", regs, SIGSEGV);
-
-       exception_exit(prev_state);
 }
 
-void kernel_fp_unavailable_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(kernel_fp_unavailable_exception)
 {
-       enum ctx_state prev_state = exception_enter();
-
        printk(KERN_EMERG "Unrecoverable FP Unavailable Exception "
                          "%lx at %lx\n", regs->trap, regs->nip);
        die("Unrecoverable FP Unavailable Exception", regs, SIGABRT);
-
-       exception_exit(prev_state);
 }
 
-void altivec_unavailable_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(altivec_unavailable_exception)
 {
-       enum ctx_state prev_state = exception_enter();
-
        if (user_mode(regs)) {
                /* A user program has executed an altivec instruction,
                   but this kernel doesn't support altivec. */
                _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
-               goto bail;
+               return;
        }
 
        printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
                        "%lx at %lx\n", regs->trap, regs->nip);
        die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
-
-bail:
-       exception_exit(prev_state);
 }
 
-void vsx_unavailable_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(vsx_unavailable_exception)
 {
        if (user_mode(regs)) {
                /* A user program has executed an vsx instruction,
@@ -1697,7 +1697,7 @@ static void tm_unavailable(struct pt_regs *regs)
        die("Unrecoverable TM Unavailable Exception", regs, SIGABRT);
 }
 
-void facility_unavailable_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(facility_unavailable_exception)
 {
        static char *facility_strings[] = {
                [FSCR_FP_LG] = "FPU",
@@ -1737,9 +1737,7 @@ void facility_unavailable_exception(struct pt_regs *regs)
                die("Unexpected facility unavailable exception", regs, SIGABRT);
        }
 
-       /* We restore the interrupt state now */
-       if (!arch_irq_disabled_regs(regs))
-               local_irq_enable();
+       interrupt_cond_local_irq_enable(regs);
 
        if (status == FSCR_DSCR_LG) {
                /*
@@ -1817,7 +1815,7 @@ out:
 
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
 
-void fp_unavailable_tm(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(fp_unavailable_tm)
 {
        /* Note:  This does not handle any kind of FP laziness. */
 
@@ -1850,7 +1848,7 @@ void fp_unavailable_tm(struct pt_regs *regs)
        tm_recheckpoint(&current->thread);
 }
 
-void altivec_unavailable_tm(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(altivec_unavailable_tm)
 {
        /* See the comments in fp_unavailable_tm().  This function operates
         * the same way.
@@ -1865,7 +1863,7 @@ void altivec_unavailable_tm(struct pt_regs *regs)
        current->thread.used_vr = 1;
 }
 
-void vsx_unavailable_tm(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(vsx_unavailable_tm)
 {
        /* See the comments in fp_unavailable_tm().  This works similarly,
         * though we're loading both FP and VEC registers in here.
@@ -1890,11 +1888,48 @@ void vsx_unavailable_tm(struct pt_regs *regs)
 }
 #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
 
-void performance_monitor_exception(struct pt_regs *regs)
+#ifdef CONFIG_PPC64
+DECLARE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi);
+DEFINE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi)
 {
+       nmi_enter();
+
+       __this_cpu_inc(irq_stat.pmu_irqs);
+
+       perf_irq(regs);
+
+       nmi_exit();
+
+       return 0;
+}
+#endif
+
+DECLARE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async);
+DEFINE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async)
+{
+       irq_enter();
+
        __this_cpu_inc(irq_stat.pmu_irqs);
 
        perf_irq(regs);
+
+       irq_exit();
+}
+
+DEFINE_INTERRUPT_HANDLER_RAW(performance_monitor_exception)
+{
+       /*
+        * On 64-bit, if perf interrupts hit in a local_irq_disable
+        * (soft-masked) region, we consider them as NMIs. This is required to
+        * prevent hash faults on user addresses when reading callchains (and
+        * looks better from an irq tracing perspective).
+        */
+       if (IS_ENABLED(CONFIG_PPC64) && unlikely(arch_irq_disabled_regs(regs)))
+               performance_monitor_exception_nmi(regs);
+       else
+               performance_monitor_exception_async(regs);
+
+       return 0;
 }
 
 #ifdef CONFIG_PPC_ADV_DEBUG_REGS
@@ -1957,8 +1992,10 @@ static void handle_debug(struct pt_regs *regs, unsigned long debug_status)
                mtspr(SPRN_DBCR0, current->thread.debug.dbcr0);
 }
 
-void DebugException(struct pt_regs *regs, unsigned long debug_status)
+DEFINE_INTERRUPT_HANDLER(DebugException)
 {
+       unsigned long debug_status = regs->dsisr;
+
        current->thread.debug.dbsr = debug_status;
 
        /* Hack alert: On BookE, Branch Taken stops on the branch itself, while
@@ -2028,7 +2065,7 @@ NOKPROBE_SYMBOL(DebugException);
 #endif /* CONFIG_PPC_ADV_DEBUG_REGS */
 
 #ifdef CONFIG_ALTIVEC
-void altivec_assist_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(altivec_assist_exception)
 {
        int err;
 
@@ -2062,9 +2099,10 @@ void altivec_assist_exception(struct pt_regs *regs)
 #endif /* CONFIG_ALTIVEC */
 
 #ifdef CONFIG_FSL_BOOKE
-void CacheLockingException(struct pt_regs *regs, unsigned long address,
-                          unsigned long error_code)
+DEFINE_INTERRUPT_HANDLER(CacheLockingException)
 {
+       unsigned long error_code = regs->dsisr;
+
        /* We treat cache locking instructions from the user
         * as priv ops, in the future we could try to do
         * something smarter
@@ -2076,7 +2114,7 @@ void CacheLockingException(struct pt_regs *regs, unsigned long address,
 #endif /* CONFIG_FSL_BOOKE */
 
 #ifdef CONFIG_SPE
-void SPEFloatingPointException(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(SPEFloatingPointException)
 {
        extern int do_spe_mathemu(struct pt_regs *regs);
        unsigned long spefscr;
@@ -2084,9 +2122,7 @@ void SPEFloatingPointException(struct pt_regs *regs)
        int code = FPE_FLTUNK;
        int err;
 
-       /* We restore the interrupt state now */
-       if (!arch_irq_disabled_regs(regs))
-               local_irq_enable();
+       interrupt_cond_local_irq_enable(regs);
 
        flush_spe_to_thread(current);
 
@@ -2128,14 +2164,12 @@ void SPEFloatingPointException(struct pt_regs *regs)
        return;
 }
 
-void SPEFloatingPointRoundException(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(SPEFloatingPointRoundException)
 {
        extern int speround_handler(struct pt_regs *regs);
        int err;
 
-       /* We restore the interrupt state now */
-       if (!arch_irq_disabled_regs(regs))
-               local_irq_enable();
+       interrupt_cond_local_irq_enable(regs);
 
        preempt_disable();
        if (regs->msr & MSR_SPE)
@@ -2170,7 +2204,7 @@ void SPEFloatingPointRoundException(struct pt_regs *regs)
  * in the MSR is 0.  This indicates that SRR0/1 are live, and that
  * we therefore lost state by taking this exception.
  */
-void unrecoverable_exception(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(unrecoverable_exception)
 {
        pr_emerg("Unrecoverable exception %lx at %lx (msr=%lx)\n",
                 regs->trap, regs->nip, regs->msr);
@@ -2190,7 +2224,7 @@ void __attribute__ ((weak)) WatchdogHandler(struct pt_regs *regs)
        return;
 }
 
-void WatchdogException(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(WatchdogException) /* XXX NMI? async? */
 {
        printk (KERN_EMERG "PowerPC Book-E Watchdog Exception\n");
        WatchdogHandler(regs);
@@ -2201,7 +2235,7 @@ void WatchdogException(struct pt_regs *regs)
  * We enter here if we discover during exception entry that we are
  * running in supervisor mode with a userspace value in the stack pointer.
  */
-void kernel_bad_stack(struct pt_regs *regs)
+DEFINE_INTERRUPT_HANDLER(kernel_bad_stack)
 {
        printk(KERN_EMERG "Bad kernel stack pointer %lx at %lx\n",
               regs->gpr[1], regs->nip);