powerpc/64e/interrupt: Use new interrupt context tracking scheme
[linux-2.6-microblaze.git] / arch / powerpc / include / asm / interrupt.h
index aedfba2..a2f5519 100644 (file)
@@ -9,10 +9,18 @@
 #include <asm/kprobes.h>
 #include <asm/runlatch.h>
 
-struct interrupt_state {
-#ifdef CONFIG_PPC_BOOK3E_64
-       enum ctx_state ctx_state;
+static inline void nap_adjust_return(struct pt_regs *regs)
+{
+#ifdef CONFIG_PPC_970_NAP
+       if (unlikely(test_thread_local_flags(_TLF_NAPPING))) {
+               /* Can avoid a test-and-clear because NMIs do not call this */
+               clear_thread_local_flags(_TLF_NAPPING);
+               regs->nip = (unsigned long)power4_idle_nap_return;
+       }
 #endif
+}
+
+struct interrupt_state {
 };
 
 static inline void booke_restore_dbcr0(void)
@@ -29,10 +37,19 @@ static inline void booke_restore_dbcr0(void)
 
 static inline void interrupt_enter_prepare(struct pt_regs *regs, struct interrupt_state *state)
 {
-       /*
-        * Book3E reconciles irq soft mask in asm
-        */
-#ifdef CONFIG_PPC_BOOK3S_64
+#ifdef CONFIG_PPC32
+       if (!arch_irq_disabled_regs(regs))
+               trace_hardirqs_off();
+
+       if (user_mode(regs)) {
+               kuep_lock();
+               account_cpu_user_entry();
+       } else {
+               kuap_save_and_lock(regs);
+       }
+#endif
+
+#ifdef CONFIG_PPC64
        if (irq_soft_mask_set_return(IRQS_ALL_DISABLED) == IRQS_ENABLED)
                trace_hardirqs_off();
        local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
@@ -53,11 +70,7 @@ static inline void interrupt_enter_prepare(struct pt_regs *regs, struct interrup
        }
 #endif
 
-#ifdef CONFIG_PPC_BOOK3E_64
-       state->ctx_state = exception_enter();
-       if (user_mode(regs))
-               account_cpu_user_entry();
-#endif
+       booke_restore_dbcr0();
 }
 
 /*
@@ -76,23 +89,8 @@ static inline void interrupt_enter_prepare(struct pt_regs *regs, struct interrup
  */
 static inline void interrupt_exit_prepare(struct pt_regs *regs, struct interrupt_state *state)
 {
-#ifdef CONFIG_PPC_BOOK3E_64
-       exception_exit(state->ctx_state);
-#endif
-
-       /*
-        * Book3S exits to user via interrupt_exit_user_prepare(), which does
-        * context tracking, which is a cleaner way to handle PREEMPT=y
-        * and avoid context entry/exit in e.g., preempt_schedule_irq()),
-        * which is likely to be where the core code wants to end up.
-        *
-        * The above comment explains why we can't do the
-        *
-        *     if (user_mode(regs))
-        *         user_exit_irqoff();
-        *
-        * sequence here.
-        */
+       if (user_mode(regs))
+               kuep_unlock();
 }
 
 static inline void interrupt_async_enter_prepare(struct pt_regs *regs, struct interrupt_state *state)
@@ -109,24 +107,46 @@ static inline void interrupt_async_enter_prepare(struct pt_regs *regs, struct in
 
 static inline void interrupt_async_exit_prepare(struct pt_regs *regs, struct interrupt_state *state)
 {
+       /*
+        * Adjust at exit so the main handler sees the true NIA. This must
+        * come before irq_exit() because irq_exit can enable interrupts, and
+        * if another interrupt is taken before nap_adjust_return has run
+        * here, then that interrupt would return directly to idle nap return.
+        */
+       nap_adjust_return(regs);
+
        irq_exit();
        interrupt_exit_prepare(regs, state);
 }
 
 struct interrupt_nmi_state {
 #ifdef CONFIG_PPC64
-#ifdef CONFIG_PPC_BOOK3S_64
        u8 irq_soft_mask;
        u8 irq_happened;
-#endif
        u8 ftrace_enabled;
 #endif
 };
 
+static inline bool nmi_disables_ftrace(struct pt_regs *regs)
+{
+       /* Allow DEC and PMI to be traced when they are soft-NMI */
+       if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) {
+               if (TRAP(regs) == 0x900)
+                      return false;
+               if (TRAP(regs) == 0xf00)
+                      return false;
+       }
+       if (IS_ENABLED(CONFIG_PPC_BOOK3E)) {
+               if (TRAP(regs) == 0x260)
+                       return false;
+       }
+
+       return true;
+}
+
 static inline void interrupt_nmi_enter_prepare(struct pt_regs *regs, struct interrupt_nmi_state *state)
 {
 #ifdef CONFIG_PPC64
-#ifdef CONFIG_PPC_BOOK3S_64
        state->irq_soft_mask = local_paca->irq_soft_mask;
        state->irq_happened = local_paca->irq_happened;
 
@@ -139,9 +159,8 @@ static inline void interrupt_nmi_enter_prepare(struct pt_regs *regs, struct inte
        local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
 
        /* Don't do any per-CPU operations until interrupt state is fixed */
-#endif
-       /* Allow DEC and PMI to be traced when they are soft-NMI */
-       if (TRAP(regs) != 0x900 && TRAP(regs) != 0xf00 && TRAP(regs) != 0x260) {
+
+       if (nmi_disables_ftrace(regs)) {
                state->ftrace_enabled = this_cpu_get_ftrace_enabled();
                this_cpu_set_ftrace_enabled(0);
        }
@@ -164,17 +183,20 @@ static inline void interrupt_nmi_exit_prepare(struct pt_regs *regs, struct inter
                        radix_enabled() || (mfmsr() & MSR_DR))
                nmi_exit();
 
+       /*
+        * nmi does not call nap_adjust_return because nmi should not create
+        * new work to do (must use irq_work for that).
+        */
+
 #ifdef CONFIG_PPC64
-       if (TRAP(regs) != 0x900 && TRAP(regs) != 0xf00 && TRAP(regs) != 0x260)
+       if (nmi_disables_ftrace(regs))
                this_cpu_set_ftrace_enabled(state->ftrace_enabled);
 
-#ifdef CONFIG_PPC_BOOK3S_64
        /* Check we didn't change the pending interrupt mask. */
        WARN_ON_ONCE((state->irq_happened | PACA_IRQ_HARD_DIS) != local_paca->irq_happened);
        local_paca->irq_happened = state->irq_happened;
        local_paca->irq_soft_mask = state->irq_soft_mask;
 #endif
-#endif
 }
 
 /*
@@ -387,6 +409,7 @@ DECLARE_INTERRUPT_HANDLER(SMIException);
 DECLARE_INTERRUPT_HANDLER(handle_hmi_exception);
 DECLARE_INTERRUPT_HANDLER(unknown_exception);
 DECLARE_INTERRUPT_HANDLER_ASYNC(unknown_async_exception);
+DECLARE_INTERRUPT_HANDLER_NMI(unknown_nmi_exception);
 DECLARE_INTERRUPT_HANDLER(instruction_breakpoint_exception);
 DECLARE_INTERRUPT_HANDLER(RunModeException);
 DECLARE_INTERRUPT_HANDLER(single_step_exception);
@@ -410,8 +433,7 @@ DECLARE_INTERRUPT_HANDLER(altivec_assist_exception);
 DECLARE_INTERRUPT_HANDLER(CacheLockingException);
 DECLARE_INTERRUPT_HANDLER(SPEFloatingPointException);
 DECLARE_INTERRUPT_HANDLER(SPEFloatingPointRoundException);
-DECLARE_INTERRUPT_HANDLER(unrecoverable_exception);
-DECLARE_INTERRUPT_HANDLER(WatchdogException);
+DECLARE_INTERRUPT_HANDLER_NMI(WatchdogException);
 DECLARE_INTERRUPT_HANDLER(kernel_bad_stack);
 
 /* slb.c */
@@ -437,6 +459,8 @@ DECLARE_INTERRUPT_HANDLER_NMI(hmi_exception_realmode);
 
 DECLARE_INTERRUPT_HANDLER_ASYNC(TAUException);
 
+void __noreturn unrecoverable_exception(struct pt_regs *regs);
+
 void replay_system_reset(void);
 void replay_soft_interrupts(void);