Blackfin arch: add supporting for double fault debug handling

Signed-off-by: Robin Getz <rgetz@blackfin.uclinux.org>
Signed-off-by: Mike Frysinger <vapier.adi@gmail.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
This commit is contained in:
Robin Getz 2008-10-08 16:27:12 +08:00 committed by Bryan Wu
parent f4585a0847
commit 0c7a6b2135
5 changed files with 250 additions and 71 deletions

View File

@ -22,6 +22,44 @@ config DEBUG_HWERR
hardware error interrupts and need to know where they are coming hardware error interrupts and need to know where they are coming
from. from.
config DEBUG_DOUBLEFAULT
bool "Debug Double Faults"
default n
help
If an exception is caused while executing code within the exception
handler, the NMI handler, the reset vector, or in emulator mode,
a double fault occurs. On the Blackfin, this is a unrecoverable
event. You have two options:
- RESET exactly when double fault occurs. The excepting
instruction address is stored in RETX, where the next kernel
boot will print it out.
- Print debug message. This is much more error prone, although
easier to handle. It is error prone since:
- The excepting instruction is not committed.
- All writebacks from the instruction are prevented.
- The generated exception is not taken.
- The EXCAUSE field is updated with an unrecoverable event
The only way to check this is to see if EXCAUSE contains the
unrecoverable event value at every exception return. By selecting
this option, you are skipping over the faulting instruction, and
hoping things stay together enough to print out a debug message.
This does add a little kernel code, but is the only method to debug
double faults - if unsure say "Y"
choice
prompt "Double Fault Failure Method"
default DEBUG_DOUBLEFAULT_PRINT
depends on DEBUG_DOUBLEFAULT
config DEBUG_DOUBLEFAULT_PRINT
bool "Print"
config DEBUG_DOUBLEFAULT_RESET
bool "Reset"
endchoice
config DEBUG_ICACHE_CHECK config DEBUG_ICACHE_CHECK
bool "Check Instruction cache coherency" bool "Check Instruction cache coherency"
depends on DEBUG_KERNEL depends on DEBUG_KERNEL

View File

@ -52,7 +52,8 @@ EXPORT_SYMBOL(mtd_size);
#endif #endif
char __initdata command_line[COMMAND_LINE_SIZE]; char __initdata command_line[COMMAND_LINE_SIZE];
unsigned int __initdata *__retx; void __initdata *init_retx, *init_saved_retx, *init_saved_seqstat,
*init_saved_icplb_fault_addr, *init_saved_dcplb_fault_addr;
/* boot memmap, for parsing "memmap=" */ /* boot memmap, for parsing "memmap=" */
#define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */ #define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */
@ -782,16 +783,25 @@ void __init setup_arch(char **cmdline_p)
_bfin_swrst = bfin_read_SWRST(); _bfin_swrst = bfin_read_SWRST();
/* If we double fault, reset the system - otherwise we hang forever */ #ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
bfin_write_SWRST(DOUBLE_FAULT); bfin_write_SWRST(_bfin_swrst & ~DOUBLE_FAULT);
#endif
#ifdef CONFIG_DEBUG_DOUBLEFAULT_RESET
bfin_write_SWRST(_bfin_swrst | DOUBLE_FAULT);
#endif
if (_bfin_swrst & RESET_DOUBLE) if (_bfin_swrst & RESET_DOUBLE) {
/* printk(KERN_EMERG "Recovering from DOUBLE FAULT event\n");
* don't decode the address, since you don't know if this #ifdef CONFIG_DEBUG_DOUBLEFAULT
* kernel's symbol map is the same as the crashing kernel /* We assume the crashing kernel, and the current symbol table match */
*/ printk(KERN_EMERG " While handling exception (EXCAUSE = 0x%x) at %pF\n",
printk(KERN_INFO "Recovering from Double Fault event at %pF\n", __retx); (int)init_saved_seqstat & SEQSTAT_EXCAUSE, init_saved_retx);
else if (_bfin_swrst & RESET_WDOG) printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %pF\n", init_saved_dcplb_fault_addr);
printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %pF\n", init_saved_icplb_fault_addr);
#endif
printk(KERN_NOTICE " The instruction at %pF caused a double exception\n",
init_retx);
} else if (_bfin_swrst & RESET_WDOG)
printk(KERN_INFO "Recovering from Watchdog event\n"); printk(KERN_INFO "Recovering from Watchdog event\n");
else if (_bfin_swrst & RESET_SOFTWARE) else if (_bfin_swrst & RESET_SOFTWARE)
printk(KERN_NOTICE "Reset caused by Software reset\n"); printk(KERN_NOTICE "Reset caused by Software reset\n");

