diff options
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r-- | arch/mips/kernel/traps.c | 146 |
1 files changed, 109 insertions, 37 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 954a198494ef..e51d8fd9a152 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/spinlock.h> | 20 | #include <linux/spinlock.h> |
21 | #include <linux/kallsyms.h> | 21 | #include <linux/kallsyms.h> |
22 | #include <linux/bootmem.h> | 22 | #include <linux/bootmem.h> |
23 | #include <linux/interrupt.h> | ||
23 | 24 | ||
24 | #include <asm/bootinfo.h> | 25 | #include <asm/bootinfo.h> |
25 | #include <asm/branch.h> | 26 | #include <asm/branch.h> |
@@ -72,28 +73,68 @@ void (*board_nmi_handler_setup)(void); | |||
72 | void (*board_ejtag_handler_setup)(void); | 73 | void (*board_ejtag_handler_setup)(void); |
73 | void (*board_bind_eic_interrupt)(int irq, int regset); | 74 | void (*board_bind_eic_interrupt)(int irq, int regset); |
74 | 75 | ||
75 | /* | 76 | |
76 | * These constant is for searching for possible module text segments. | 77 | static void show_raw_backtrace(unsigned long reg29) |
77 | * MODULE_RANGE is a guess of how much space is likely to be vmalloced. | 78 | { |
78 | */ | 79 | unsigned long *sp = (unsigned long *)reg29; |
79 | #define MODULE_RANGE (8*1024*1024) | 80 | unsigned long addr; |
81 | |||
82 | printk("Call Trace:"); | ||
83 | #ifdef CONFIG_KALLSYMS | ||
84 | printk("\n"); | ||
85 | #endif | ||
86 | while (!kstack_end(sp)) { | ||
87 | addr = *sp++; | ||
88 | if (__kernel_text_address(addr)) | ||
89 | print_ip_sym(addr); | ||
90 | } | ||
91 | printk("\n"); | ||
92 | } | ||
93 | |||
94 | #ifdef CONFIG_KALLSYMS | ||
95 | static int raw_show_trace; | ||
96 | static int __init set_raw_show_trace(char *str) | ||
97 | { | ||
98 | raw_show_trace = 1; | ||
99 | return 1; | ||
100 | } | ||
101 | __setup("raw_show_trace", set_raw_show_trace); | ||
102 | |||
103 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | ||
104 | unsigned long pc, unsigned long ra); | ||
105 | |||
106 | static void show_backtrace(struct task_struct *task, struct pt_regs *regs) | ||
107 | { | ||
108 | unsigned long sp = regs->regs[29]; | ||
109 | unsigned long ra = regs->regs[31]; | ||
110 | unsigned long pc = regs->cp0_epc; | ||
111 | |||
112 | if (raw_show_trace || !__kernel_text_address(pc)) { | ||
113 | show_raw_backtrace(sp); | ||
114 | return; | ||
115 | } | ||
116 | printk("Call Trace:\n"); | ||
117 | do { | ||
118 | print_ip_sym(pc); | ||
119 | pc = unwind_stack(task, &sp, pc, ra); | ||
120 | ra = 0; | ||
121 | } while (pc); | ||
122 | printk("\n"); | ||
123 | } | ||
124 | #else | ||
125 | #define show_backtrace(task, r) show_raw_backtrace((r)->regs[29]); | ||
126 | #endif | ||
80 | 127 | ||
81 | /* | 128 | /* |
82 | * This routine abuses get_user()/put_user() to reference pointers | 129 | * This routine abuses get_user()/put_user() to reference pointers |
83 | * with at least a bit of error checking ... | 130 | * with at least a bit of error checking ... |
84 | */ | 131 | */ |
85 | void show_stack(struct task_struct *task, unsigned long *sp) | 132 | static void show_stacktrace(struct task_struct *task, struct pt_regs *regs) |
86 | { | 133 | { |
87 | const int field = 2 * sizeof(unsigned long); | 134 | const int field = 2 * sizeof(unsigned long); |
88 | long stackdata; | 135 | long stackdata; |
89 | int i; | 136 | int i; |
90 | 137 | unsigned long *sp = (unsigned long *)regs->regs[29]; | |
91 | if (!sp) { | ||
92 | if (task && task != current) | ||
93 | sp = (unsigned long *) task->thread.reg29; | ||
94 | else | ||
95 | sp = (unsigned long *) &sp; | ||
96 | } | ||
97 | 138 | ||
98 | printk("Stack :"); | 139 | printk("Stack :"); |
99 | i = 0; | 140 | i = 0; |
@@ -114,32 +155,48 @@ void show_stack(struct task_struct *task, unsigned long *sp) | |||
114 | i++; | 155 | i++; |
115 | } | 156 | } |
116 | printk("\n"); | 157 | printk("\n"); |
158 | show_backtrace(task, regs); | ||
117 | } | 159 | } |
118 | 160 | ||
119 | void show_trace(struct task_struct *task, unsigned long *stack) | 161 | static __always_inline void prepare_frametrace(struct pt_regs *regs) |
120 | { | 162 | { |
121 | const int field = 2 * sizeof(unsigned long); | 163 | __asm__ __volatile__( |
122 | unsigned long addr; | 164 | ".set push\n\t" |
123 | 165 | ".set noat\n\t" | |
124 | if (!stack) { | 166 | #ifdef CONFIG_64BIT |
125 | if (task && task != current) | 167 | "1: dla $1, 1b\n\t" |
126 | stack = (unsigned long *) task->thread.reg29; | 168 | "sd $1, %0\n\t" |
127 | else | 169 | "sd $29, %1\n\t" |
128 | stack = (unsigned long *) &stack; | 170 | "sd $31, %2\n\t" |
129 | } | 171 | #else |
130 | 172 | "1: la $1, 1b\n\t" | |
131 | printk("Call Trace:"); | 173 | "sw $1, %0\n\t" |
132 | #ifdef CONFIG_KALLSYMS | 174 | "sw $29, %1\n\t" |
133 | printk("\n"); | 175 | "sw $31, %2\n\t" |
134 | #endif | 176 | #endif |
135 | while (!kstack_end(stack)) { | 177 | ".set pop\n\t" |
136 | addr = *stack++; | 178 | : "=m" (regs->cp0_epc), |
137 | if (__kernel_text_address(addr)) { | 179 | "=m" (regs->regs[29]), "=m" (regs->regs[31]) |
138 | printk(" [<%0*lx>] ", field, addr); | 180 | : : "memory"); |
139 | print_symbol("%s\n", addr); | 181 | } |
182 | |||
183 | void show_stack(struct task_struct *task, unsigned long *sp) | ||
184 | { | ||
185 | struct pt_regs regs; | ||
186 | if (sp) { | ||
187 | regs.regs[29] = (unsigned long)sp; | ||
188 | regs.regs[31] = 0; | ||
189 | regs.cp0_epc = 0; | ||
190 | } else { | ||
191 | if (task && task != current) { | ||
192 | regs.regs[29] = task->thread.reg29; | ||
193 | regs.regs[31] = 0; | ||
194 | regs.cp0_epc = task->thread.reg31; | ||
195 | } else { | ||
196 | prepare_frametrace(®s); | ||
140 | } | 197 | } |
141 | } | 198 | } |
142 | printk("\n"); | 199 | show_stacktrace(task, ®s); |
143 | } | 200 | } |
144 | 201 | ||
145 | /* | 202 | /* |
@@ -147,9 +204,15 @@ void show_trace(struct task_struct *task, unsigned long *stack) | |||
147 | */ | 204 | */ |
148 | void dump_stack(void) | 205 | void dump_stack(void) |
149 | { | 206 | { |
150 | unsigned long stack; | 207 | struct pt_regs regs; |
151 | 208 | ||
152 | show_trace(current, &stack); | 209 | /* |
210 | * Remove any garbage that may be in regs (specially func | ||
211 | * addresses) to avoid show_raw_backtrace() to report them | ||
212 | */ | ||
213 | memset(®s, 0, sizeof(regs)); | ||
214 | prepare_frametrace(®s); | ||
215 | show_backtrace(current, ®s); | ||
153 | } | 216 | } |
154 | 217 | ||
155 | EXPORT_SYMBOL(dump_stack); | 218 | EXPORT_SYMBOL(dump_stack); |
@@ -268,8 +331,7 @@ void show_registers(struct pt_regs *regs) | |||
268 | print_modules(); | 331 | print_modules(); |
269 | printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", | 332 | printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", |
270 | current->comm, current->pid, current_thread_info(), current); | 333 | current->comm, current->pid, current_thread_info(), current); |
271 | show_stack(current, (long *) regs->regs[29]); | 334 | show_stacktrace(current, regs); |
272 | show_trace(current, (long *) regs->regs[29]); | ||
273 | show_code((unsigned int *) regs->cp0_epc); | 335 | show_code((unsigned int *) regs->cp0_epc); |
274 | printk("\n"); | 336 | printk("\n"); |
275 | } | 337 | } |
@@ -292,6 +354,16 @@ NORET_TYPE void ATTRIB_NORET die(const char * str, struct pt_regs * regs) | |||
292 | printk("%s[#%d]:\n", str, ++die_counter); | 354 | printk("%s[#%d]:\n", str, ++die_counter); |
293 | show_registers(regs); | 355 | show_registers(regs); |
294 | spin_unlock_irq(&die_lock); | 356 | spin_unlock_irq(&die_lock); |
357 | |||
358 | if (in_interrupt()) | ||
359 | panic("Fatal exception in interrupt"); | ||
360 | |||
361 | if (panic_on_oops) { | ||
362 | printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); | ||
363 | ssleep(5); | ||
364 | panic("Fatal exception"); | ||
365 | } | ||
366 | |||
295 | do_exit(SIGSEGV); | 367 | do_exit(SIGSEGV); |
296 | } | 368 | } |
297 | 369 | ||