Blackfin: sanitize manual control of IPEND[4]
[deliverable/linux.git] / arch / blackfin / mach-common / entry.S
index fb1795d5be2af2e9765f31c1c40fafd90257580e..2c58b60ff1313b2665ecedb386c075ceef886233 100644 (file)
@@ -301,25 +301,31 @@ ENTRY(_ex_replaceable)
        nop;
 
 ENTRY(_ex_trap_c)
+       /* The only thing that has been saved in this context is
+        * (R7:6,P5:4), ASTAT & SP - don't use anything else
+        */
+
+       GET_PDA(p5, r6);
+
        /* Make sure we are not in a double fault */
        p4.l = lo(IPEND);
        p4.h = hi(IPEND);
        r7 = [p4];
        CC = BITTST (r7, 5);
        if CC jump _double_fault;
+       [p5 + PDA_EXIPEND] = r7;
 
        /* Call C code (trap_c) to handle the exception, which most
         * likely involves sending a signal to the current process.
         * To avoid double faults, lower our priority to IRQ5 first.
         */
-       P5.h = _exception_to_level5;
-       P5.l = _exception_to_level5;
+       r7.h = _exception_to_level5;
+       r7.l = _exception_to_level5;
        p4.l = lo(EVT5);
        p4.h = hi(EVT5);
-       [p4] = p5;
+       [p4] = r7;
        csync;
 
-       GET_PDA(p5, r6);
 #ifndef CONFIG_DEBUG_DOUBLEFAULT
 
        /*
@@ -349,8 +355,7 @@ ENTRY(_ex_trap_c)
        BITCLR(r6, SYSCFG_SSSTEP_P);
        SYSCFG = r6;
 
-       /* Disable all interrupts, but make sure level 5 is enabled so
-        * we can switch to that level.  Save the old mask.  */
+       /* Save the current IMASK, since we change in order to jump to level 5 */
        cli r6;
        [p5 + PDA_EXIMASK] = r6;
 
@@ -358,9 +363,21 @@ ENTRY(_ex_trap_c)
        p4.h = hi(SAFE_USER_INSTRUCTION);
        retx = p4;
 
+       /* Disable all interrupts, but make sure level 5 is enabled so
+        * we can switch to that level.
+        */
        r6 = 0x3f;
        sti r6;
 
+       /* In case interrupts are disabled IPEND[4] (global interrupt disable bit)
+        * clear it (re-enabling interrupts again) by the special sequence of pushing
+        * RETI onto the stack.  This way we can lower ourselves to IVG5 even if the
+        * exception was taken after the interrupt handler was called but before it
+        * got a chance to enable global interrupts itself.
+        */
+       [--sp] = reti;
+       sp += 4;
+
        raise 5;
        jump.s _bfin_return_from_exception;
 ENDPROC(_ex_trap_c)
@@ -420,47 +437,52 @@ ENDPROC(_double_fault)
 ENTRY(_exception_to_level5)
        SAVE_ALL_SYS
 
-       GET_PDA(p4, r7);        /* Fetch current PDA */
-       r6 = [p4 + PDA_RETX];
+       GET_PDA(p5, r7);        /* Fetch current PDA */
+       r6 = [p5 + PDA_RETX];
        [sp + PT_PC] = r6;
 
-       r6 = [p4 + PDA_SYSCFG];
+       r6 = [p5 + PDA_SYSCFG];
        [sp + PT_SYSCFG] = r6;
 
-       /* Restore interrupt mask.  We haven't pushed RETI, so this
-        * doesn't enable interrupts until we return from this handler.  */
-       r6 = [p4 + PDA_EXIMASK];
-       sti r6;
-
        /* Restore the hardware error vector.  */
-       P5.h = _evt_ivhw;
-       P5.l = _evt_ivhw;
+       r7.h = _evt_ivhw;
+       r7.l = _evt_ivhw;
        p4.l = lo(EVT5);
        p4.h = hi(EVT5);
-       [p4] = p5;
+       [p4] = r7;
        csync;
 
-       p2.l = lo(IPEND);
-       p2.h = hi(IPEND);
-       csync;
-       r0 = [p2];              /* Read current IPEND */
-       [sp + PT_IPEND] = r0;   /* Store IPEND */
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
+       /* Now that we have the hardware error vector programmed properly
+        * we can re-enable interrupts (IPEND[4]), so if the _trap_c causes
+        * another hardware error, we can catch it (self-nesting).
+        */
+       [--sp] = reti;
+       sp += 4;
+#endif
+
+       r7 = [p5 + PDA_EXIPEND] /* Read the IPEND from the Exception state */
+       [sp + PT_IPEND] = r7;   /* Store IPEND onto the stack */
 
        r0 = sp;        /* stack frame pt_regs pointer argument ==> r0 */
        SP += -12;
        call _trap_c;
        SP += 12;
 