View File

@ -68,7 +68,15 @@ void __init trap_init(void)
CSYNC(); CSYNC();
} }
unsigned long saved_icplb_fault_addr, saved_dcplb_fault_addr; /*
* Used to save the RETX, SEQSTAT, I/D CPLB FAULT ADDR
* values across the transition from exception to IRQ5.
* We put these in L1, so they are going to be in a valid
* location during exception context
*/
__attribute__((l1_data))
unsigned long saved_retx, saved_seqstat,
saved_icplb_fault_addr, saved_dcplb_fault_addr;
static void decode_address(char *buf, unsigned long address) static void decode_address(char *buf, unsigned long address)
{ {
@ -186,9 +194,27 @@ asmlinkage void double_fault_c(struct pt_regs *fp)
console_verbose(); console_verbose();
oops_in_progress = 1; oops_in_progress = 1;
printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n");
#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
if (((long)fp->seqstat & SEQSTAT_EXCAUSE) == VEC_UNCOV) {
char buf[150];
decode_address(buf, saved_retx);
printk(KERN_EMERG "While handling exception (EXCAUSE = 0x%x) at %s:\n",
(int)saved_seqstat & SEQSTAT_EXCAUSE, buf);
decode_address(buf, saved_dcplb_fault_addr);
printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %s\n", buf);
decode_address(buf, saved_icplb_fault_addr);
printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %s\n", buf);
decode_address(buf, fp->retx);
printk(KERN_NOTICE "The instruction at %s caused a double exception\n",
buf);
} else
#endif
{
dump_bfin_process(fp); dump_bfin_process(fp);
dump_bfin_mem(fp); dump_bfin_mem(fp);
show_regs(fp); show_regs(fp);
}
panic("Double Fault - unrecoverable event\n"); panic("Double Fault - unrecoverable event\n");
} }

View File

