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:
parent
f4585a0847
commit
0c7a6b2135
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue