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
5 changed files with 250 additions and 71 deletions
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue