diff options
Diffstat (limited to 'arch/blackfin/kernel/traps.c')
-rw-r--r-- | arch/blackfin/kernel/traps.c | 212 |
1 files changed, 155 insertions, 57 deletions
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 21a55ef19cbd..66b5f3e3ae2a 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c | |||
@@ -36,8 +36,10 @@ | |||
36 | #include <asm/cacheflush.h> | 36 | #include <asm/cacheflush.h> |
37 | #include <asm/blackfin.h> | 37 | #include <asm/blackfin.h> |
38 | #include <asm/irq_handler.h> | 38 | #include <asm/irq_handler.h> |
39 | #include <linux/irq.h> | ||
39 | #include <asm/trace.h> | 40 | #include <asm/trace.h> |
40 | #include <asm/fixed_code.h> | 41 | #include <asm/fixed_code.h> |
42 | #include <asm/dma.h> | ||
41 | 43 | ||
42 | #ifdef CONFIG_KGDB | 44 | #ifdef CONFIG_KGDB |
43 | # include <linux/debugger.h> | 45 | # include <linux/debugger.h> |
@@ -170,7 +172,7 @@ asmlinkage void double_fault_c(struct pt_regs *fp) | |||
170 | oops_in_progress = 1; | 172 | oops_in_progress = 1; |
171 | printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); | 173 | printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); |
172 | dump_bfin_process(fp); | 174 | dump_bfin_process(fp); |
173 | dump_bfin_mem((void *)fp->retx); | 175 | dump_bfin_mem(fp); |
174 | show_regs(fp); | 176 | show_regs(fp); |
175 | panic("Double Fault - unrecoverable event\n"); | 177 | panic("Double Fault - unrecoverable event\n"); |
176 | 178 | ||
@@ -195,9 +197,13 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
195 | * we will kernel panic, so the system reboots. | 197 | * we will kernel panic, so the system reboots. |
196 | * If KGDB is enabled, don't set this for kernel breakpoints | 198 | * If KGDB is enabled, don't set this for kernel breakpoints |
197 | */ | 199 | */ |
198 | if ((bfin_read_IPEND() & 0xFFC0) | 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 | */ | ||
204 | if ((bfin_read_IPEND() & 0xFFC0) && (trapnr != VEC_STEP) | ||
199 | #ifdef CONFIG_KGDB | 205 | #ifdef CONFIG_KGDB |
200 | && trapnr != VEC_EXCPT02 | 206 | && (trapnr != VEC_EXCPT02) |
201 | #endif | 207 | #endif |
202 | ){ | 208 | ){ |
203 | console_verbose(); | 209 | console_verbose(); |
@@ -433,6 +439,36 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
433 | /* 0x3D - Reserved, Caught by default */ | 439 | /* 0x3D - Reserved, Caught by default */ |
434 | /* 0x3E - Reserved, Caught by default */ | 440 | /* 0x3E - Reserved, Caught by default */ |
435 | /* 0x3F - Reserved, Caught by default */ | 441 | /* 0x3F - Reserved, Caught by default */ |
442 | case VEC_HWERR: | ||
443 | info.si_code = BUS_ADRALN; | ||
444 | sig = SIGBUS; | ||
445 | switch (fp->seqstat & SEQSTAT_HWERRCAUSE) { | ||
446 | /* System MMR Error */ | ||
447 | case (SEQSTAT_HWERRCAUSE_SYSTEM_MMR): | ||
448 | info.si_code = BUS_ADRALN; | ||
449 | sig = SIGBUS; | ||
450 | printk(KERN_NOTICE HWC_x2(KERN_NOTICE)); | ||
451 | break; | ||
452 | /* External Memory Addressing Error */ | ||
453 | case (SEQSTAT_HWERRCAUSE_EXTERN_ADDR): | ||
454 | info.si_code = BUS_ADRERR; | ||
455 | sig = SIGBUS; | ||
456 | printk(KERN_NOTICE HWC_x3(KERN_NOTICE)); | ||
457 | break; | ||
458 | /* Performance Monitor Overflow */ | ||
459 | case (SEQSTAT_HWERRCAUSE_PERF_FLOW): | ||
460 | printk(KERN_NOTICE HWC_x12(KERN_NOTICE)); | ||
461 | break; | ||
462 | /* RAISE 5 instruction */ | ||
463 | case (SEQSTAT_HWERRCAUSE_RAISE_5): | ||
464 | printk(KERN_NOTICE HWC_x18(KERN_NOTICE)); | ||
465 | break; | ||
466 | default: /* Reserved */ | ||
467 | printk(KERN_NOTICE HWC_default(KERN_NOTICE)); | ||
468 | break; | ||
469 | } | ||
470 | CHK_DEBUGGER_TRAP(); | ||
471 | break; | ||
436 | default: | 472 | default: |
437 | info.si_code = TRAP_ILLTRAP; | 473 | info.si_code = TRAP_ILLTRAP; |
438 | sig = SIGTRAP; | 474 | sig = SIGTRAP; |
@@ -447,7 +483,7 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
447 | if (sig != SIGTRAP) { | 483 | if (sig != SIGTRAP) { |
448 | unsigned long stack; | 484 | unsigned long stack; |
449 | dump_bfin_process(fp); | 485 | dump_bfin_process(fp); |
450 | dump_bfin_mem((void *)fp->retx); | 486 | dump_bfin_mem(fp); |
451 | show_regs(fp); | 487 | show_regs(fp); |
452 | 488 | ||
453 | /* Print out the trace buffer if it makes sense */ | 489 | /* Print out the trace buffer if it makes sense */ |
@@ -461,6 +497,7 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
461 | dump_bfin_trace_buffer(); | 497 | dump_bfin_trace_buffer(); |
462 | show_stack(current, &stack); | 498 | show_stack(current, &stack); |
463 | if (oops_in_progress) { | 499 | if (oops_in_progress) { |
500 | print_modules(); | ||
464 | #ifndef CONFIG_ACCESS_CHECK | 501 | #ifndef CONFIG_ACCESS_CHECK |
465 | printk(KERN_EMERG "Please turn on " | 502 | printk(KERN_EMERG "Please turn on " |
466 | "CONFIG_ACCESS_CHECK\n"); | 503 | "CONFIG_ACCESS_CHECK\n"); |
@@ -474,13 +511,6 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
474 | info.si_addr = (void *)fp->pc; | 511 | info.si_addr = (void *)fp->pc; |
475 | force_sig_info(sig, &info, current); | 512 | force_sig_info(sig, &info, current); |
476 | 513 | ||
477 | /* Ensure that bad return addresses don't end up in an infinite | ||
478 | * loop, due to speculative loads/reads. This needs to be done after | ||
479 | * the signal has been sent. | ||
480 | */ | ||
481 | if (trapnr == VEC_CPLB_I_M && sig != SIGTRAP) | ||
482 | fp->pc = SAFE_USER_INSTRUCTION; | ||
483 | |||
484 | trace_buffer_restore(j); | 514 | trace_buffer_restore(j); |
485 | return; | 515 | return; |
486 | } | 516 | } |
@@ -616,8 +646,10 @@ void dump_bfin_process(struct pt_regs *fp) | |||
616 | if (oops_in_progress) | 646 | if (oops_in_progress) |
617 | printk(KERN_EMERG "Kernel OOPS in progress\n"); | 647 | printk(KERN_EMERG "Kernel OOPS in progress\n"); |
618 | 648 | ||
619 | if (context & 0x0020) | 649 | if (context & 0x0020 && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) |
620 | printk(KERN_NOTICE "Deferred excecption or HW Error context\n"); | 650 | printk(KERN_NOTICE "HW Error context\n"); |
651 | else if (context & 0x0020) | ||
652 | printk(KERN_NOTICE "Defered Exception context\n"); | ||
621 | else if (context & 0x3FC0) | 653 | else if (context & 0x3FC0) |
622 | printk(KERN_NOTICE "Interrupt context\n"); | 654 | printk(KERN_NOTICE "Interrupt context\n"); |
623 | else if (context & 0x4000) | 655 | else if (context & 0x4000) |
@@ -645,59 +677,124 @@ void dump_bfin_process(struct pt_regs *fp) | |||
645 | "No Valid process in current context\n"); | 677 | "No Valid process in current context\n"); |
646 | } | 678 | } |
647 | 679 | ||
648 | void dump_bfin_mem(void *retaddr) | 680 | void dump_bfin_mem(struct pt_regs *fp) |
649 | { | 681 | { |
682 | unsigned short *addr, *erraddr, val = 0, err = 0; | ||
683 | char sti = 0, buf[6]; | ||
650 | 684 | ||
651 | if (retaddr >= (void *)FIXED_CODE_START && retaddr < (void *)physical_mem_end | 685 | if (unlikely((fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR)) |
652 | #if L1_CODE_LENGTH != 0 | 686 | erraddr = (void *)fp->pc; |
653 | /* FIXME: Copy the code out of L1 Instruction SRAM through dma | 687 | else |
654 | memcpy. */ | 688 | erraddr = (void *)fp->retx; |
655 | && !(retaddr >= (void *)L1_CODE_START | 689 | |
656 | && retaddr < (void *)(L1_CODE_START + L1_CODE_LENGTH)) | 690 | printk(KERN_NOTICE "return address: [0x%p]; contents of:", erraddr); |
657 | #endif | 691 | |
658 | ) { | 692 | for (addr = (unsigned short *)((unsigned long)erraddr & ~0xF) - 0x10; |
659 | int i = ((unsigned int)retaddr & 0xFFFFFFF0) - 32; | 693 | addr < (unsigned short *)((unsigned long)erraddr & ~0xF) + 0x10; |
660 | unsigned short x = 0; | 694 | addr++) { |
661 | printk(KERN_NOTICE "return address: [0x%p]; contents of:", retaddr); | 695 | if (!((unsigned long)addr & 0xF)) |
662 | for (; i < ((unsigned int)retaddr & 0xFFFFFFF0) + 32; i += 2) { | 696 | printk("\n" KERN_NOTICE "0x%p: ", addr); |
663 | if (!(i & 0xF)) | 697 | |
664 | printk("\n" KERN_NOTICE "0x%08x: ", i); | 698 | if (get_user(val, addr)) { |
665 | 699 | if (addr >= (unsigned short *)L1_CODE_START && | |
666 | if (get_user(x, (unsigned short *)i)) | 700 | addr < (unsigned short *)(L1_CODE_START + L1_CODE_LENGTH)) { |
667 | break; | 701 | dma_memcpy(&val, addr, sizeof(val)); |
702 | sprintf(buf, "%04x", val); | ||
703 | } else if (addr >= (unsigned short *)FIXED_CODE_START && | ||
704 | addr <= (unsigned short *)memory_start) { | ||
705 | val = bfin_read16(addr); | ||
706 | sprintf(buf, "%04x", val); | ||
707 | } else { | ||
708 | val = 0; | ||
709 | sprintf(buf, "????"); | ||
710 | } | ||
711 | } else | ||
712 | sprintf(buf, "%04x", val); | ||
713 | |||
714 | if (addr == erraddr) { | ||
715 | printk("[%s]", buf); | ||
716 | err = val; | ||
717 | } else | ||
718 | printk(" %s ", buf); | ||
719 | |||
720 | /* Do any previous instructions turn on interrupts? */ | ||
721 | if (addr <= erraddr && /* in the past */ | ||
722 | ((val >= 0x0040 && val <= 0x0047) || /* STI instruction */ | ||
723 | val == 0x017b)) /* [SP++] = RETI */ | ||
724 | sti = 1; | ||
725 | } | ||
726 | |||
727 | printk("\n"); | ||
728 | |||
729 | /* Hardware error interrupts can be deferred */ | ||
730 | if (unlikely(sti && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR && | ||
731 | oops_in_progress)){ | ||
732 | printk(KERN_NOTICE "Looks like this was a deferred error - sorry\n"); | ||
668 | #ifndef CONFIG_DEBUG_HWERR | 733 | #ifndef CONFIG_DEBUG_HWERR |
669 | /* If one of the last few instructions was a STI | 734 | printk(KERN_NOTICE "The remaining message may be meaningless\n" |
670 | * it is likely that the error occured awhile ago | 735 | KERN_NOTICE "You should enable CONFIG_DEBUG_HWERR to get a" |
671 | * and we just noticed. This only happens in kernel | 736 | " better idea where it came from\n"); |
672 | * context, which should mean an oops is happening | 737 | #else |
673 | */ | 738 | /* If we are handling only one peripheral interrupt |
674 | if (oops_in_progress && x >= 0x0040 && x <= 0x0047 && i <= 0) | 739 | * and current mm and pid are valid, and the last error |
675 | panic("\n\nWARNING : You should reconfigure" | 740 | * was in that user space process's text area |
676 | " the kernel to turn on\n" | 741 | * print it out - because that is where the problem exists |
677 | " 'Hardware error interrupt" | 742 | */ |
678 | " debugging'\n" | 743 | if ((!(((fp)->ipend & ~0x30) & (((fp)->ipend & ~0x30) - 1))) && |
679 | " The rest of this error" | 744 | (current->pid && current->mm)) { |
680 | " is meanless\n"); | 745 | /* And the last RETI points to the current userspace context */ |
681 | #endif | 746 | if ((fp + 1)->pc >= current->mm->start_code && |
682 | if (i == (unsigned int)retaddr) | 747 | (fp + 1)->pc <= current->mm->end_code) { |
683 | printk("[%04x]", x); | 748 | printk(KERN_NOTICE "It might be better to look around here : \n"); |
684 | else | 749 | printk(KERN_NOTICE "-------------------------------------------\n"); |
685 | printk(" %04x ", x); | 750 | show_regs(fp + 1); |
751 | printk(KERN_NOTICE "-------------------------------------------\n"); | ||
752 | } | ||
686 | } | 753 | } |
687 | printk("\n"); | 754 | #endif |
688 | } else | 755 | } |
689 | printk("\n" KERN_NOTICE | ||
690 | "Cannot look at the [PC] <%p> for it is" | ||
691 | " in unreadable memory - sorry\n", retaddr); | ||
692 | } | 756 | } |
693 | 757 | ||
694 | void show_regs(struct pt_regs *fp) | 758 | void show_regs(struct pt_regs *fp) |
695 | { | 759 | { |
696 | char buf [150]; | 760 | char buf [150]; |
761 | struct irqaction *action; | ||
762 | unsigned int i; | ||
763 | unsigned long flags; | ||
697 | 764 | ||
698 | printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\n"); | 765 | printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted()); |
699 | printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", | 766 | printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", |
700 | (long)fp->seqstat, fp->ipend, fp->syscfg); | 767 | (long)fp->seqstat, fp->ipend, fp->syscfg); |
768 | printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n", | ||
769 | (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14); | ||
770 | printk(KERN_NOTICE " EXCAUSE : 0x%lx\n", | ||
771 | fp->seqstat & SEQSTAT_EXCAUSE); | ||
772 | for (i = 6; i <= 15 ; i++) { | ||
773 | if (fp->ipend & (1 << i)) { | ||
774 | decode_address(buf, bfin_read32(EVT0 + 4*i)); | ||
775 | printk(KERN_NOTICE " physical IVG%i asserted : %s\n", i, buf); | ||
776 | } | ||
777 | } | ||
778 | |||
779 | /* if no interrupts are going off, don't print this out */ | ||
780 | if (fp->ipend & ~0x3F) { | ||
781 | for (i = 0; i < (NR_IRQS - 1); i++) { | ||
782 | spin_lock_irqsave(&irq_desc[i].lock, flags); | ||
783 | action = irq_desc[i].action; | ||
784 | if (!action) | ||
785 | goto unlock; | ||
786 | |||
787 | decode_address(buf, (unsigned int)action->handler); | ||
788 | printk(KERN_NOTICE " logical irq %3d mapped : %s", i, buf); | ||
789 | for (action = action->next; action; action = action->next) { | ||
790 | decode_address(buf, (unsigned int)action->handler); | ||
791 | printk(", %s", buf); | ||
792 | } | ||
793 | printk("\n"); | ||
794 | unlock: | ||
795 | spin_unlock_irqrestore(&irq_desc[i].lock, flags); | ||
796 | } | ||
797 | } | ||
701 | 798 | ||
702 | decode_address(buf, fp->rete); | 799 | decode_address(buf, fp->rete); |
703 | printk(KERN_NOTICE " RETE: %s\n", buf); | 800 | printk(KERN_NOTICE " RETE: %s\n", buf); |
@@ -708,9 +805,10 @@ void show_regs(struct pt_regs *fp) | |||
708 | decode_address(buf, fp->rets); | 805 | decode_address(buf, fp->rets); |
709 | printk(KERN_NOTICE " RETS: %s\n", buf); | 806 | printk(KERN_NOTICE " RETS: %s\n", buf); |
710 | decode_address(buf, fp->pc); | 807 | decode_address(buf, fp->pc); |
711 | printk(KERN_NOTICE " PC: %s\n", buf); | 808 | printk(KERN_NOTICE " PC : %s\n", buf); |
712 | 809 | ||
713 | if ((long)fp->seqstat & SEQSTAT_EXCAUSE) { | 810 | if (((long)fp->seqstat & SEQSTAT_EXCAUSE) && |
811 | (((long)fp->seqstat & SEQSTAT_EXCAUSE) != VEC_HWERR)) { | ||
714 | decode_address(buf, bfin_read_DCPLB_FAULT_ADDR()); | 812 | decode_address(buf, bfin_read_DCPLB_FAULT_ADDR()); |
715 | printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf); | 813 | printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf); |
716 | decode_address(buf, bfin_read_ICPLB_FAULT_ADDR()); | 814 | decode_address(buf, bfin_read_ICPLB_FAULT_ADDR()); |
@@ -824,7 +922,7 @@ void panic_cplb_error(int cplb_panic, struct pt_regs *fp) | |||
824 | printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void *)bfin_read_DCPLB_FAULT_ADDR()); | 922 | printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void *)bfin_read_DCPLB_FAULT_ADDR()); |
825 | printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void *)bfin_read_ICPLB_FAULT_ADDR()); | 923 | printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void *)bfin_read_ICPLB_FAULT_ADDR()); |
826 | dump_bfin_process(fp); | 924 | dump_bfin_process(fp); |
827 | dump_bfin_mem((void *)fp->retx); | 925 | dump_bfin_mem(fp); |
828 | show_regs(fp); | 926 | show_regs(fp); |
829 | dump_stack(); | 927 | dump_stack(); |
830 | panic("Unrecoverable event\n"); | 928 | panic("Unrecoverable event\n"); |