aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin
diff options
context:
space:
mode:
authorRobin Getz <rgetz@blackfin.uclinux.org>2008-10-08 04:27:12 -0400
committerBryan Wu <cooloney@kernel.org>2008-10-08 04:27:12 -0400
commit0c7a6b2135c1bcb5139ca9ca87f292caafcb9410 (patch)
treeee1e8b36a4dba9120035ddac270e63ba91dc649c /arch/blackfin
parentf4585a08479a730fb809606b8ee327a5398c117c (diff)
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>
Diffstat (limited to 'arch/blackfin')
-rw-r--r--arch/blackfin/Kconfig.debug38
-rw-r--r--arch/blackfin/kernel/setup.c30
-rw-r--r--arch/blackfin/kernel/traps.c34
-rw-r--r--arch/blackfin/mach-common/entry.S189
-rw-r--r--arch/blackfin/mach-common/head.S40
5 files changed, 255 insertions, 76 deletions
diff --git a/arch/blackfin/Kconfig.debug b/arch/blackfin/Kconfig.debug
index c468624d55f0..0afa89818722 100644
--- a/arch/blackfin/Kconfig.debug
+++ b/arch/blackfin/Kconfig.debug
@@ -22,6 +22,44 @@ config DEBUG_HWERR
22 hardware error interrupts and need to know where they are coming 22 hardware error interrupts and need to know where they are coming
23 from. 23 from.
24 24
25config DEBUG_DOUBLEFAULT
26 bool "Debug Double Faults"
27 default n
28 help
29 If an exception is caused while executing code within the exception
30 handler, the NMI handler, the reset vector, or in emulator mode,
31 a double fault occurs. On the Blackfin, this is a unrecoverable
32 event. You have two options:
33 - RESET exactly when double fault occurs. The excepting
34 instruction address is stored in RETX, where the next kernel
35 boot will print it out.
36 - Print debug message. This is much more error prone, although
37 easier to handle. It is error prone since:
38 - The excepting instruction is not committed.
39 - All writebacks from the instruction are prevented.
40 - The generated exception is not taken.
41 - The EXCAUSE field is updated with an unrecoverable event
42 The only way to check this is to see if EXCAUSE contains the
43 unrecoverable event value at every exception return. By selecting
44 this option, you are skipping over the faulting instruction, and
45 hoping things stay together enough to print out a debug message.
46
47 This does add a little kernel code, but is the only method to debug
48 double faults - if unsure say "Y"
49
50choice
51 prompt "Double Fault Failure Method"
52 default DEBUG_DOUBLEFAULT_PRINT
53 depends on DEBUG_DOUBLEFAULT
54
55config DEBUG_DOUBLEFAULT_PRINT
56 bool "Print"
57
58config DEBUG_DOUBLEFAULT_RESET
59 bool "Reset"
60
61endchoice
62
25config DEBUG_ICACHE_CHECK 63config DEBUG_ICACHE_CHECK
26 bool "Check Instruction cache coherency" 64 bool "Check Instruction cache coherency"
27 depends on DEBUG_KERNEL 65 depends on DEBUG_KERNEL
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c
index 7a82d10b4ebf..8e639dc886a3 100644
--- a/arch/blackfin/kernel/setup.c
+++ b/arch/blackfin/kernel/setup.c
@@ -52,7 +52,8 @@ EXPORT_SYMBOL(mtd_size);
52#endif 52#endif
53 53
54char __initdata command_line[COMMAND_LINE_SIZE]; 54char __initdata command_line[COMMAND_LINE_SIZE];
55unsigned int __initdata *__retx; 55void __initdata *init_retx, *init_saved_retx, *init_saved_seqstat,
56 *init_saved_icplb_fault_addr, *init_saved_dcplb_fault_addr;
56 57
57/* boot memmap, for parsing "memmap=" */ 58/* boot memmap, for parsing "memmap=" */
58#define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */ 59#define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */
@@ -782,16 +783,25 @@ void __init setup_arch(char **cmdline_p)
782 783
783 _bfin_swrst = bfin_read_SWRST(); 784 _bfin_swrst = bfin_read_SWRST();
784 785
785 /* If we double fault, reset the system - otherwise we hang forever */ 786#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
786 bfin_write_SWRST(DOUBLE_FAULT); 787 bfin_write_SWRST(_bfin_swrst & ~DOUBLE_FAULT);
788#endif
789#ifdef CONFIG_DEBUG_DOUBLEFAULT_RESET
790 bfin_write_SWRST(_bfin_swrst | DOUBLE_FAULT);
791#endif
787 792
788 if (_bfin_swrst & RESET_DOUBLE) 793 if (_bfin_swrst & RESET_DOUBLE) {
789 /* 794 printk(KERN_EMERG "Recovering from DOUBLE FAULT event\n");
790 * don't decode the address, since you don't know if this 795#ifdef CONFIG_DEBUG_DOUBLEFAULT
791 * kernel's symbol map is the same as the crashing kernel 796 /* We assume the crashing kernel, and the current symbol table match */
792 */ 797 printk(KERN_EMERG " While handling exception (EXCAUSE = 0x%x) at %pF\n",
793 printk(KERN_INFO "Recovering from Double Fault event at %pF\n", __retx); 798 (int)init_saved_seqstat & SEQSTAT_EXCAUSE, init_saved_retx);
794 else if (_bfin_swrst & RESET_WDOG) 799 printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %pF\n", init_saved_dcplb_fault_addr);
800 printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %pF\n", init_saved_icplb_fault_addr);
801#endif
802 printk(KERN_NOTICE " The instruction at %pF caused a double exception\n",
803 init_retx);
804 } else if (_bfin_swrst & RESET_WDOG)
795 printk(KERN_INFO "Recovering from Watchdog event\n"); 805 printk(KERN_INFO "Recovering from Watchdog event\n");
796 else if (_bfin_swrst & RESET_SOFTWARE) 806 else if (_bfin_swrst & RESET_SOFTWARE)
797 printk(KERN_NOTICE "Reset caused by Software reset\n"); 807 printk(KERN_NOTICE "Reset caused by Software reset\n");
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c
index fd24e04fc19e..bd41fca315dd 100644
--- a/arch/blackfin/kernel/traps.c
+++ b/arch/blackfin/kernel/traps.c
@@ -68,7 +68,15 @@ void __init trap_init(void)
68 CSYNC(); 68 CSYNC();
69} 69}
70 70
71unsigned long saved_icplb_fault_addr, saved_dcplb_fault_addr; 71/*
72 * Used to save the RETX, SEQSTAT, I/D CPLB FAULT ADDR
73 * values across the transition from exception to IRQ5.
74 * We put these in L1, so they are going to be in a valid
75 * location during exception context
76 */
77__attribute__((l1_data))
78unsigned long saved_retx, saved_seqstat,
79 saved_icplb_fault_addr, saved_dcplb_fault_addr;
72 80
73static void decode_address(char *buf, unsigned long address) 81static void decode_address(char *buf, unsigned long address)
74{ 82{
@@ -186,9 +194,27 @@ asmlinkage void double_fault_c(struct pt_regs *fp)
186 console_verbose(); 194 console_verbose();
187 oops_in_progress = 1; 195 oops_in_progress = 1;
188 printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); 196 printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n");
189 dump_bfin_process(fp); 197#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
190 dump_bfin_mem(fp); 198 if (((long)fp->seqstat & SEQSTAT_EXCAUSE) == VEC_UNCOV) {
191 show_regs(fp); 199 char buf[150];
200 decode_address(buf, saved_retx);
201 printk(KERN_EMERG "While handling exception (EXCAUSE = 0x%x) at %s:\n",
202 (int)saved_seqstat & SEQSTAT_EXCAUSE, buf);
203 decode_address(buf, saved_dcplb_fault_addr);
204 printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %s\n", buf);
205 decode_address(buf, saved_icplb_fault_addr);
206 printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %s\n", buf);
207
208 decode_address(buf, fp->retx);
209 printk(KERN_NOTICE "The instruction at %s caused a double exception\n",
210 buf);
211 } else
212#endif
213 {
214 dump_bfin_process(fp);
215 dump_bfin_mem(fp);
216 show_regs(fp);
217 }
192 panic("Double Fault - unrecoverable event\n"); 218 panic("Double Fault - unrecoverable event\n");
193 219
194} 220}
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index 847c172a99eb..90c7397036ed 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -129,6 +129,18 @@ ENTRY(_ex_icplb_miss)
129#else 129#else
130 call __cplb_hdr; 130 call __cplb_hdr;
131#endif 131#endif
132
133#ifdef CONFIG_DEBUG_DOUBLEFAULT
134 /* While we were processing this, did we double fault? */
135 r7 = SEQSTAT; /* reason code is in bit 5:0 */
136 r6.l = lo(SEQSTAT_EXCAUSE);
137 r6.h = hi(SEQSTAT_EXCAUSE);
138 r7 = r7 & r6;
139 r6 = 0x25;
140 CC = R7 == R6;
141 if CC JUMP _double_fault;
142#endif
143
132 DEBUG_HWTRACE_RESTORE(p5, r7) 144 DEBUG_HWTRACE_RESTORE(p5, r7)
133 RESTORE_ALL_SYS 145 RESTORE_ALL_SYS
134 SP = EX_SCRATCH_REG; 146 SP = EX_SCRATCH_REG;
@@ -136,11 +148,8 @@ ENTRY(_ex_icplb_miss)
136ENDPROC(_ex_icplb_miss) 148ENDPROC(_ex_icplb_miss)
137 149
138ENTRY(_ex_syscall) 150ENTRY(_ex_syscall)
139 (R7:6,P5:4) = [sp++];
140 ASTAT = [sp++];
141 raise 15; /* invoked by TRAP #0, for sys call */ 151 raise 15; /* invoked by TRAP #0, for sys call */
142 sp = EX_SCRATCH_REG; 152 jump.s _bfin_return_from_exception;
143 rtx
144ENDPROC(_ex_syscall) 153ENDPROC(_ex_syscall)
145 154
146ENTRY(_ex_soft_bp) 155ENTRY(_ex_soft_bp)
@@ -250,6 +259,29 @@ ENTRY(_bfin_return_from_exception)
250 R7=LC1; 259 R7=LC1;
251 LC1=R7; 260 LC1=R7;
252#endif 261#endif
262
263#ifdef CONFIG_DEBUG_DOUBLEFAULT
264 /* While we were processing the current exception,
265 * did we cause another, and double fault?
266 */
267 r7 = SEQSTAT; /* reason code is in bit 5:0 */
268 r6.l = lo(SEQSTAT_EXCAUSE);
269 r6.h = hi(SEQSTAT_EXCAUSE);
270 r7 = r7 & r6;
271 r6 = 0x25;
272 CC = R7 == R6;
273 if CC JUMP _double_fault;
274
275 /* Did we cause a HW error? */
276 p5.l = lo(ILAT);
277 p5.h = hi(ILAT);
278 r6 = [p5];
279 r7 = 0x20; /* Did I just cause anther HW error? */
280 r7 = r7 & r1;
281 CC = R7 == R6;
282 if CC JUMP _double_fault;
283#endif
284
253 (R7:6,P5:4) = [sp++]; 285 (R7:6,P5:4) = [sp++];
254 ASTAT = [sp++]; 286 ASTAT = [sp++];
255 sp = EX_SCRATCH_REG; 287 sp = EX_SCRATCH_REG;
@@ -292,6 +324,14 @@ ENTRY(_ex_trap_c)
292 [p4] = p5; 324 [p4] = p5;
293 csync; 325 csync;
294 326
327#ifndef CONFIG_DEBUG_DOUBLEFAULT
328 /*
329 * Save these registers, as they are only valid in exception context
330 * (where we are now - as soon as we defer to IRQ5, they can change)
331 * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3,
332 * but they are not very interesting, so don't save them
333 */
334
295 p4.l = lo(DCPLB_FAULT_ADDR); 335 p4.l = lo(DCPLB_FAULT_ADDR);
296 p4.h = hi(DCPLB_FAULT_ADDR); 336 p4.h = hi(DCPLB_FAULT_ADDR);
297 r7 = [p4]; 337 r7 = [p4];
@@ -304,12 +344,11 @@ ENTRY(_ex_trap_c)
304 p5.l = _saved_icplb_fault_addr; 344 p5.l = _saved_icplb_fault_addr;
305 [p5] = r7; 345 [p5] = r7;
306 346
307 p4.l = _excpt_saved_stuff;
308 p4.h = _excpt_saved_stuff;
309
310 r6 = retx; 347 r6 = retx;
348 p4.l = _saved_retx;
349 p4.h = _saved_retx;
311 [p4] = r6; 350 [p4] = r6;
312 351#endif
313 r6 = SYSCFG; 352 r6 = SYSCFG;
314 [p4 + 4] = r6; 353 [p4 + 4] = r6;
315 BITCLR(r6, 0); 354 BITCLR(r6, 0);
@@ -327,59 +366,56 @@ ENTRY(_ex_trap_c)
327 r6 = 0x3f; 366 r6 = 0x3f;
328 sti r6; 367 sti r6;
329 368
330 (R7:6,P5:4) = [sp++];
331 ASTAT = [sp++];
332 SP = EX_SCRATCH_REG;
333 raise 5; 369 raise 5;
334 rtx; 370 jump.s _bfin_return_from_exception;
335ENDPROC(_ex_trap_c) 371ENDPROC(_ex_trap_c)
336 372
337/* We just realized we got an exception, while we were processing a different 373/* We just realized we got an exception, while we were processing a different
338 * exception. This is a unrecoverable event, so crash 374 * exception. This is a unrecoverable event, so crash
339 */ 375 */
340ENTRY(_double_fault) 376ENTRY(_double_fault)
341 /* Turn caches & protection off, to ensure we don't get any more 377 /* Turn caches & protection off, to ensure we don't get any more
342 * double exceptions 378 * double exceptions
343 */ 379 */
344 380
345 P4.L = LO(IMEM_CONTROL); 381 P4.L = LO(IMEM_CONTROL);
346 P4.H = HI(IMEM_CONTROL); 382 P4.H = HI(IMEM_CONTROL);
347 383
348 R5 = [P4]; /* Control Register*/ 384 R5 = [P4]; /* Control Register*/
349 BITCLR(R5,ENICPLB_P); 385 BITCLR(R5,ENICPLB_P);
350 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ 386 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
351 .align 8; 387 .align 8;
352 [P4] = R5; 388 [P4] = R5;
353 SSYNC; 389 SSYNC;
354 390
355 P4.L = LO(DMEM_CONTROL); 391 P4.L = LO(DMEM_CONTROL);
356 P4.H = HI(DMEM_CONTROL); 392 P4.H = HI(DMEM_CONTROL);
357 R5 = [P4]; 393 R5 = [P4];
358 BITCLR(R5,ENDCPLB_P); 394 BITCLR(R5,ENDCPLB_P);
359 SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ 395 SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
360 .align 8; 396 .align 8;
361 [P4] = R5; 397 [P4] = R5;
362 SSYNC; 398 SSYNC;
363 399
364 /* Fix up the stack */ 400 /* Fix up the stack */
365 (R7:6,P5:4) = [sp++]; 401 (R7:6,P5:4) = [sp++];
366 ASTAT = [sp++]; 402 ASTAT = [sp++];
367 SP = EX_SCRATCH_REG; 403 SP = EX_SCRATCH_REG;
368 404
369 /* We should be out of the exception stack, and back down into 405 /* We should be out of the exception stack, and back down into
370 * kernel or user space stack 406 * kernel or user space stack
371 */ 407 */
372 SAVE_ALL_SYS 408 SAVE_ALL_SYS
373 409
374 /* The dumping functions expect the return address in the RETI 410 /* The dumping functions expect the return address in the RETI
375 * slot. */ 411 * slot. */
376 r6 = retx; 412 r6 = retx;
377 [sp + PT_PC] = r6; 413 [sp + PT_PC] = r6;
378 414
379 r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ 415 r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */
380 SP += -12; 416 SP += -12;
381 call _double_fault_c; 417 call _double_fault_c;
382 SP += 12; 418 SP += 12;
383.L_double_fault_panic: 419.L_double_fault_panic:
384 JUMP .L_double_fault_panic 420 JUMP .L_double_fault_panic
385 421
@@ -388,8 +424,8 @@ ENDPROC(_double_fault)
388ENTRY(_exception_to_level5) 424ENTRY(_exception_to_level5)
389 SAVE_ALL_SYS 425 SAVE_ALL_SYS
390 426
391 p4.l = _excpt_saved_stuff; 427 p4.l = _saved_retx;
392 p4.h = _excpt_saved_stuff; 428 p4.h = _saved_retx;
393 r6 = [p4]; 429 r6 = [p4];
394 [sp + PT_PC] = r6; 430 [sp + PT_PC] = r6;
395 431
@@ -420,6 +456,17 @@ ENTRY(_exception_to_level5)
420 call _trap_c; 456 call _trap_c;
421 SP += 12; 457 SP += 12;
422 458
459#ifdef CONFIG_DEBUG_DOUBLEFAULT
460 /* Grab ILAT */
461 p2.l = lo(ILAT);
462 p2.h = hi(ILAT);
463 r0 = [p2];
464 r1 = 0x20; /* Did I just cause anther HW error? */
465 r0 = r0 & r1;
466 CC = R0 == R1;
467 if CC JUMP _double_fault;
468#endif
469
423 call _ret_from_exception; 470 call _ret_from_exception;
424 RESTORE_ALL_SYS 471 RESTORE_ALL_SYS
425 rti; 472 rti;
@@ -436,7 +483,39 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
436 /* Try to deal with syscalls quickly. */ 483 /* Try to deal with syscalls quickly. */
437 [--sp] = ASTAT; 484 [--sp] = ASTAT;
438 [--sp] = (R7:6,P5:4); 485 [--sp] = (R7:6,P5:4);
486
487#ifdef CONFIG_DEBUG_DOUBLEFAULT
488 /*
489 * Save these registers, as they are only valid in exception context
490 * (where we are now - as soon as we defer to IRQ5, they can change)
491 * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3,
492 * but they are not very interesting, so don't save them
493 */
494
495 p4.l = lo(DCPLB_FAULT_ADDR);
496 p4.h = hi(DCPLB_FAULT_ADDR);
497 r7 = [p4];
498 p5.h = _saved_dcplb_fault_addr;
499 p5.l = _saved_dcplb_fault_addr;
500 [p5] = r7;
501
502 r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)];
503 p5.h = _saved_icplb_fault_addr;
504 p5.l = _saved_icplb_fault_addr;
505 [p5] = r7;
506
507 p4.l = _saved_retx;
508 p4.h = _saved_retx;
509 r6 = retx;
510 [p4] = r6;
511
439 r7 = SEQSTAT; /* reason code is in bit 5:0 */ 512 r7 = SEQSTAT; /* reason code is in bit 5:0 */
513 p4.l = _saved_seqstat;
514 p4.h = _saved_seqstat;
515 [p4] = r7;
516#else
517 r7 = SEQSTAT; /* reason code is in bit 5:0 */
518#endif
440 r6.l = lo(SEQSTAT_EXCAUSE); 519 r6.l = lo(SEQSTAT_EXCAUSE);
441 r6.h = hi(SEQSTAT_EXCAUSE); 520 r6.h = hi(SEQSTAT_EXCAUSE);
442 r7 = r7 & r6; 521 r7 = r7 & r6;
@@ -1432,15 +1511,7 @@ ENTRY(_sys_call_table)
1432 .rept NR_syscalls-(.-_sys_call_table)/4 1511 .rept NR_syscalls-(.-_sys_call_table)/4
1433 .long _sys_ni_syscall 1512 .long _sys_ni_syscall
1434 .endr 1513 .endr
1435 1514END(_sys_call_table)
1436 /*
1437 * Used to save the real RETX, IMASK and SYSCFG when temporarily
1438 * storing safe values across the transition from exception to IRQ5.
1439 */
1440_excpt_saved_stuff:
1441 .long 0;
1442 .long 0;
1443 .long 0;
1444 1515
1445_exception_stack: 1516_exception_stack:
1446 .rept 1024 1517 .rept 1024
diff --git a/arch/blackfin/mach-common/head.S b/arch/blackfin/mach-common/head.S
index 191b4e974c4b..7cb21cfcbf28 100644
--- a/arch/blackfin/mach-common/head.S
+++ b/arch/blackfin/mach-common/head.S
@@ -90,12 +90,46 @@ ENTRY(__start)
90 [p0] = R0; 90 [p0] = R0;
91 SSYNC; 91 SSYNC;
92 92
93 /* Save RETX, in case of doublefault */ 93 /* in case of double faults, save a few things */
94 p0.l = ___retx; 94 p0.l = _init_retx;
95 p0.h = ___retx; 95 p0.h = _init_retx;
96 R0 = RETX; 96 R0 = RETX;
97 [P0] = R0; 97 [P0] = R0;
98 98
99#ifdef CONFIG_DEBUG_DOUBLEFAULT
100 /* Only save these if we are storing them,
101 * This happens here, since L1 gets clobbered
102 * below
103 */
104 p0.l = _saved_retx;
105 p0.h = _saved_retx;
106 p1.l = _init_saved_retx;
107 p1.h = _init_saved_retx;
108 r0 = [p0];
109 [p1] = r0;
110
111 p0.l = _saved_dcplb_fault_addr;
112 p0.h = _saved_dcplb_fault_addr;
113 p1.l = _init_saved_dcplb_fault_addr;
114 p1.h = _init_saved_dcplb_fault_addr;
115 r0 = [p0];
116 [p1] = r0;
117
118 p0.l = _saved_icplb_fault_addr;
119 p0.h = _saved_icplb_fault_addr;
120 p1.l = _init_saved_icplb_fault_addr;
121 p1.h = _init_saved_icplb_fault_addr;
122 r0 = [p0];
123 [p1] = r0;
124
125 p0.l = _saved_seqstat;
126 p0.h = _saved_seqstat;
127 p1.l = _init_saved_seqstat;
128 p1.h = _init_saved_seqstat;
129 r0 = [p0];
130 [p1] = r0;
131#endif
132
99 /* Initialize stack pointer */ 133 /* Initialize stack pointer */
100 sp.l = lo(INITIAL_STACK); 134 sp.l = lo(INITIAL_STACK);
101 sp.h = hi(INITIAL_STACK); 135 sp.h = hi(INITIAL_STACK);