diff options
author | Robin Getz <rgetz@blackfin.uclinux.org> | 2008-07-26 07:45:46 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@kernel.org> | 2008-07-26 07:45:46 -0400 |
commit | f09630bff51daaf427968c61c0f2370c64148e06 (patch) | |
tree | 5efbe18ad418b7006b79bbe8322ec2defb160813 /arch/blackfin/kernel/traps.c | |
parent | 0a6304a9517aa3593913ecfbbcad80e798641723 (diff) |
Blackfin arch: Add unwinding for stack info, and a little more detail on trace buffer
Signed-off-by: Robin Getz <rgetz@blackfin.uclinux.org>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Diffstat (limited to 'arch/blackfin/kernel/traps.c')
-rw-r--r-- | arch/blackfin/kernel/traps.c | 296 |
1 files changed, 241 insertions, 55 deletions
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index f061f5181623..ad922ab91543 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c | |||
@@ -69,8 +69,6 @@ void __init trap_init(void) | |||
69 | 69 | ||
70 | unsigned long saved_icplb_fault_addr, saved_dcplb_fault_addr; | 70 | unsigned long saved_icplb_fault_addr, saved_dcplb_fault_addr; |
71 | 71 | ||
72 | int kstack_depth_to_print = 48; | ||
73 | |||
74 | static void decode_address(char *buf, unsigned long address) | 72 | static void decode_address(char *buf, unsigned long address) |
75 | { | 73 | { |
76 | struct vm_list_struct *vml; | 74 | struct vm_list_struct *vml; |
@@ -163,6 +161,9 @@ static void decode_address(char *buf, unsigned long address) | |||
163 | if (!in_atomic) | 161 | if (!in_atomic) |
164 | mmput(mm); | 162 | mmput(mm); |
165 | 163 | ||
164 | if (!strlen(buf)) | ||
165 | sprintf(buf, "<0x%p> [ %s ] dynamic memory", (void *)address, name); | ||
166 | |||
166 | goto done; | 167 | goto done; |
167 | } | 168 | } |
168 | 169 | ||
@@ -173,7 +174,7 @@ static void decode_address(char *buf, unsigned long address) | |||
173 | } | 174 | } |
174 | 175 | ||
175 | /* we were unable to find this address anywhere */ | 176 | /* we were unable to find this address anywhere */ |
176 | sprintf(buf, "<0x%p> /* unknown address */", (void *)address); | 177 | sprintf(buf, "<0x%p> /* kernel dynamic memory */", (void *)address); |
177 | 178 | ||
178 | done: | 179 | done: |
179 | write_unlock_irqrestore(&tasklist_lock, flags); | 180 | write_unlock_irqrestore(&tasklist_lock, flags); |
@@ -494,7 +495,7 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
494 | BUG_ON(sig == 0); | 495 | BUG_ON(sig == 0); |
495 | 496 | ||
496 | if (sig != SIGTRAP) { | 497 | if (sig != SIGTRAP) { |
497 | unsigned long stack; | 498 | unsigned long *stack; |
498 | dump_bfin_process(fp); | 499 | dump_bfin_process(fp); |
499 | dump_bfin_mem(fp); | 500 | dump_bfin_mem(fp); |
500 | show_regs(fp); | 501 | show_regs(fp); |
@@ -508,14 +509,23 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
508 | else | 509 | else |
509 | #endif | 510 | #endif |
510 | dump_bfin_trace_buffer(); | 511 | dump_bfin_trace_buffer(); |
511 | show_stack(current, &stack); | 512 | |
512 | if (oops_in_progress) { | 513 | if (oops_in_progress) { |
514 | /* Dump the current kernel stack */ | ||
515 | printk(KERN_NOTICE "\n" KERN_NOTICE "Kernel Stack\n"); | ||
516 | show_stack(current, NULL); | ||
517 | |||
513 | print_modules(); | 518 | print_modules(); |
514 | #ifndef CONFIG_ACCESS_CHECK | 519 | #ifndef CONFIG_ACCESS_CHECK |
515 | printk(KERN_EMERG "Please turn on " | 520 | printk(KERN_EMERG "Please turn on " |
516 | "CONFIG_ACCESS_CHECK\n"); | 521 | "CONFIG_ACCESS_CHECK\n"); |
517 | #endif | 522 | #endif |
518 | panic("Kernel exception"); | 523 | panic("Kernel exception"); |
524 | } else { | ||
525 | /* Dump the user space stack */ | ||
526 | stack = (unsigned long *)rdusp(); | ||
527 | printk(KERN_NOTICE "Userspace Stack\n"); | ||
528 | show_stack(NULL, stack); | ||
519 | } | 529 | } |
520 | } | 530 | } |
521 | 531 | ||
@@ -532,11 +542,71 @@ asmlinkage void trap_c(struct pt_regs *fp) | |||
532 | 542 | ||
533 | #define EXPAND_LEN ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 256 - 1) | 543 | #define EXPAND_LEN ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 256 - 1) |
534 | 544 | ||
545 | /* | ||
546 | * Similar to get_user, do some address checking, then dereference | ||
547 | * Return true on sucess, false on bad address | ||
548 | */ | ||
549 | bool get_instruction(unsigned short *val, unsigned short *address) | ||
550 | { | ||
551 | |||
552 | unsigned long addr; | ||
553 | |||
554 | addr = (unsigned long)address; | ||
555 | |||
556 | /* Check for odd addresses */ | ||
557 | if (addr & 0x1) | ||
558 | return false; | ||
559 | |||
560 | /* Check that things do not wrap around */ | ||
561 | if (addr > (addr + 2)) | ||
562 | return false; | ||
563 | |||
564 | /* | ||
565 | * Since we are in exception context, we need to do a little address checking | ||
566 | * We need to make sure we are only accessing valid memory, and | ||
567 | * we don't read something in the async space that can hang forever | ||
568 | */ | ||
569 | if ((addr >= FIXED_CODE_START && (addr + 2) <= physical_mem_end) || | ||
570 | #ifdef L2_START | ||
571 | (addr >= L2_START && (addr + 2) <= (L2_START + L2_LENGTH)) || | ||
572 | #endif | ||
573 | (addr >= BOOT_ROM_START && (addr + 2) <= (BOOT_ROM_START + BOOT_ROM_LENGTH)) || | ||
574 | #if L1_DATA_A_LENGTH != 0 | ||
575 | (addr >= L1_DATA_A_START && (addr + 2) <= (L1_DATA_A_START + L1_DATA_A_LENGTH)) || | ||
576 | #endif | ||
577 | #if L1_DATA_B_LENGTH != 0 | ||
578 | (addr >= L1_DATA_B_START && (addr + 2) <= (L1_DATA_B_START + L1_DATA_B_LENGTH)) || | ||
579 | #endif | ||
580 | (addr >= L1_SCRATCH_START && (addr + 2) <= (L1_SCRATCH_START + L1_SCRATCH_LENGTH)) || | ||
581 | (!(bfin_read_EBIU_AMBCTL0() & B0RDYEN) && | ||
582 | addr >= ASYNC_BANK0_BASE && (addr + 2) <= (ASYNC_BANK0_BASE + ASYNC_BANK0_SIZE)) || | ||
583 | (!(bfin_read_EBIU_AMBCTL0() & B1RDYEN) && | ||
584 | addr >= ASYNC_BANK1_BASE && (addr + 2) <= (ASYNC_BANK1_BASE + ASYNC_BANK1_SIZE)) || | ||
585 | (!(bfin_read_EBIU_AMBCTL1() & B2RDYEN) && | ||
586 | addr >= ASYNC_BANK2_BASE && (addr + 2) <= (ASYNC_BANK2_BASE + ASYNC_BANK1_SIZE)) || | ||
587 | (!(bfin_read_EBIU_AMBCTL1() & B3RDYEN) && | ||
588 | addr >= ASYNC_BANK3_BASE && (addr + 2) <= (ASYNC_BANK3_BASE + ASYNC_BANK1_SIZE))) { | ||
589 | *val = *address; | ||
590 | return true; | ||
591 | } | ||
592 | |||
593 | #if L1_CODE_LENGTH != 0 | ||
594 | if (addr >= L1_CODE_START && (addr + 2) <= (L1_CODE_START + L1_CODE_LENGTH)) { | ||
595 | dma_memcpy(val, address, 2); | ||
596 | return true; | ||
597 | } | ||
598 | #endif | ||
599 | |||
600 | |||
601 | return false; | ||
602 | } | ||
603 | |||
535 | void dump_bfin_trace_buffer(void) | 604 | void dump_bfin_trace_buffer(void) |
536 | { | 605 | { |
537 | #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON | 606 | #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON |
538 | int tflags, i = 0; | 607 | int tflags, i = 0; |
539 | char buf[150]; | 608 | char buf[150]; |
609 | unsigned short val = 0, *addr; | ||
540 | #ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND | 610 | #ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND |
541 | int j, index; | 611 | int j, index; |
542 | #endif | 612 | #endif |
@@ -549,8 +619,42 @@ void dump_bfin_trace_buffer(void) | |||
549 | for (; bfin_read_TBUFSTAT() & TBUFCNT; i++) { | 619 | for (; bfin_read_TBUFSTAT() & TBUFCNT; i++) { |
550 | decode_address(buf, (unsigned long)bfin_read_TBUF()); | 620 | decode_address(buf, (unsigned long)bfin_read_TBUF()); |
551 | printk(KERN_NOTICE "%4i Target : %s\n", i, buf); | 621 | printk(KERN_NOTICE "%4i Target : %s\n", i, buf); |
552 | decode_address(buf, (unsigned long)bfin_read_TBUF()); | 622 | addr = (unsigned short *)bfin_read_TBUF(); |
553 | printk(KERN_NOTICE " Source : %s\n", buf); | 623 | decode_address(buf, (unsigned long)addr); |
624 | printk(KERN_NOTICE " Source : %s ", buf); | ||
625 | if (get_instruction(&val, addr)) { | ||
626 | if (val == 0x0010) | ||
627 | printk("RTS"); | ||
628 | else if (val == 0x0011) | ||
629 | printk("RTI"); | ||
630 | else if (val == 0x0012) | ||
631 | printk("RTX"); | ||
632 | else if (val >= 0x0050 && val <= 0x0057) | ||
633 | printk("JUMP (P%i)", val & 7); | ||
634 | else if (val >= 0x0060 && val <= 0x0067) | ||
635 | printk("CALL (P%i)", val & 7); | ||
636 | else if (val >= 0x0070 && val <= 0x0077) | ||
637 | printk("CALL (PC+P%i)", val & 7); | ||
638 | else if (val >= 0x0080 && val <= 0x0087) | ||
639 | printk("JUMP (PC+P%i)", val & 7); | ||
640 | else if ((val >= 0x1000 && val <= 0x13FF) || | ||
641 | (val >= 0x1800 && val <= 0x1BFF)) | ||
642 | printk("IF !CC JUMP"); | ||
643 | else if ((val >= 0x1400 && val <= 0x17ff) || | ||
644 | (val >= 0x1c00 && val <= 0x1fff)) | ||
645 | printk("IF CC JUMP"); | ||
646 | else if (val >= 0x2000 && val <= 0x2fff) | ||
647 | printk("JUMP.S"); | ||
648 | else if (val >= 0xe080 && val <= 0xe0ff) | ||
649 | printk("LSETUP"); | ||
650 | else if (val >= 0xe200 && val <= 0xe2ff) | ||
651 | printk("JUMP.L"); | ||
652 | else if (val >= 0xe300 && val <= 0xe3ff) | ||
653 | printk("CALL pcrel"); | ||
654 | else | ||
655 | printk("0x%04x", val); | ||
656 | } | ||
657 | printk("\n"); | ||
554 | } | 658 | } |
555 | } | 659 | } |
556 | 660 | ||
@@ -582,59 +686,151 @@ void dump_bfin_trace_buffer(void) | |||
582 | } | 686 | } |
583 | EXPORT_SYMBOL(dump_bfin_trace_buffer); | 687 | EXPORT_SYMBOL(dump_bfin_trace_buffer); |
584 | 688 | ||
585 | static void show_trace(struct task_struct *tsk, unsigned long *sp) | 689 | /* |
690 | * Checks to see if the address pointed to is either a | ||
691 | * 16-bit CALL instruction, or a 32-bit CALL instruction | ||
692 | */ | ||
693 | bool is_bfin_call(unsigned short *addr) | ||
586 | { | 694 | { |
587 | unsigned long addr; | 695 | unsigned short opcode = 0, *ins_addr; |
696 | ins_addr = (unsigned short *)addr; | ||
588 | 697 | ||
589 | printk(KERN_NOTICE "\n" KERN_NOTICE "Call Trace:\n"); | 698 | if (!get_instruction(&opcode, ins_addr)) |
590 | 699 | return false; | |
591 | while (!kstack_end(sp)) { | ||
592 | addr = *sp++; | ||
593 | /* | ||
594 | * If the address is either in the text segment of the | ||
595 | * kernel, or in the region which contains vmalloc'ed | ||
596 | * memory, it *may* be the address of a calling | ||
597 | * routine; if so, print it so that someone tracing | ||
598 | * down the cause of the crash will be able to figure | ||
599 | * out the call path that was taken. | ||
600 | */ | ||
601 | if (kernel_text_address(addr)) | ||
602 | print_ip_sym(addr); | ||
603 | } | ||
604 | 700 | ||
605 | printk(KERN_NOTICE "\n"); | 701 | if ((opcode >= 0x0060 && opcode <= 0x0067) || |
606 | } | 702 | (opcode >= 0x0070 && opcode <= 0x0077)) |
703 | return true; | ||
704 | |||
705 | ins_addr--; | ||
706 | if (!get_instruction(&opcode, ins_addr)) | ||
707 | return false; | ||
607 | 708 | ||
709 | if (opcode >= 0xE300 && opcode <= 0xE3FF) | ||
710 | return true; | ||
711 | |||
712 | return false; | ||
713 | |||
714 | } | ||
608 | void show_stack(struct task_struct *task, unsigned long *stack) | 715 | void show_stack(struct task_struct *task, unsigned long *stack) |
609 | { | 716 | { |
610 | unsigned long *endstack, addr; | 717 | unsigned int *addr, *endstack, *fp = 0, *frame; |
611 | int i; | 718 | unsigned short *ins_addr; |
719 | char buf[150]; | ||
720 | unsigned int i, j, ret_addr, frame_no = 0; | ||
612 | 721 | ||
613 | /* Cannot call dump_bfin_trace_buffer() here as show_stack() is | 722 | /* |
614 | * called externally in some places in the kernel. | 723 | * If we have been passed a specific stack, use that one otherwise |
724 | * if we have been passed a task structure, use that, otherwise | ||
725 | * use the stack of where the variable "stack" exists | ||
615 | */ | 726 | */ |
616 | 727 | ||
617 | if (!stack) { | 728 | if (stack == NULL) { |
618 | if (task) | 729 | if (task) { |
730 | /* We know this is a kernel stack, so this is the start/end */ | ||
619 | stack = (unsigned long *)task->thread.ksp; | 731 | stack = (unsigned long *)task->thread.ksp; |
620 | else | 732 | endstack = (unsigned int *)(((unsigned int)(stack) & ~(THREAD_SIZE - 1)) + THREAD_SIZE); |
733 | } else { | ||
734 | /* print out the existing stack info */ | ||
621 | stack = (unsigned long *)&stack; | 735 | stack = (unsigned long *)&stack; |
736 | endstack = (unsigned int *)PAGE_ALIGN((unsigned int)stack); | ||
737 | } | ||
738 | } else | ||
739 | endstack = (unsigned int *)PAGE_ALIGN((unsigned int)stack); | ||
740 | |||
741 | decode_address(buf, (unsigned int)stack); | ||
742 | printk(KERN_NOTICE "Stack info:\n" KERN_NOTICE " SP: [0x%p] %s\n", stack, buf); | ||
743 | addr = (unsigned int *)((unsigned int)stack & ~0x3F); | ||
744 | |||
745 | /* First thing is to look for a frame pointer */ | ||
746 | for (addr = (unsigned int *)((unsigned int)stack & ~0xF), i = 0; | ||
747 | addr < endstack; addr++, i++) { | ||
748 | if (*addr & 0x1) | ||
749 | continue; | ||
750 | ins_addr = (unsigned short *)*addr; | ||
751 | ins_addr--; | ||
752 | if (is_bfin_call(ins_addr)) | ||
753 | fp = addr - 1; | ||
754 | |||
755 | if (fp) { | ||
756 | /* Let's check to see if it is a frame pointer */ | ||
757 | while (fp >= (addr - 1) && fp < endstack && fp) | ||
758 | fp = (unsigned int *)*fp; | ||
759 | if (fp == 0 || fp == endstack) { | ||
760 | fp = addr - 1; | ||
761 | break; | ||
762 | } | ||
763 | fp = 0; | ||
764 | } | ||
622 | } | 765 | } |
766 | if (fp) { | ||
767 | frame = fp; | ||
768 | printk(" FP: (0x%p)\n", fp); | ||
769 | } else | ||
770 | frame = 0; | ||
623 | 771 | ||
624 | addr = (unsigned long)stack; | 772 | /* |
625 | endstack = (unsigned long *)PAGE_ALIGN(addr); | 773 | * Now that we think we know where things are, we |
774 | * walk the stack again, this time printing things out | ||
775 | * incase there is no frame pointer, we still look for | ||
776 | * valid return addresses | ||
777 | */ | ||
626 | 778 | ||
627 | printk(KERN_NOTICE "Stack from %08lx:", (unsigned long)stack); | 779 | /* First time print out data, next time, print out symbols */ |
628 | for (i = 0; i < kstack_depth_to_print; i++) { | 780 | for (j = 0; j <= 1; j++) { |
629 | if (stack + 1 > endstack) | 781 | if (j) |
630 | break; | 782 | printk(KERN_NOTICE "Return addresses in stack:\n"); |
631 | if (i % 8 == 0) | 783 | else |
632 | printk("\n" KERN_NOTICE " "); | 784 | printk(KERN_NOTICE " Memory from 0x%08lx to %p", ((long unsigned int)stack & ~0xF), endstack); |
633 | printk(" %08lx", *stack++); | 785 | |
786 | fp = frame; | ||
787 | frame_no = 0; | ||
788 | |||
789 | for (addr = (unsigned int *)((unsigned int)stack & ~0xF), i = 0; | ||
790 | addr <= endstack; addr++, i++) { | ||
791 | |||
792 | ret_addr = 0; | ||
793 | if (!j && i % 8 == 0) | ||
794 | printk("\n" KERN_NOTICE "%p:",addr); | ||
795 | |||
796 | /* if it is an odd address, or zero, just skip it */ | ||
797 | if (*addr & 0x1 || !*addr) | ||
798 | goto print; | ||
799 | |||
800 | ins_addr = (unsigned short *)*addr; | ||
801 | |||
802 | /* Go back one instruction, and see if it is a CALL */ | ||
803 | ins_addr--; | ||
804 | ret_addr = is_bfin_call(ins_addr); | ||
805 | print: | ||
806 | if (!j && stack == (unsigned long *)addr) | ||
807 | printk("[%08x]", *addr); | ||
808 | else if (ret_addr) | ||
809 | if (j) { | ||
810 | decode_address(buf, (unsigned int)*addr); | ||
811 | if (frame == addr) { | ||
812 | printk(KERN_NOTICE " frame %2i : %s\n", frame_no, buf); | ||
813 | continue; | ||
814 | } | ||
815 | printk(KERN_NOTICE " address : %s\n", buf); | ||
816 | } else | ||
817 | printk("<%08x>", *addr); | ||
818 | else if (fp == addr) { | ||
819 | if (j) | ||
820 | frame = addr+1; | ||
821 | else | ||
822 | printk("(%08x)", *addr); | ||
823 | |||
824 | fp = (unsigned int *)*addr; | ||
825 | frame_no++; | ||
826 | |||
827 | } else if (!j) | ||
828 | printk(" %08x ", *addr); | ||
829 | } | ||
830 | if (!j) | ||
831 | printk("\n"); | ||
634 | } | 832 | } |
635 | printk("\n"); | ||
636 | 833 | ||
637 | show_trace(task, stack); | ||
638 | } | 834 | } |
639 | 835 | ||
640 | void dump_stack(void) | 836 | void dump_stack(void) |
@@ -715,19 +911,9 @@ void dump_bfin_mem(struct pt_regs *fp) | |||
715 | if (!((unsigned long)addr & 0xF)) | 911 | if (!((unsigned long)addr & 0xF)) |
716 | printk("\n" KERN_NOTICE "0x%p: ", addr); | 912 | printk("\n" KERN_NOTICE "0x%p: ", addr); |
717 | 913 | ||
718 | if (get_user(val, addr)) { | 914 | if (get_instruction(&val, addr)) { |
719 | if (addr >= (unsigned short *)L1_CODE_START && | ||
720 | addr < (unsigned short *)(L1_CODE_START + L1_CODE_LENGTH)) { | ||
721 | dma_memcpy(&val, addr, sizeof(val)); | ||
722 | sprintf(buf, "%04x", val); | ||
723 | } else if (addr >= (unsigned short *)FIXED_CODE_START && | ||
724 | addr <= (unsigned short *)memory_start) { | ||
725 | val = bfin_read16(addr); | ||
726 | sprintf(buf, "%04x", val); | ||
727 | } else { | ||
728 | val = 0; | 915 | val = 0; |
729 | sprintf(buf, "????"); | 916 | sprintf(buf, "????"); |
730 | } | ||
731 | } else | 917 | } else |
732 | sprintf(buf, "%04x", val); | 918 | sprintf(buf, "%04x", val); |
733 | 919 | ||