aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32/kernel/traps.c
diff options
context:
space:
mode:
authorHaavard Skinnemoen <hskinnemoen@atmel.com>2007-03-13 12:59:11 -0400
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2007-04-27 07:44:13 -0400
commit623b0355d5b1f9c6d05005b649a2f3a7b9fd7816 (patch)
tree43ef35d4f6e83a49c1fb72df4b538271b650c054 /arch/avr32/kernel/traps.c
parent3b328c98093702c584692bffabd440800b383d73 (diff)
[AVR32] Clean up exception handling code
* Use generic BUG() handling * Remove some useless debug statements * Use a common function _exception() to send signals or oops when an exception can't be handled. This makes sure init doesn't enter an infinite exception loop as well. Borrowed from powerpc. * Add some basic exception tracing support to the page fault code. * Rework dump_stack(), show_regs() and friends and move everything into process.c * Print information about configuration options and chip type when oopsing Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'arch/avr32/kernel/traps.c')
-rw-r--r--arch/avr32/kernel/traps.c421
1 files changed, 127 insertions, 294 deletions
diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c
index adc01a12d154..4f0382d8483f 100644
--- a/arch/avr32/kernel/traps.c
+++ b/arch/avr32/kernel/traps.c
@@ -5,158 +5,25 @@
5 * it under the terms of the GNU General Public License version 2 as 5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation. 6 * published by the Free Software Foundation.
7 */ 7 */
8#undef DEBUG 8
9#include <linux/sched.h> 9#include <linux/bug.h>
10#include <linux/init.h> 10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/kallsyms.h> 11#include <linux/kallsyms.h>
12#include <linux/module.h>
13#include <linux/notifier.h> 13#include <linux/notifier.h>
14#include <linux/sched.h>
15#include <linux/uaccess.h>
14 16
15#include <asm/traps.h>
16#include <asm/sysreg.h>
17#include <asm/addrspace.h> 17#include <asm/addrspace.h>
18#include <asm/ocd.h>
19#include <asm/mmu_context.h> 18#include <asm/mmu_context.h>
20#include <asm/uaccess.h> 19#include <asm/ocd.h>
21 20#include <asm/sysreg.h>
22static void dump_mem(const char *str, unsigned long bottom, unsigned long top) 21#include <asm/traps.h>
23{
24 unsigned long p;
25 int i;
26
27 printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
28
29 for (p = bottom & ~31; p < top; ) {
30 printk("%04lx: ", p & 0xffff);
31
32 for (i = 0; i < 8; i++, p += 4) {
33 unsigned int val;
34
35 if (p < bottom || p >= top)
36 printk(" ");
37 else {
38 if (__get_user(val, (unsigned int __user *)p)) {
39 printk("\n");
40 goto out;
41 }
42 printk("%08x ", val);
43 }
44 }
45 printk("\n");
46 }
47
48out:
49 return;
50}
51
52static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
53{
54 return (p > (unsigned long)tinfo)
55 && (p < (unsigned long)tinfo + THREAD_SIZE - 3);
56}
57
58#ifdef CONFIG_FRAME_POINTER
59static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
60 struct pt_regs *regs)
61{
62 unsigned long lr, fp;
63 struct thread_info *tinfo;
64
65 tinfo = (struct thread_info *)
66 ((unsigned long)sp & ~(THREAD_SIZE - 1));
67
68 if (regs)
69 fp = regs->r7;
70 else if (tsk == current)
71 asm("mov %0, r7" : "=r"(fp));
72 else
73 fp = tsk->thread.cpu_context.r7;
74
75 /*
76 * Walk the stack as long as the frame pointer (a) is within
77 * the kernel stack of the task, and (b) it doesn't move
78 * downwards.
79 */
80 while (valid_stack_ptr(tinfo, fp)) {
81 unsigned long new_fp;
82
83 lr = *(unsigned long *)fp;
84 printk(" [<%08lx>] ", lr);
85 print_symbol("%s\n", lr);
86
87 new_fp = *(unsigned long *)(fp + 4);
88 if (new_fp <= fp)
89 break;
90 fp = new_fp;
91 }
92 printk("\n");
93}
94#else
95static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
96 struct pt_regs *regs)
97{
98 unsigned long addr;
99
100 while (!kstack_end(sp)) {
101 addr = *sp++;
102 if (kernel_text_address(addr)) {
103 printk(" [<%08lx>] ", addr);
104 print_symbol("%s\n", addr);
105 }
106 }
107}
108#endif
109
110void show_trace(struct task_struct *tsk, unsigned long *sp,
111 struct pt_regs *regs)
112{
113 if (regs &&
114 (((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
115 ((regs->sr & MODE_MASK) == MODE_USER)))
116 return;
117
118 printk ("Call trace:");
119#ifdef CONFIG_KALLSYMS
120 printk("\n");
121#endif
122
123 __show_trace(tsk, sp, regs);
124 printk("\n");
125}
126
127void show_stack(struct task_struct *tsk, unsigned long *sp)
128{
129 unsigned long stack;
130
131 if (!tsk)
132 tsk = current;
133 if (sp == 0) {
134 if (tsk == current) {
135 register unsigned long *real_sp __asm__("sp");
136 sp = real_sp;
137 } else {
138 sp = (unsigned long *)tsk->thread.cpu_context.ksp;
139 }
140 }
141
142 stack = (unsigned long)sp;
143 dump_mem("Stack: ", stack,
144 THREAD_SIZE + (unsigned long)tsk->thread_info);
145 show_trace(tsk, sp, NULL);
146}
147
148void dump_stack(void)
149{
150 show_stack(NULL, NULL);
151}
152EXPORT_SYMBOL(dump_stack);
153 22
154ATOMIC_NOTIFIER_HEAD(avr32_die_chain); 23ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
155 24
156int register_die_notifier(struct notifier_block *nb) 25int register_die_notifier(struct notifier_block *nb)
157{ 26{
158 pr_debug("register_die_notifier: %p\n", nb);
159
160 return atomic_notifier_chain_register(&avr32_die_chain, nb); 27 return atomic_notifier_chain_register(&avr32_die_chain, nb);
161} 28}
162EXPORT_SYMBOL(register_die_notifier); 29EXPORT_SYMBOL(register_die_notifier);
@@ -169,93 +36,103 @@ EXPORT_SYMBOL(unregister_die_notifier);
169 36
170static DEFINE_SPINLOCK(die_lock); 37static DEFINE_SPINLOCK(die_lock);
171 38
172void __die(const char *str, struct pt_regs *regs, unsigned long err, 39void NORET_TYPE die(const char *str, struct pt_regs *regs, long err)
173 const char *file, const char *func, unsigned long line)
174{ 40{
175 struct task_struct *tsk = current;
176 static int die_counter; 41 static int die_counter;
177 42
178 console_verbose(); 43 console_verbose();
179 spin_lock_irq(&die_lock); 44 spin_lock_irq(&die_lock);
180 bust_spinlocks(1); 45 bust_spinlocks(1);
181 46
182 printk(KERN_ALERT "%s", str); 47 printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n" KERN_EMERG,
183 if (file && func) 48 str, err, ++die_counter);
184 printk(" in %s:%s, line %ld", file, func, line); 49#ifdef CONFIG_PREEMPT
185 printk("[#%d]:\n", ++die_counter); 50 printk("PREEMPT ");
186 print_modules(); 51#endif
187 show_regs(regs); 52#ifdef CONFIG_FRAME_POINTER
188 printk("Process %s (pid: %d, stack limit = 0x%p)\n", 53 printk("FRAME_POINTER ");
189 tsk->comm, tsk->pid, tsk->thread_info + 1); 54#endif
190 55 if (current_cpu_data.features & AVR32_FEATURE_OCD) {
191 if (!user_mode(regs) || in_interrupt()) { 56 unsigned long did = __mfdr(DBGREG_DID);
192 dump_mem("Stack: ", regs->sp, 57 printk("chip: 0x%03lx:0x%04lx rev %lu\n",
193 THREAD_SIZE + (unsigned long)tsk->thread_info); 58 (did >> 1) & 0x7ff,
59 (did >> 12) & 0x7fff,
60 (did >> 28) & 0xf);
61 } else {
62 printk("cpu: arch %u r%u / core %u r%u\n",
63 current_cpu_data.arch_type,
64 current_cpu_data.arch_revision,
65 current_cpu_data.cpu_type,
66 current_cpu_data.cpu_revision);
194 } 67 }
195 68
69 print_modules();
70 show_regs_log_lvl(regs, KERN_EMERG);
71 show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG);
196 bust_spinlocks(0); 72 bust_spinlocks(0);
197 spin_unlock_irq(&die_lock); 73 spin_unlock_irq(&die_lock);
198 do_exit(SIGSEGV); 74
75 if (in_interrupt())
76 panic("Fatal exception in interrupt");
77
78 if (panic_on_oops)
79 panic("Fatal exception");
80
81 do_exit(err);
199} 82}
200 83
201void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err, 84void _exception(long signr, struct pt_regs *regs, int code,
202 const char *file, const char *func, unsigned long line) 85 unsigned long addr)
203{ 86{
87 siginfo_t info;
88
204 if (!user_mode(regs)) 89 if (!user_mode(regs))
205 __die(str, regs, err, file, func, line); 90 die("Unhandled exception in kernel mode", regs, signr);
206} 91
92 memset(&info, 0, sizeof(info));
93 info.si_signo = signr;
94 info.si_code = code;
95 info.si_addr = (void __user *)addr;
96 force_sig_info(signr, &info, current);
207 97
208asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
209{
210#ifdef CONFIG_SUBARCH_AVR32B
211 /* 98 /*
212 * The exception entry always saves RSR_EX. For NMI, this is 99 * Init gets no signals that it doesn't have a handler for.
213 * wrong; it should be RSR_NMI 100 * That's all very well, but if it has caused a synchronous
101 * exception and we ignore the resulting signal, it will just
102 * generate the same exception over and over again and we get
103 * nowhere. Better to kill it and let the kernel panic.
214 */ 104 */
215 regs->sr = sysreg_read(RSR_NMI); 105 if (is_init(current)) {
216#endif 106 __sighandler_t handler;
107
108 spin_lock_irq(&current->sighand->siglock);
109 handler = current->sighand->action[signr-1].sa.sa_handler;
110 spin_unlock_irq(&current->sighand->siglock);
111 if (handler == SIG_DFL) {
112 /* init has generated a synchronous exception
113 and it doesn't have a handler for the signal */
114 printk(KERN_CRIT "init has generated signal %ld "
115 "but has no handler for it\n", signr);
116 do_exit(signr);
117 }
118 }
119}
217 120
218 printk("NMI taken!!!!\n"); 121asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
219 die("NMI", regs, ecr); 122{
220 BUG(); 123 printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n");
124 show_regs_log_lvl(regs, KERN_ALERT);
125 show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT);
221} 126}
222 127
223asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs) 128asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
224{ 129{
225 printk("Unable to handle critical exception %lu at pc = %08lx!\n", 130 die("Critical exception", regs, SIGKILL);
226 ecr, regs->pc);
227 die("Oops", regs, ecr);
228 BUG();
229} 131}
230 132
231asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs) 133asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
232{ 134{
233 siginfo_t info; 135 _exception(SIGBUS, regs, BUS_ADRALN, regs->pc);
234
235 die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
236
237#ifdef DEBUG
238 if (ecr == ECR_ADDR_ALIGN_X)
239 pr_debug("Instruction Address Exception at pc = %08lx\n",
240 regs->pc);
241 else if (ecr == ECR_ADDR_ALIGN_R)
242 pr_debug("Data Address Exception (Read) at pc = %08lx\n",
243 regs->pc);
244 else if (ecr == ECR_ADDR_ALIGN_W)
245 pr_debug("Data Address Exception (Write) at pc = %08lx\n",
246 regs->pc);
247 else
248 BUG();
249
250 show_regs(regs);
251#endif
252
253 info.si_signo = SIGBUS;
254 info.si_errno = 0;
255 info.si_code = BUS_ADRALN;
256 info.si_addr = (void __user *)regs->pc;
257
258 force_sig_info(SIGBUS, &info, current);
259} 136}
260 137
261/* This way of handling undefined instructions is stolen from ARM */ 138/* This way of handling undefined instructions is stolen from ARM */
@@ -280,7 +157,8 @@ static int do_cop_absent(u32 insn)
280{ 157{
281 int cop_nr; 158 int cop_nr;
282 u32 cpucr; 159 u32 cpucr;
283 if ( (insn & 0xfdf00000) == 0xf1900000 ) 160
161 if ((insn & 0xfdf00000) == 0xf1900000)
284 /* LDC0 */ 162 /* LDC0 */
285 cop_nr = 0; 163 cop_nr = 0;
286 else 164 else
@@ -292,136 +170,91 @@ static int do_cop_absent(u32 insn)
292 sysreg_write(CPUCR, cpucr); 170 sysreg_write(CPUCR, cpucr);
293 171
294 cpucr = sysreg_read(CPUCR); 172 cpucr = sysreg_read(CPUCR);
295 if ( !(cpucr & (1 << (24 + cop_nr))) ){ 173 if (!(cpucr & (1 << (24 + cop_nr))))
296 printk("Coprocessor #%i not found!\n", cop_nr); 174 return -ENODEV;
297 return -1;
298 }
299 175
300 return 0; 176 return 0;
301} 177}
302 178
303#ifdef CONFIG_BUG 179int is_valid_bugaddr(unsigned long pc)
304#ifdef CONFIG_DEBUG_BUGVERBOSE
305static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
306{
307 char *file;
308 u16 line;
309 char c;
310
311 if (__get_user(line, (u16 __user *)(regs->pc + 2)))
312 return;
313 if (__get_user(file, (char * __user *)(regs->pc + 4))
314 || (unsigned long)file < PAGE_OFFSET
315 || __get_user(c, file))
316 file = "<bad filename>";
317
318 printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
319}
320#else
321static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
322{ 180{
181 unsigned short opcode;
182
183 if (pc < PAGE_OFFSET)
184 return 0;
185 if (probe_kernel_address((u16 *)pc, opcode))
186 return 0;
323 187
188 return opcode == AVR32_BUG_OPCODE;
324} 189}
325#endif
326#endif
327 190
328asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs) 191asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
329{ 192{
330 u32 insn; 193 u32 insn;
331 struct undef_hook *hook; 194 struct undef_hook *hook;
332 siginfo_t info;
333 void __user *pc; 195 void __user *pc;
196 long code;
334 197
335 if (!user_mode(regs)) 198 if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) {
336 goto kernel_trap; 199 enum bug_trap_type type;
200
201 type = report_bug(regs->pc);
202 switch (type) {
203 case BUG_TRAP_TYPE_NONE:
204 break;
205 case BUG_TRAP_TYPE_WARN:
206 regs->pc += 2;
207 return;
208 case BUG_TRAP_TYPE_BUG:
209 die("Kernel BUG", regs, SIGKILL);
210 }
211 }
337 212
338 local_irq_enable(); 213 local_irq_enable();
339 214
340 pc = (void __user *)instruction_pointer(regs); 215 if (user_mode(regs)) {
341 if (__get_user(insn, (u32 __user *)pc)) 216 pc = (void __user *)instruction_pointer(regs);
342 goto invalid_area; 217 if (get_user(insn, (u32 __user *)pc))
218 goto invalid_area;
343 219
344 if (ecr == ECR_COPROC_ABSENT) { 220 if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn))
345 if (do_cop_absent(insn) == 0)
346 return; 221 return;
347 }
348 222
349 spin_lock_irq(&undef_lock); 223 spin_lock_irq(&undef_lock);
350 list_for_each_entry(hook, &undef_hook, node) { 224 list_for_each_entry(hook, &undef_hook, node) {
351 if ((insn & hook->insn_mask) == hook->insn_val) { 225 if ((insn & hook->insn_mask) == hook->insn_val) {
352 if (hook->fn(regs, insn) == 0) { 226 if (hook->fn(regs, insn) == 0) {
353 spin_unlock_irq(&undef_lock); 227 spin_unlock_irq(&undef_lock);
354 return; 228 return;
229 }
355 } 230 }
356 } 231 }
232 spin_unlock_irq(&undef_lock);
357 } 233 }
358 spin_unlock_irq(&undef_lock);
359
360invalid_area:
361 234
362#ifdef DEBUG
363 printk("Illegal instruction at pc = %08lx\n", regs->pc);
364 if (regs->pc < TASK_SIZE) {
365 unsigned long ptbr, pgd, pte, *p;
366
367 ptbr = sysreg_read(PTBR);
368 p = (unsigned long *)ptbr;
369 pgd = p[regs->pc >> 22];
370 p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
371 pte = p[(regs->pc >> 12) & 0x3ff];
372 printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
373 }
374#endif
375
376 info.si_signo = SIGILL;
377 info.si_errno = 0;
378 info.si_addr = (void __user *)regs->pc;
379 switch (ecr) { 235 switch (ecr) {
380 case ECR_ILLEGAL_OPCODE:
381 case ECR_UNIMPL_INSTRUCTION:
382 info.si_code = ILL_ILLOPC;
383 break;
384 case ECR_PRIVILEGE_VIOLATION: 236 case ECR_PRIVILEGE_VIOLATION:
385 info.si_code = ILL_PRVOPC; 237 code = ILL_PRVOPC;
386 break; 238 break;
387 case ECR_COPROC_ABSENT: 239 case ECR_COPROC_ABSENT:
388 info.si_code = ILL_COPROC; 240 code = ILL_COPROC;
389 break; 241 break;
390 default: 242 default:
391 BUG(); 243 code = ILL_ILLOPC;
244 break;
392 } 245 }
393 246
394 force_sig_info(SIGILL, &info, current); 247 _exception(SIGILL, regs, code, regs->pc);
395 return; 248 return;
396 249
397kernel_trap: 250invalid_area:
398#ifdef CONFIG_BUG 251 _exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc);
399 if (__kernel_text_address(instruction_pointer(regs))) {
400 insn = *(u16 *)instruction_pointer(regs);
401 if (insn == AVR32_BUG_OPCODE) {
402 do_bug_verbose(regs, insn);
403 die("Kernel BUG", regs, 0);
404 return;
405 }
406 }
407#endif
408
409 die("Oops: Illegal instruction in kernel code", regs, ecr);
410} 252}
411 253
412asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs) 254asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
413{ 255{
414 siginfo_t info; 256 /* We have no FPU yet */
415 257 _exception(SIGILL, regs, ILL_COPROC, regs->pc);
416 printk("Floating-point exception at pc = %08lx\n", regs->pc);
417
418 /* We have no FPU... */
419 info.si_signo = SIGILL;
420 info.si_errno = 0;
421 info.si_addr = (void __user *)regs->pc;
422 info.si_code = ILL_COPROC;
423
424 force_sig_info(SIGILL, &info, current);
425} 258}
426 259
427 260