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
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
bool "Check Instruction cache coherency"
depends on DEBUG_KERNEL

View File

@ -52,7 +52,8 @@ EXPORT_SYMBOL(mtd_size);
#endif
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=" */
#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();
/* If we double fault, reset the system - otherwise we hang forever */
bfin_write_SWRST(DOUBLE_FAULT);
#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
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)
/*
* don't decode the address, since you don't know if this
* kernel's symbol map is the same as the crashing kernel
*/
printk(KERN_INFO "Recovering from Double Fault event at %pF\n", __retx);
else if (_bfin_swrst & RESET_WDOG)
if (_bfin_swrst & RESET_DOUBLE) {
printk(KERN_EMERG "Recovering from DOUBLE FAULT event\n");
#ifdef CONFIG_DEBUG_DOUBLEFAULT
/* We assume the crashing kernel, and the current symbol table match */
printk(KERN_EMERG " While handling exception (EXCAUSE = 0x%x) at %pF\n",
(int)init_saved_seqstat & SEQSTAT_EXCAUSE, init_saved_retx);
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");
else if (_bfin_swrst & RESET_SOFTWARE)
printk(KERN_NOTICE "Reset caused by Software reset\n");

View File

@ -68,7 +68,15 @@ void __init trap_init(void)
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)
{
@ -186,9 +194,27 @@ asmlinkage void double_fault_c(struct pt_regs *fp)
console_verbose();
oops_in_progress = 1;
printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n");
dump_bfin_process(fp);
dump_bfin_mem(fp);
show_regs(fp);
#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_mem(fp);
show_regs(fp);
}
panic("Double Fault - unrecoverable event\n");
}

View File

@ -129,6 +129,18 @@ ENTRY(_ex_icplb_miss)
#else
call __cplb_hdr;
#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)
RESTORE_ALL_SYS
SP = EX_SCRATCH_REG;
@ -136,11 +148,8 @@ ENTRY(_ex_icplb_miss)
ENDPROC(_ex_icplb_miss)
ENTRY(_ex_syscall)
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
raise 15; /* invoked by TRAP #0, for sys call */
sp = EX_SCRATCH_REG;
rtx
jump.s _bfin_return_from_exception;
ENDPROC(_ex_syscall)
ENTRY(_ex_soft_bp)
@ -250,6 +259,29 @@ ENTRY(_bfin_return_from_exception)
R7=LC1;
LC1=R7;
#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++];
ASTAT = [sp++];
sp = EX_SCRATCH_REG;
@ -292,6 +324,14 @@ ENTRY(_ex_trap_c)
[p4] = p5;
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.h = hi(DCPLB_FAULT_ADDR);
r7 = [p4];
@ -304,12 +344,11 @@ ENTRY(_ex_trap_c)
p5.l = _saved_icplb_fault_addr;
[p5] = r7;
p4.l = _excpt_saved_stuff;
p4.h = _excpt_saved_stuff;
r6 = retx;
p4.l = _saved_retx;
p4.h = _saved_retx;
[p4] = r6;
#endif
r6 = SYSCFG;
[p4 + 4] = r6;
BITCLR(r6, 0);
@ -327,59 +366,56 @@ ENTRY(_ex_trap_c)
r6 = 0x3f;
sti r6;
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
SP = EX_SCRATCH_REG;
raise 5;
rtx;
jump.s _bfin_return_from_exception;
ENDPROC(_ex_trap_c)
/* We just realized we got an exception, while we were processing a different
* exception. This is a unrecoverable event, so crash
*/
ENTRY(_double_fault)
/* Turn caches & protection off, to ensure we don't get any more
* double exceptions
*/
/* Turn caches & protection off, to ensure we don't get any more
* double exceptions
*/
P4.L = LO(IMEM_CONTROL);
P4.H = HI(IMEM_CONTROL);
P4.L = LO(IMEM_CONTROL);
P4.H = HI(IMEM_CONTROL);
R5 = [P4]; /* Control Register*/
BITCLR(R5,ENICPLB_P);
SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
.align 8;
[P4] = R5;
SSYNC;
R5 = [P4]; /* Control Register*/
BITCLR(R5,ENICPLB_P);
SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
.align 8;
[P4] = R5;
SSYNC;
P4.L = LO(DMEM_CONTROL);
P4.H = HI(DMEM_CONTROL);
R5 = [P4];
BITCLR(R5,ENDCPLB_P);
SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
.align 8;
[P4] = R5;
SSYNC;
P4.L = LO(DMEM_CONTROL);
P4.H = HI(DMEM_CONTROL);
R5 = [P4];
BITCLR(R5,ENDCPLB_P);
SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
.align 8;
[P4] = R5;
SSYNC;
/* Fix up the stack */
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
SP = EX_SCRATCH_REG;
/* Fix up the stack */
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
SP = EX_SCRATCH_REG;
/* We should be out of the exception stack, and back down into
* kernel or user space stack
*/
SAVE_ALL_SYS
/* We should be out of the exception stack, and back down into
* kernel or user space stack
*/
SAVE_ALL_SYS
/* The dumping functions expect the return address in the RETI
* slot. */
r6 = retx;
[sp + PT_PC] = r6;
r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */
SP += -12;
call _double_fault_c;
SP += 12;
r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */
SP += -12;
call _double_fault_c;
SP += 12;
.L_double_fault_panic:
JUMP .L_double_fault_panic
@ -388,8 +424,8 @@ ENDPROC(_double_fault)
ENTRY(_exception_to_level5)
SAVE_ALL_SYS
p4.l = _excpt_saved_stuff;
p4.h = _excpt_saved_stuff;
p4.l = _saved_retx;
p4.h = _saved_retx;
r6 = [p4];
[sp + PT_PC] = r6;
@ -420,6 +456,17 @@ ENTRY(_exception_to_level5)
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
call _ret_from_exception;
RESTORE_ALL_SYS
rti;
@ -436,7 +483,39 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
/* Try to deal with syscalls quickly. */
[--sp] = ASTAT;
[--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 */
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.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6;
@ -1432,15 +1511,7 @@ ENTRY(_sys_call_table)
.rept NR_syscalls-(.-_sys_call_table)/4
.long _sys_ni_syscall
.endr
/*
* 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;
END(_sys_call_table)
_exception_stack:
.rept 1024

View File

@ -90,12 +90,46 @@ ENTRY(__start)
[p0] = R0;
SSYNC;
/* Save RETX, in case of doublefault */
p0.l = ___retx;
p0.h = ___retx;
/* in case of double faults, save a few things */
p0.l = _init_retx;
p0.h = _init_retx;
R0 = RETX;
[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 */
sp.l = lo(INITIAL_STACK);
sp.h = hi(INITIAL_STACK);