Commit ae4f073c authored by Robin Getz's avatar Robin Getz Committed by Mike Frysinger

Blackfin: make EVT3->EVT5 lowering more robust wrt IPEND[4]

We handle many exceptions at EVT5 (hardware error level) so that we can
catch exceptions in our exception handling code.  Today - if the global
interrupt enable bit (IPEND[4]) is set (interrupts disabled) our trap
handling code goes into a infinite loop, since we need interrupts to be
on to defer things to EVT5.

Normal kernel code should not trigger this for any reason as IPEND[4] gets
cleared early (when doing an interrupt context save) and the kernel stack
there should be sane (or something much worse is happening in the system).
But there have been a few times where this has happened, so this change
makes sure we dump a proper crash message even when things have gone south.
Signed-off-by: default avatarRobin Getz <robin.getz@analog.com>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
parent d4b834c1
...@@ -50,6 +50,7 @@ struct blackfin_pda { /* Per-processor Data Area */ ...@@ -50,6 +50,7 @@ struct blackfin_pda { /* Per-processor Data Area */
unsigned long ex_optr; unsigned long ex_optr;
unsigned long ex_buf[4]; unsigned long ex_buf[4];
unsigned long ex_imask; /* Saved imask from exception */ unsigned long ex_imask; /* Saved imask from exception */
unsigned long ex_ipend; /* Saved IPEND from exception */
unsigned long *ex_stack; /* Exception stack space */ unsigned long *ex_stack; /* Exception stack space */
#ifdef ANOMALY_05000261 #ifdef ANOMALY_05000261
......
...@@ -145,6 +145,7 @@ int main(void) ...@@ -145,6 +145,7 @@ int main(void)
DEFINE(PDA_EXBUF, offsetof(struct blackfin_pda, ex_buf)); DEFINE(PDA_EXBUF, offsetof(struct blackfin_pda, ex_buf));
DEFINE(PDA_EXIMASK, offsetof(struct blackfin_pda, ex_imask)); DEFINE(PDA_EXIMASK, offsetof(struct blackfin_pda, ex_imask));
DEFINE(PDA_EXSTACK, offsetof(struct blackfin_pda, ex_stack)); DEFINE(PDA_EXSTACK, offsetof(struct blackfin_pda, ex_stack));
DEFINE(PDA_EXIPEND, offsetof(struct blackfin_pda, ex_ipend));
#ifdef ANOMALY_05000261 #ifdef ANOMALY_05000261
DEFINE(PDA_LFRETX, offsetof(struct blackfin_pda, last_cplb_fault_retx)); DEFINE(PDA_LFRETX, offsetof(struct blackfin_pda, last_cplb_fault_retx));
#endif #endif
......
...@@ -267,11 +267,6 @@ asmlinkage void trap_c(struct pt_regs *fp) ...@@ -267,11 +267,6 @@ asmlinkage void trap_c(struct pt_regs *fp)
* double faults if the stack has become corrupt * double faults if the stack has become corrupt
*/ */
#ifndef CONFIG_KGDB
/* IPEND is skipped if KGDB isn't enabled (see entry code) */
fp->ipend = bfin_read_IPEND();
#endif
/* trap_c() will be called for exceptions. During exceptions /* trap_c() will be called for exceptions. During exceptions
* processing, the pc value should be set with retx value. * processing, the pc value should be set with retx value.
* With this change we can cleanup some code in signal.c- TODO * With this change we can cleanup some code in signal.c- TODO
...@@ -1116,10 +1111,16 @@ void show_regs(struct pt_regs *fp) ...@@ -1116,10 +1111,16 @@ void show_regs(struct pt_regs *fp)
verbose_printk(KERN_NOTICE "%s", linux_banner); verbose_printk(KERN_NOTICE "%s", linux_banner);
verbose_printk(KERN_NOTICE "\nSEQUENCER STATUS:\t\t%s\n", verbose_printk(KERN_NOTICE "\nSEQUENCER STATUS:\t\t%s\n", print_tainted());
print_tainted()); verbose_printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx IMASK: %04lx SYSCFG: %04lx\n",
verbose_printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", (long)fp->seqstat, fp->ipend, cpu_pda[smp_processor_id()].ex_imask, fp->syscfg);
(long)fp->seqstat, fp->ipend, fp->syscfg); if (fp->ipend & EVT_IRPTEN)
verbose_printk(KERN_NOTICE " Global Interrupts Disabled (IPEND[4])\n");
if (!(cpu_pda[smp_processor_id()].ex_imask & (EVT_IVG13 | EVT_IVG12 | EVT_IVG11 |
EVT_IVG10 | EVT_IVG9 | EVT_IVG8 | EVT_IVG7 | EVT_IVTMR)))
verbose_printk(KERN_NOTICE " Peripheral interrupts masked off\n");
if (!(cpu_pda[smp_processor_id()].ex_imask & (EVT_IVG15 | EVT_IVG14)))
verbose_printk(KERN_NOTICE " Kernel interrupts masked off\n");
if ((fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) { if ((fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) {
verbose_printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n", verbose_printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n",
(fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14); (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14);
......
...@@ -301,25 +301,31 @@ ENTRY(_ex_replaceable) ...@@ -301,25 +301,31 @@ ENTRY(_ex_replaceable)
nop; nop;
ENTRY(_ex_trap_c) 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 */ /* Make sure we are not in a double fault */
p4.l = lo(IPEND); p4.l = lo(IPEND);
p4.h = hi(IPEND); p4.h = hi(IPEND);
r7 = [p4]; r7 = [p4];
CC = BITTST (r7, 5); CC = BITTST (r7, 5);
if CC jump _double_fault; if CC jump _double_fault;
[p5 + PDA_EXIPEND] = r7;
/* Call C code (trap_c) to handle the exception, which most /* Call C code (trap_c) to handle the exception, which most
* likely involves sending a signal to the current process. * likely involves sending a signal to the current process.
* To avoid double faults, lower our priority to IRQ5 first. * To avoid double faults, lower our priority to IRQ5 first.
*/ */
P5.h = _exception_to_level5; r7.h = _exception_to_level5;
P5.l = _exception_to_level5; r7.l = _exception_to_level5;
p4.l = lo(EVT5); p4.l = lo(EVT5);
p4.h = hi(EVT5); p4.h = hi(EVT5);
[p4] = p5; [p4] = r7;
csync; csync;
GET_PDA(p5, r6);
#ifndef CONFIG_DEBUG_DOUBLEFAULT #ifndef CONFIG_DEBUG_DOUBLEFAULT
/* /*
...@@ -349,8 +355,7 @@ ENTRY(_ex_trap_c) ...@@ -349,8 +355,7 @@ ENTRY(_ex_trap_c)
BITCLR(r6, SYSCFG_SSSTEP_P); BITCLR(r6, SYSCFG_SSSTEP_P);
SYSCFG = r6; SYSCFG = r6;
/* Disable all interrupts, but make sure level 5 is enabled so /* Save the current IMASK, since we change in order to jump to level 5 */
* we can switch to that level. Save the old mask. */
cli r6; cli r6;
[p5 + PDA_EXIMASK] = r6; [p5 + PDA_EXIMASK] = r6;
...@@ -358,9 +363,21 @@ ENTRY(_ex_trap_c) ...@@ -358,9 +363,21 @@ ENTRY(_ex_trap_c)
p4.h = hi(SAFE_USER_INSTRUCTION); p4.h = hi(SAFE_USER_INSTRUCTION);
retx = p4; retx = p4;
/* Disable all interrupts, but make sure level 5 is enabled so
* we can switch to that level.
*/
r6 = 0x3f; r6 = 0x3f;
sti r6; 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; raise 5;
jump.s _bfin_return_from_exception; jump.s _bfin_return_from_exception;
ENDPROC(_ex_trap_c) ENDPROC(_ex_trap_c)
...@@ -420,47 +437,52 @@ ENDPROC(_double_fault) ...@@ -420,47 +437,52 @@ ENDPROC(_double_fault)
ENTRY(_exception_to_level5) ENTRY(_exception_to_level5)
SAVE_ALL_SYS SAVE_ALL_SYS
GET_PDA(p4, r7); /* Fetch current PDA */ GET_PDA(p5, r7); /* Fetch current PDA */
r6 = [p4 + PDA_RETX]; r6 = [p5 + PDA_RETX];
[sp + PT_PC] = r6; [sp + PT_PC] = r6;
r6 = [p4 + PDA_SYSCFG]; r6 = [p5 + PDA_SYSCFG];
[sp + PT_SYSCFG] = r6; [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. */ /* Restore the hardware error vector. */
P5.h = _evt_ivhw; r7.h = _evt_ivhw;
P5.l = _evt_ivhw; r7.l = _evt_ivhw;
p4.l = lo(EVT5); p4.l = lo(EVT5);
p4.h = hi(EVT5); p4.h = hi(EVT5);
[p4] = p5; [p4] = r7;
csync; csync;
p2.l = lo(IPEND); #ifdef CONFIG_DEBUG_DOUBLEFAULT
p2.h = hi(IPEND); /* Now that we have the hardware error vector programmed properly
csync; * we can re-enable interrupts (IPEND[4]), so if the _trap_c causes
r0 = [p2]; /* Read current IPEND */ * another hardware error, we can catch it (self-nesting).
[sp + PT_IPEND] = r0; /* Store IPEND */ */
[--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 */ r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */
SP += -12; SP += -12;
call _trap_c; call _trap_c;
SP += 12; SP += 12;
#ifdef CONFIG_DEBUG_DOUBLEFAULT /* If interrupts were off during the exception (IPEND[4] = 1), turn them off
/* Grab ILAT */ * before we return.
p2.l = lo(ILAT); */
p2.h = hi(ILAT); CC = BITTST(r7, EVT_IRPTEN_P)
r0 = [p2]; if !CC jump 1f;
r1 = 0x20; /* Did I just cause anther HW error? */ /* this will load a random value into the reti register - but that is OK,
r0 = r0 & r1; * since we do restore it to the correct value in the 'RESTORE_ALL_SYS' macro
CC = R0 == R1; */
if CC JUMP _double_fault; sp += -4;
#endif reti = [sp++];
1:
/* restore the interrupt mask (IMASK) */
r6 = [p5 + PDA_EXIMASK];
sti r6;
call _ret_from_exception; call _ret_from_exception;
RESTORE_ALL_SYS RESTORE_ALL_SYS
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment