diff options
Diffstat (limited to 'arch/sh/kernel/traps.c')
| -rw-r--r-- | arch/sh/kernel/traps.c | 167 |
1 files changed, 94 insertions, 73 deletions
diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index c2c597e09482..53dfa55f3156 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c | |||
| @@ -1,38 +1,25 @@ | |||
| 1 | /* $Id: traps.c,v 1.17 2004/05/02 01:46:30 sugioka Exp $ | 1 | /* |
| 2 | * | 2 | * 'traps.c' handles hardware traps and faults after we have saved some |
| 3 | * linux/arch/sh/traps.c | 3 | * state in 'entry.S'. |
| 4 | * | 4 | * |
| 5 | * SuperH version: Copyright (C) 1999 Niibe Yutaka | 5 | * SuperH version: Copyright (C) 1999 Niibe Yutaka |
| 6 | * Copyright (C) 2000 Philipp Rumpf | 6 | * Copyright (C) 2000 Philipp Rumpf |
| 7 | * Copyright (C) 2000 David Howells | 7 | * Copyright (C) 2000 David Howells |
| 8 | * Copyright (C) 2002, 2003 Paul Mundt | 8 | * Copyright (C) 2002 - 2006 Paul Mundt |
| 9 | */ | 9 | * |
| 10 | 10 | * This file is subject to the terms and conditions of the GNU General Public | |
| 11 | /* | 11 | * License. See the file "COPYING" in the main directory of this archive |
| 12 | * 'Traps.c' handles hardware traps and faults after we have saved some | 12 | * for more details. |
| 13 | * state in 'entry.S'. | ||
| 14 | */ | 13 | */ |
| 15 | #include <linux/sched.h> | ||
| 16 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 17 | #include <linux/string.h> | ||
| 18 | #include <linux/errno.h> | ||
| 19 | #include <linux/ptrace.h> | 15 | #include <linux/ptrace.h> |
| 20 | #include <linux/timer.h> | ||
| 21 | #include <linux/mm.h> | ||
| 22 | #include <linux/smp.h> | ||
| 23 | #include <linux/smp_lock.h> | ||
| 24 | #include <linux/init.h> | 16 | #include <linux/init.h> |
| 25 | #include <linux/delay.h> | ||
| 26 | #include <linux/spinlock.h> | 17 | #include <linux/spinlock.h> |
| 27 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 28 | #include <linux/kallsyms.h> | 19 | #include <linux/kallsyms.h> |
| 29 | 20 | #include <linux/io.h> | |
| 30 | #include <asm/system.h> | 21 | #include <asm/system.h> |
| 31 | #include <asm/uaccess.h> | 22 | #include <asm/uaccess.h> |
| 32 | #include <asm/io.h> | ||
| 33 | #include <asm/atomic.h> | ||
| 34 | #include <asm/processor.h> | ||
| 35 | #include <asm/sections.h> | ||
| 36 | 23 | ||
| 37 | #ifdef CONFIG_SH_KGDB | 24 | #ifdef CONFIG_SH_KGDB |
| 38 | #include <asm/kgdb.h> | 25 | #include <asm/kgdb.h> |
| @@ -53,13 +40,32 @@ | |||
| 53 | #define TRAP_ILLEGAL_SLOT_INST 13 | 40 | #define TRAP_ILLEGAL_SLOT_INST 13 |
| 54 | #endif | 41 | #endif |
| 55 | 42 | ||
| 56 | /* | 43 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) |
| 57 | * These constants are for searching for possible module text | 44 | { |
| 58 | * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is | 45 | unsigned long p; |
| 59 | * a guess of how much space is likely to be vmalloced. | 46 | int i; |
| 60 | */ | 47 | |
| 61 | #define VMALLOC_OFFSET (8*1024*1024) | 48 | printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); |
| 62 | #define MODULE_RANGE (8*1024*1024) | 49 | |
| 50 | for (p = bottom & ~31; p < top; ) { | ||
| 51 | printk("%04lx: ", p & 0xffff); | ||
| 52 | |||
| 53 | for (i = 0; i < 8; i++, p += 4) { | ||
| 54 | unsigned int val; | ||
| 55 | |||
| 56 | if (p < bottom || p >= top) | ||
| 57 | printk(" "); | ||
| 58 | else { | ||
| 59 | if (__get_user(val, (unsigned int __user *)p)) { | ||
| 60 | printk("\n"); | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | printk("%08x ", val); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | printk("\n"); | ||
| 67 | } | ||
| 68 | } | ||
| 63 | 69 | ||
| 64 | DEFINE_SPINLOCK(die_lock); | 70 | DEFINE_SPINLOCK(die_lock); |
| 65 | 71 | ||
| @@ -69,14 +75,28 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
| 69 | 75 | ||
| 70 | console_verbose(); | 76 | console_verbose(); |
| 71 | spin_lock_irq(&die_lock); | 77 | spin_lock_irq(&die_lock); |
| 78 | bust_spinlocks(1); | ||
| 79 | |||
| 72 | printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); | 80 | printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); |
| 81 | |||
| 73 | CHK_REMOTE_DEBUG(regs); | 82 | CHK_REMOTE_DEBUG(regs); |
| 83 | print_modules(); | ||
| 74 | show_regs(regs); | 84 | show_regs(regs); |
| 85 | |||
| 86 | printk("Process: %s (pid: %d, stack limit = %p)\n", | ||
| 87 | current->comm, current->pid, task_stack_page(current) + 1); | ||
| 88 | |||
| 89 | if (!user_mode(regs) || in_interrupt()) | ||
| 90 | dump_mem("Stack: ", regs->regs[15], THREAD_SIZE + | ||
| 91 | (unsigned long)task_stack_page(current)); | ||
| 92 | |||
| 93 | bust_spinlocks(0); | ||
| 75 | spin_unlock_irq(&die_lock); | 94 | spin_unlock_irq(&die_lock); |
| 76 | do_exit(SIGSEGV); | 95 | do_exit(SIGSEGV); |
| 77 | } | 96 | } |
| 78 | 97 | ||
| 79 | static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err) | 98 | static inline void die_if_kernel(const char *str, struct pt_regs *regs, |
| 99 | long err) | ||
| 80 | { | 100 | { |
| 81 | if (!user_mode(regs)) | 101 | if (!user_mode(regs)) |
| 82 | die(str, regs, err); | 102 | die(str, regs, err); |
| @@ -93,8 +113,7 @@ static int handle_unaligned_notify_count = 10; | |||
| 93 | */ | 113 | */ |
| 94 | static int die_if_no_fixup(const char * str, struct pt_regs * regs, long err) | 114 | static int die_if_no_fixup(const char * str, struct pt_regs * regs, long err) |
| 95 | { | 115 | { |
| 96 | if (!user_mode(regs)) | 116 | if (!user_mode(regs)) { |
| 97 | { | ||
| 98 | const struct exception_table_entry *fixup; | 117 | const struct exception_table_entry *fixup; |
| 99 | fixup = search_exception_tables(regs->pc); | 118 | fixup = search_exception_tables(regs->pc); |
| 100 | if (fixup) { | 119 | if (fixup) { |
| @@ -550,7 +569,10 @@ int is_dsp_inst(struct pt_regs *regs) | |||
| 550 | #define is_dsp_inst(regs) (0) | 569 | #define is_dsp_inst(regs) (0) |
| 551 | #endif /* CONFIG_SH_DSP */ | 570 | #endif /* CONFIG_SH_DSP */ |
| 552 | 571 | ||
| 553 | extern int do_fpu_inst(unsigned short, struct pt_regs*); | 572 | /* arch/sh/kernel/cpu/sh4/fpu.c */ |
| 573 | extern int do_fpu_inst(unsigned short, struct pt_regs *); | ||
| 574 | extern asmlinkage void do_fpu_state_restore(unsigned long r4, unsigned long r5, | ||
| 575 | unsigned long r6, unsigned long r7, struct pt_regs regs); | ||
| 554 | 576 | ||
| 555 | asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, | 577 | asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, |
| 556 | unsigned long r6, unsigned long r7, | 578 | unsigned long r6, unsigned long r7, |
| @@ -709,14 +731,20 @@ void __init per_cpu_trap_init(void) | |||
| 709 | : "memory"); | 731 | : "memory"); |
| 710 | } | 732 | } |
| 711 | 733 | ||
| 712 | void __init trap_init(void) | 734 | void *set_exception_table_vec(unsigned int vec, void *handler) |
| 713 | { | 735 | { |
| 714 | extern void *exception_handling_table[]; | 736 | extern void *exception_handling_table[]; |
| 737 | void *old_handler; | ||
| 738 | |||
| 739 | old_handler = exception_handling_table[vec]; | ||
| 740 | exception_handling_table[vec] = handler; | ||
| 741 | return old_handler; | ||
| 742 | } | ||
| 715 | 743 | ||
| 716 | exception_handling_table[TRAP_RESERVED_INST] | 744 | void __init trap_init(void) |
| 717 | = (void *)do_reserved_inst; | 745 | { |
| 718 | exception_handling_table[TRAP_ILLEGAL_SLOT_INST] | 746 | set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst); |
| 719 | = (void *)do_illegal_slot_inst; | 747 | set_exception_table_vec(TRAP_ILLEGAL_SLOT_INST, do_illegal_slot_inst); |
| 720 | 748 | ||
| 721 | #if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SH_FPU) || \ | 749 | #if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SH_FPU) || \ |
| 722 | defined(CONFIG_SH_FPU_EMU) | 750 | defined(CONFIG_SH_FPU_EMU) |
| @@ -725,61 +753,54 @@ void __init trap_init(void) | |||
| 725 | * reserved. They'll be handled in the math-emu case, or faulted on | 753 | * reserved. They'll be handled in the math-emu case, or faulted on |
| 726 | * otherwise. | 754 | * otherwise. |
| 727 | */ | 755 | */ |
| 728 | /* entry 64 corresponds to EXPEVT=0x800 */ | 756 | set_exception_table_evt(0x800, do_reserved_inst); |
| 729 | exception_handling_table[64] = (void *)do_reserved_inst; | 757 | set_exception_table_evt(0x820, do_illegal_slot_inst); |
| 730 | exception_handling_table[65] = (void *)do_illegal_slot_inst; | 758 | #elif defined(CONFIG_SH_FPU) |
| 759 | set_exception_table_evt(0x800, do_fpu_state_restore); | ||
| 760 | set_exception_table_evt(0x820, do_fpu_state_restore); | ||
| 731 | #endif | 761 | #endif |
| 732 | 762 | ||
| 733 | /* Setup VBR for boot cpu */ | 763 | /* Setup VBR for boot cpu */ |
| 734 | per_cpu_trap_init(); | 764 | per_cpu_trap_init(); |
| 735 | } | 765 | } |
| 736 | 766 | ||
| 737 | void show_stack(struct task_struct *tsk, unsigned long *sp) | 767 | void show_trace(struct task_struct *tsk, unsigned long *sp, |
| 768 | struct pt_regs *regs) | ||
| 738 | { | 769 | { |
| 739 | unsigned long *stack, addr; | 770 | unsigned long addr; |
| 740 | unsigned long module_start = VMALLOC_START; | ||
| 741 | unsigned long module_end = VMALLOC_END; | ||
| 742 | int i = 1; | ||
| 743 | |||
| 744 | if (!tsk) | ||
| 745 | tsk = current; | ||
| 746 | if (tsk == current) | ||
| 747 | sp = (unsigned long *)current_stack_pointer; | ||
| 748 | else | ||
| 749 | sp = (unsigned long *)tsk->thread.sp; | ||
| 750 | 771 | ||
| 751 | stack = sp; | 772 | if (regs && user_mode(regs)) |
| 773 | return; | ||
| 752 | 774 | ||
| 753 | printk("\nCall trace: "); | 775 | printk("\nCall trace: "); |
| 754 | #ifdef CONFIG_KALLSYMS | 776 | #ifdef CONFIG_KALLSYMS |
| 755 | printk("\n"); | 777 | printk("\n"); |
| 756 | #endif | 778 | #endif |
| 757 | 779 | ||
| 758 | while (!kstack_end(stack)) { | 780 | while (!kstack_end(sp)) { |
| 759 | addr = *stack++; | 781 | addr = *sp++; |
| 760 | if (((addr >= (unsigned long)_text) && | 782 | if (kernel_text_address(addr)) |
| 761 | (addr <= (unsigned long)_etext)) || | 783 | print_ip_sym(addr); |
| 762 | ((addr >= module_start) && (addr <= module_end))) { | ||
| 763 | /* | ||
| 764 | * For 80-columns display, 6 entry is maximum. | ||
| 765 | * NOTE: '[<8c00abcd>] ' consumes 13 columns . | ||
| 766 | */ | ||
| 767 | #ifndef CONFIG_KALLSYMS | ||
| 768 | if (i && ((i % 6) == 0)) | ||
| 769 | printk("\n "); | ||
| 770 | #endif | ||
| 771 | printk("[<%08lx>] ", addr); | ||
| 772 | print_symbol("%s\n", addr); | ||
| 773 | i++; | ||
| 774 | } | ||
| 775 | } | 784 | } |
| 776 | 785 | ||
| 777 | printk("\n"); | 786 | printk("\n"); |
| 778 | } | 787 | } |
| 779 | 788 | ||
| 780 | void show_task(unsigned long *sp) | 789 | void show_stack(struct task_struct *tsk, unsigned long *sp) |
| 781 | { | 790 | { |
| 782 | show_stack(NULL, sp); | 791 | unsigned long stack; |
| 792 | |||
| 793 | if (!tsk) | ||
| 794 | tsk = current; | ||
| 795 | if (tsk == current) | ||
| 796 | sp = (unsigned long *)current_stack_pointer; | ||
| 797 | else | ||
| 798 | sp = (unsigned long *)tsk->thread.sp; | ||
| 799 | |||
| 800 | stack = (unsigned long)sp; | ||
| 801 | dump_mem("Stack: ", stack, THREAD_SIZE + | ||
| 802 | (unsigned long)task_stack_page(tsk)); | ||
| 803 | show_trace(tsk, sp, NULL); | ||
| 783 | } | 804 | } |
| 784 | 805 | ||
| 785 | void dump_stack(void) | 806 | void dump_stack(void) |