-#ifdef CONFIG_DEBUG_DOUBLEFAULT
-       /* Grab ILAT */
-       p2.l = lo(ILAT);
-       p2.h = hi(ILAT);
-       r0 = [p2];
-       r1 = 0x20;  /* Did I just cause anther HW error? */
-       r0 = r0 & r1;
-       CC = R0 == R1;
-       if CC JUMP _double_fault;
-#endif
+       /* If interrupts were off during the exception (IPEND[4] = 1), turn them off
+        * before we return.
+        */
+       CC = BITTST(r7, EVT_IRPTEN_P)
+       if !CC jump 1f;
+       /* this will load a random value into the reti register - but that is OK,
+        * since we do restore it to the correct value in the 'RESTORE_ALL_SYS' macro
+        */
+       sp += -4;
+       reti = [sp++];
+1:
+       /* restore the interrupt mask (IMASK) */
+       r6 = [p5 + PDA_EXIMASK];
+       sti r6;
 
        call _ret_from_exception;
        RESTORE_ALL_SYS
@@ -686,8 +708,14 @@ ENTRY(_system_call)
 #ifdef CONFIG_IPIPE
        cc = BITTST(r7, TIF_IRQ_SYNC);
        if !cc jump .Lsyscall_no_irqsync;
+       /*
+        * Clear IPEND[4] manually to undo what resume_userspace_1 just did;
+        * we need this so that high priority domain interrupts may still
+        * preempt the current domain while the pipeline log is being played
+        * back.
+        */
        [--sp] = reti;
-       r0 = [sp++];
+       SP += 4; /* don't merge with next insn to keep the pattern obvious */
        SP += -12;
        call ___ipipe_sync_root;
        SP += 12;
@@ -699,7 +727,7 @@ ENTRY(_system_call)
 
        /* Reenable interrupts.  */
        [--sp] = reti;
-       r0 = [sp++];
+       sp += 4;
 
        SP += -12;
        call _schedule;
@@ -715,7 +743,7 @@ ENTRY(_system_call)
 .Lsyscall_do_signals:
        /* Reenable interrupts.  */
        [--sp] = reti;
-       r0 = [sp++];
+       sp += 4;
 
        r0 = sp;
        SP += -12;
@@ -725,10 +753,6 @@ ENTRY(_system_call)
 .Lsyscall_really_exit:
        r5 = [sp + PT_RESERVED];
        rets = r5;
-#ifdef CONFIG_IPIPE
-       [--sp] = reti;
-       r5 = [sp++];
-#endif /* CONFIG_IPIPE */
        rts;
 ENDPROC(_system_call)
 
@@ -882,14 +906,9 @@ ENDPROC(_ret_from_exception)
 
 #ifdef CONFIG_IPIPE
 
-_sync_root_irqs:
-       [--sp] = reti;          /* Reenable interrupts */
-       r0 = [sp++];
-       jump.l ___ipipe_sync_root
-
 _resume_kernel_from_int:
-       r0.l = _sync_root_irqs
-       r0.h = _sync_root_irqs
+       r0.l = ___ipipe_sync_root;
+       r0.h = ___ipipe_sync_root;
        [--sp] = rets;
        [--sp] = ( r7:4, p5:3 );
        SP += -12;
@@ -953,10 +972,10 @@ ENTRY(_lower_to_irq14)
 #endif
 
 #ifdef CONFIG_DEBUG_HWERR
-       /* enable irq14 & hwerr interrupt, until we transition to _evt14_softirq */
+       /* enable irq14 & hwerr interrupt, until we transition to _evt_evt14 */
        r0 = (EVT_IVG14 | EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU);
 #else
-       /* Only enable irq14 interrupt, until we transition to _evt14_softirq */
+       /* Only enable irq14 interrupt, until we transition to _evt_evt14 */
        r0 = (EVT_IVG14 | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU);
 #endif
        sti r0;
@@ -964,7 +983,7 @@ ENTRY(_lower_to_irq14)
        rti;
 ENDPROC(_lower_to_irq14)
 
-ENTRY(_evt14_softirq)
+ENTRY(_evt_evt14)
 #ifdef CONFIG_DEBUG_HWERR
        r0 = (EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU);
        sti r0;
@@ -974,7 +993,7 @@ ENTRY(_evt14_softirq)
        [--sp] = RETI;
        SP += 4;
        rts;
-ENDPROC(_evt14_softirq)
+ENDPROC(_evt_evt14)
 
 ENTRY(_schedule_and_signal_from_int)
        /* To end up here, vector 15 was changed - so we have to change it
This page took 0.027163 seconds and 5 git commands to generate.