@ -129,6 +129,18 @@ ENTRY(_ex_icplb_miss)
#else #else
call __cplb_hdr; call __cplb_hdr;
#endif #endif
#ifdef CONFIG_DEBUG_DOUBLEFAULT
/* While we were processing this, did we double fault? */
r7 = SEQSTAT; /* reason code is in bit 5:0 */
r6.l = lo(SEQSTAT_EXCAUSE);
r6.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6;
r6 = 0x25;
CC = R7 == R6;
if CC JUMP _double_fault;
#endif
DEBUG_HWTRACE_RESTORE(p5, r7) DEBUG_HWTRACE_RESTORE(p5, r7)
RESTORE_ALL_SYS RESTORE_ALL_SYS
SP = EX_SCRATCH_REG; SP = EX_SCRATCH_REG;
@ -136,11 +148,8 @@ ENTRY(_ex_icplb_miss)
ENDPROC(_ex_icplb_miss) ENDPROC(_ex_icplb_miss)
ENTRY(_ex_syscall) ENTRY(_ex_syscall)
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
raise 15; /* invoked by TRAP #0, for sys call */ raise 15; /* invoked by TRAP #0, for sys call */
sp = EX_SCRATCH_REG; jump.s _bfin_return_from_exception;
rtx
ENDPROC(_ex_syscall) ENDPROC(_ex_syscall)
ENTRY(_ex_soft_bp) ENTRY(_ex_soft_bp)
@ -250,6 +259,29 @@ ENTRY(_bfin_return_from_exception)
R7=LC1; R7=LC1;
LC1=R7; LC1=R7;
#endif #endif
#ifdef CONFIG_DEBUG_DOUBLEFAULT
/* While we were processing the current exception,
* did we cause another, and double fault?
*/
r7 = SEQSTAT; /* reason code is in bit 5:0 */
r6.l = lo(SEQSTAT_EXCAUSE);
r6.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6;
r6 = 0x25;
CC = R7 == R6;
if CC JUMP _double_fault;
/* Did we cause a HW error? */
p5.l = lo(ILAT);
p5.h = hi(ILAT);
r6 = [p5];
r7 = 0x20; /* Did I just cause anther HW error? */
r7 = r7 & r1;
CC = R7 == R6;
if CC JUMP _double_fault;
#endif
(R7:6,P5:4) = [sp++]; (R7:6,P5:4) = [sp++];
ASTAT = [sp++]; ASTAT = [sp++];
sp = EX_SCRATCH_REG; sp = EX_SCRATCH_REG;
@ -292,6 +324,14 @@ ENTRY(_ex_trap_c)
[p4] = p5; [p4] = p5;
csync; csync;
#ifndef CONFIG_DEBUG_DOUBLEFAULT
/*
* Save these registers, as they are only valid in exception context
* (where we are now - as soon as we defer to IRQ5, they can change)
* DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3,
* but they are not very interesting, so don't save them
*/
p4.l = lo(DCPLB_FAULT_ADDR); p4.l = lo(DCPLB_FAULT_ADDR);
p4.h = hi(DCPLB_FAULT_ADDR); p4.h = hi(DCPLB_FAULT_ADDR);
r7 = [p4]; r7 = [p4];
@ -304,12 +344,11 @@ ENTRY(_ex_trap_c)
p5.l = _saved_icplb_fault_addr; p5.l = _saved_icplb_fault_addr;
[p5] = r7; [p5] = r7;
p4.l = _excpt_saved_stuff;
p4.h = _excpt_saved_stuff;
r6 = retx; r6 = retx;
p4.l = _saved_retx;
p4.h = _saved_retx;
[p4] = r6; [p4] = r6;
#endif
r6 = SYSCFG; r6 = SYSCFG;
[p4 + 4] = r6; [p4 + 4] = r6;
BITCLR(r6, 0); BITCLR(r6, 0);
@ -327,11 +366,8 @@ ENTRY(_ex_trap_c)
r6 = 0x3f; r6 = 0x3f;
sti r6; sti r6;
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
SP = EX_SCRATCH_REG;
raise 5; raise 5;
rtx; jump.s _bfin_return_from_exception;
ENDPROC(_ex_trap_c) ENDPROC(_ex_trap_c)
/* We just realized we got an exception, while we were processing a different /* We just realized we got an exception, while we were processing a different
@ -388,8 +424,8 @@ ENDPROC(_double_fault)
ENTRY(_exception_to_level5) ENTRY(_exception_to_level5)
SAVE_ALL_SYS SAVE_ALL_SYS
p4.l = _excpt_saved_stuff; p4.l = _saved_retx;
p4.h = _excpt_saved_stuff; p4.h = _saved_retx;
r6 = [p4]; r6 = [p4];
[sp + PT_PC] = r6; [sp + PT_PC] = r6;
@ -420,6 +456,17 @@ ENTRY(_exception_to_level5)
call _trap_c; call _trap_c;
SP += 12; 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
call _ret_from_exception; call _ret_from_exception;
RESTORE_ALL_SYS RESTORE_ALL_SYS
rti; rti;
@ -436,7 +483,39 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
/* Try to deal with syscalls quickly. */ /* Try to deal with syscalls quickly. */
[--sp] = ASTAT; [--sp] = ASTAT;
[--sp] = (R7:6,P5:4); [--sp] = (R7:6,P5:4);
#ifdef CONFIG_DEBUG_DOUBLEFAULT
/*
* Save these registers, as they are only valid in exception context
* (where we are now - as soon as we defer to IRQ5, they can change)
* DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3,
* but they are not very interesting, so don't save them
*/
p4.l = lo(DCPLB_FAULT_ADDR);
p4.h = hi(DCPLB_FAULT_ADDR);
r7 = [p4];
p5.h = _saved_dcplb_fault_addr;
p5.l = _saved_dcplb_fault_addr;
[p5] = r7;
r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)];
p5.h = _saved_icplb_fault_addr;
p5.l = _saved_icplb_fault_addr;
[p5] = r7;
p4.l = _saved_retx;
p4.h = _saved_retx;
r6 = retx;
[p4] = r6;
r7 = SEQSTAT; /* reason code is in bit 5:0 */ r7 = SEQSTAT; /* reason code is in bit 5:0 */
p4.l = _saved_seqstat;
p4.h = _saved_seqstat;
[p4] = r7;
#else
r7 = SEQSTAT; /* reason code is in bit 5:0 */
#endif
r6.l = lo(SEQSTAT_EXCAUSE); r6.l = lo(SEQSTAT_EXCAUSE);
r6.h = hi(SEQSTAT_EXCAUSE); r6.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6; r7 = r7 & r6;
@ -1432,15 +1511,7 @@ ENTRY(_sys_call_table)
.rept NR_syscalls-(.-_sys_call_table)/4 .rept NR_syscalls-(.-_sys_call_table)/4
.long _sys_ni_syscall .long _sys_ni_syscall
.endr .endr
END(_sys_call_table)
/*
* Used to save the real RETX, IMASK and SYSCFG when temporarily
* storing safe values across the transition from exception to IRQ5.
*/
_excpt_saved_stuff:
.long 0;
.long 0;
.long 0;
_exception_stack: _exception_stack:
.rept 1024 .rept 1024

