aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r--arch/mips/kernel/traps.c146
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);
72void (*board_ejtag_handler_setup)(void); 73void (*board_ejtag_handler_setup)(void);
73void (*board_bind_eic_interrupt)(int irq, int regset); 74void (*board_bind_eic_interrupt)(int irq, int regset);
74 75
75/* 76
76 * These constant is for searching for possible module text segments. 77static 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
95static int raw_show_trace;
96static 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
103extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
104 unsigned long pc, unsigned long ra);
105
106static 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 */
85void show_stack(struct task_struct *task, unsigned long *sp) 132static 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
119void show_trace(struct task_struct *task, unsigned long *stack) 161static __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
183void 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(&regs);
140 } 197 }
141 } 198 }
142 printk("\n"); 199 show_stacktrace(task, &regs);
143} 200}
144 201
145/* 202/*
@@ -147,9 +204,15 @@ void show_trace(struct task_struct *task, unsigned long *stack)
147 */ 204 */
148void dump_stack(void) 205void 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(&regs, 0, sizeof(regs));
214 prepare_frametrace(&regs);
215 show_backtrace(current, &regs);
153} 216}
154 217
155EXPORT_SYMBOL(dump_stack); 218EXPORT_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