diff options
Diffstat (limited to 'arch/blackfin/kernel/traps.c')
-rw-r--r-- | arch/blackfin/kernel/traps.c | 132 |
1 files changed, 84 insertions, 48 deletions
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 8bbfef31666b..c90f16825f98 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/irq.h> | 39 | #include <linux/irq.h> |
40 | #include <asm/trace.h> | 40 | #include <asm/trace.h> |
41 | #include <asm/fixed_code.h> | 41 | #include <asm/fixed_code.h> |
42 | #include <asm/dma.h> | ||
42 | 43 | ||
43 | #ifdef CONFIG_KGDB | 44 | #ifdef CONFIG_KGDB |
44 | # include <linux/debugger.h> | 45 | # include <linux/debugger.h> |
@@ -171,7 +172,7 @@ asmlinkage void double_fault_c(struct pt_regs *fp) | |||
171 | oops_in_progress = 1; | 172 | oops_in_progress = 1; |
172 | printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); | 173 | printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); |
173 | dump_bfin_process(fp); | 174 | dump_bfin_process(fp); |
174 | dump_bfin_mem((void *)fp->retx); | 175 | dump_bfin_mem(fp); |
175 | show_regs(fp); | 176 | show_regs(fp); |
176 | panic("Double Fault - unrecoverable event\n"); | 177 | panic("Double Fault - unrecoverable event\n"); |
177 | 178 | ||
@@ -196,6 +197,10 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
196 | * we will kernel panic, so the system reboots. | 197 | * we will kernel panic, so the system reboots. |
197 | * If KGDB is enabled, don't set this for kernel breakpoints | 198 | * If KGDB is enabled, don't set this for kernel breakpoints |
198 | */ | 199 | */ |
200 | |||
201 | /* TODO: check to see if we are in some sort of deferred HWERR | ||
202 | * that we should be able to recover from, not kernel panic | ||
203 | */ | ||
199 | if ((bfin_read_IPEND() & 0xFFC0) | 204 | if ((bfin_read_IPEND() & 0xFFC0) |
200 | #ifdef CONFIG_KGDB | 205 | #ifdef CONFIG_KGDB |
201 | && trapnr != VEC_EXCPT02 | 206 | && trapnr != VEC_EXCPT02 |
@@ -478,11 +483,7 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
478 | if (sig != SIGTRAP) { | 483 | if (sig != SIGTRAP) { |
479 | unsigned long stack; | 484 | unsigned long stack; |
480 | dump_bfin_process(fp); | 485 | dump_bfin_process(fp); |
481 | /* Is it an interrupt, or an exception? */ | 486 | dump_bfin_mem(fp); |
482 | if (trapnr == VEC_HWERR) | ||
483 | dump_bfin_mem((void *)fp->pc); | ||
484 | else | ||
485 | dump_bfin_mem((void *)fp->retx); | ||
486 | show_regs(fp); | 487 | show_regs(fp); |
487 | 488 | ||
488 | /* Print out the trace buffer if it makes sense */ | 489 | /* Print out the trace buffer if it makes sense */ |
@@ -644,8 +645,10 @@ void dump_bfin_process(struct pt_regs *fp) | |||
644 | if (oops_in_progress) | 645 | if (oops_in_progress) |
645 | printk(KERN_EMERG "Kernel OOPS in progress\n"); | 646 | printk(KERN_EMERG "Kernel OOPS in progress\n"); |
646 | 647 | ||
647 | if (context & 0x0020) | 648 | if (context & 0x0020 && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) |
648 | printk(KERN_NOTICE "Deferred excecption or HW Error context\n"); | 649 | printk(KERN_NOTICE "HW Error context\n"); |
650 | else if (context & 0x0020) | ||
651 | printk(KERN_NOTICE "Defered Exception context\n"); | ||
649 | else if (context & 0x3FC0) | 652 | else if (context & 0x3FC0) |
650 | printk(KERN_NOTICE "Interrupt context\n"); | 653 | printk(KERN_NOTICE "Interrupt context\n"); |
651 | else if (context & 0x4000) | 654 | else if (context & 0x4000) |
@@ -673,49 +676,82 @@ void dump_bfin_process(struct pt_regs *fp) | |||
673 | "No Valid process in current context\n"); | 676 | "No Valid process in current context\n"); |
674 | } | 677 | } |
675 | 678 | ||
676 | void dump_bfin_mem(void *retaddr) | 679 | void dump_bfin_mem(struct pt_regs *fp) |
677 | { | 680 | { |
681 | unsigned short *addr, *erraddr, val = 0, err = 0; | ||
682 | char sti = 0, buf[6]; | ||
678 | 683 | ||
679 | if (retaddr >= (void *)FIXED_CODE_START && retaddr < (void *)physical_mem_end | 684 | if (unlikely((fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR)) |
680 | #if L1_CODE_LENGTH != 0 | 685 | erraddr = (void *)fp->pc; |
681 | /* FIXME: Copy the code out of L1 Instruction SRAM through dma | 686 | else |
682 | memcpy. */ | 687 | erraddr = (void *)fp->retx; |
683 | && !(retaddr >= (void *)L1_CODE_START | 688 | |
684 | && retaddr < (void *)(L1_CODE_START + L1_CODE_LENGTH)) | 689 | printk(KERN_NOTICE "return address: [0x%p]; contents of:", erraddr); |
685 | #endif | 690 | |
686 | ) { | 691 | for (addr = (unsigned short *)((unsigned long)erraddr & ~0xF) - 0x10; |
687 | int i = ((unsigned int)retaddr & 0xFFFFFFF0) - 32; | 692 | addr < (unsigned short *)((unsigned long)erraddr & ~0xF) + 0x10; |
688 | unsigned short x = 0; | 693 | addr++) { |
689 | printk(KERN_NOTICE "return address: [0x%p]; contents of:", retaddr); | 694 | if (!((unsigned long)addr & 0xF)) |
690 | for (; i < ((unsigned int)retaddr & 0xFFFFFFF0) + 32; i += 2) { | 695 | printk("\n" KERN_NOTICE "0x%p: ", addr); |
691 | if (!(i & 0xF)) | 696 | |
692 | printk("\n" KERN_NOTICE "0x%08x: ", i); | 697 | if (get_user(val, addr)) { |
693 | 698 | if (addr >= (unsigned short *)L1_CODE_START && | |
694 | if (get_user(x, (unsigned short *)i)) | 699 | addr < (unsigned short *)(L1_CODE_START + L1_CODE_LENGTH)) { |
695 | break; | 700 | dma_memcpy(&val, addr, sizeof(val)); |
701 | sprintf(buf, "%04x", val); | ||
702 | } else if (addr >= (unsigned short *)FIXED_CODE_START && | ||
703 | addr <= (unsigned short *)memory_start) { | ||
704 | val = bfin_read16(addr); | ||
705 | sprintf(buf, "%04x", val); | ||
706 | } else { | ||
707 | val = 0; | ||
708 | sprintf(buf, "????"); | ||
709 | } | ||
710 | } else | ||
711 | sprintf(buf, "%04x", val); | ||
712 | |||
713 | if (addr == erraddr) { | ||
714 | printk("[%s]", buf); | ||
715 | err = val; | ||
716 | } else | ||
717 | printk(" %s ", buf); | ||
718 | |||
719 | /* Do any previous instructions turn on interrupts? */ | ||
720 | if (addr <= erraddr && /* in the past */ | ||
721 | ((val >= 0x0040 && val <= 0x0047) || /* STI instruction */ | ||
722 | val == 0x017b)) /* [SP++] = RETI */ | ||
723 | sti = 1; | ||
724 | } | ||
725 | |||
726 | printk("\n"); | ||
727 | |||
728 | /* Hardware error interrupts can be deferred */ | ||
729 | if (unlikely(sti && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR && | ||
730 | oops_in_progress)){ | ||
731 | printk(KERN_NOTICE "Looks like this was a deferred error - sorry\n"); | ||
696 | #ifndef CONFIG_DEBUG_HWERR | 732 | #ifndef CONFIG_DEBUG_HWERR |
697 | /* If one of the last few instructions was a STI | 733 | printk(KERN_NOTICE "The remaining message may be meaningless\n" |
698 | * it is likely that the error occured awhile ago | 734 | KERN_NOTICE "You should enable CONFIG_DEBUG_HWERR to get a" |
699 | * and we just noticed. This only happens in kernel | 735 | " better idea where it came from\n"); |
700 | * context, which should mean an oops is happening | 736 | #else |
701 | */ | 737 | /* If we are handling only one peripheral interrupt |
702 | if (oops_in_progress && x >= 0x0040 && x <= 0x0047 && i <= 0) | 738 | * and current mm and pid are valid, and the last error |
703 | printk(KERN_EMERG "\n" | 739 | * was in that user space process's text area |
704 | KERN_EMERG "WARNING : You should reconfigure" | 740 | * print it out - because that is where the problem exists |
705 | " the kernel to turn on\n" | 741 | */ |
706 | KERN_EMERG " 'Hardware error interrupt debugging'\n" | 742 | if ((!(((fp)->ipend & ~0x30) & (((fp)->ipend & ~0x30) - 1))) && |
707 | KERN_EMERG " The rest of this error is meanless\n"); | 743 | (current->pid && current->mm)) { |
708 | #endif | 744 | /* And the last RETI points to the current userspace context */ |
709 | if (i == (unsigned int)retaddr) | 745 | if ((fp + 1)->pc >= current->mm->start_code && |
710 | printk("[%04x]", x); | 746 | (fp + 1)->pc <= current->mm->end_code) { |
711 | else | 747 | printk(KERN_NOTICE "It might be better to look around here : \n"); |
712 | printk(" %04x ", x); | 748 | printk(KERN_NOTICE "-------------------------------------------\n"); |
749 | show_regs(fp + 1); | ||
750 | printk(KERN_NOTICE "-------------------------------------------\n"); | ||
751 | } | ||
713 | } | 752 | } |
714 | printk("\n"); | 753 | #endif |
715 | } else | 754 | } |
716 | printk("\n" KERN_NOTICE | ||
717 | "Cannot look at the [PC] <%p> for it is" | ||
718 | " in unreadable memory - sorry\n", retaddr); | ||
719 | } | 755 | } |
720 | 756 | ||
721 | void show_regs(struct pt_regs *fp) | 757 | void show_regs(struct pt_regs *fp) |
@@ -885,7 +921,7 @@ void panic_cplb_error(int cplb_panic, struct pt_regs *fp) | |||
885 | printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void *)bfin_read_DCPLB_FAULT_ADDR()); | 921 | printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void *)bfin_read_DCPLB_FAULT_ADDR()); |
886 | printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void *)bfin_read_ICPLB_FAULT_ADDR()); | 922 | printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void *)bfin_read_ICPLB_FAULT_ADDR()); |
887 | dump_bfin_process(fp); | 923 | dump_bfin_process(fp); |
888 | dump_bfin_mem((void *)fp->retx); | 924 | dump_bfin_mem(fp); |
889 | show_regs(fp); | 925 | show_regs(fp); |
890 | dump_stack(); | 926 | dump_stack(); |
891 | panic("Unrecoverable event\n"); | 927 | panic("Unrecoverable event\n"); |