View File

@ -90,12 +90,46 @@ ENTRY(__start)
[p0] = R0; [p0] = R0;
SSYNC; SSYNC;
/* Save RETX, in case of doublefault */ /* in case of double faults, save a few things */
p0.l = ___retx; p0.l = _init_retx;
p0.h = ___retx; p0.h = _init_retx;
R0 = RETX; R0 = RETX;
[P0] = R0; [P0] = R0;
#ifdef CONFIG_DEBUG_DOUBLEFAULT
/* Only save these if we are storing them,
* This happens here, since L1 gets clobbered
* below
*/
p0.l = _saved_retx;
p0.h = _saved_retx;
p1.l = _init_saved_retx;
p1.h = _init_saved_retx;
r0 = [p0];
[p1] = r0;
p0.l = _saved_dcplb_fault_addr;
p0.h = _saved_dcplb_fault_addr;
p1.l = _init_saved_dcplb_fault_addr;
p1.h = _init_saved_dcplb_fault_addr;
r0 = [p0];
[p1] = r0;
p0.l = _saved_icplb_fault_addr;
p0.h = _saved_icplb_fault_addr;
p1.l = _init_saved_icplb_fault_addr;
p1.h = _init_saved_icplb_fault_addr;
r0 = [p0];
[p1] = r0;
p0.l = _saved_seqstat;
p0.h = _saved_seqstat;
p1.l = _init_saved_seqstat;
p1.h = _init_saved_seqstat;
r0 = [p0];
[p1] = r0;
#endif
/* Initialize stack pointer */ /* Initialize stack pointer */
sp.l = lo(INITIAL_STACK); sp.l = lo(INITIAL_STACK);
sp.h = hi(INITIAL_STACK); sp.h = hi(INITIAL_STACK);