[PATCH] avr32 architecture
[deliverable/linux.git] / arch / avr32 / kernel / traps.c
CommitLineData
5f97f7f9
HS
1/*
2 * Copyright (C) 2004-2006 Atmel Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#undef DEBUG
9#include <linux/sched.h>
10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/kallsyms.h>
13#include <linux/notifier.h>
14
15#include <asm/traps.h>
16#include <asm/sysreg.h>
17#include <asm/addrspace.h>
18#include <asm/ocd.h>
19#include <asm/mmu_context.h>
20#include <asm/uaccess.h>
21
22static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
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
52#ifdef CONFIG_FRAME_POINTER
53static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
54 struct pt_regs *regs)
55{
56 unsigned long __user *fp;
57 unsigned long __user *last_fp = NULL;
58
59 if (regs) {
60 fp = (unsigned long __user *)regs->r7;
61 } else if (tsk == current) {
62 register unsigned long __user *real_fp __asm__("r7");
63 fp = real_fp;
64 } else {
65 fp = (unsigned long __user *)tsk->thread.cpu_context.r7;
66 }
67
68 /*
69 * Walk the stack until (a) we get an exception, (b) the frame
70 * pointer becomes zero, or (c) the frame pointer gets stuck
71 * at the same value.
72 */
73 while (fp && fp != last_fp) {
74 unsigned long lr, new_fp = 0;
75
76 last_fp = fp;
77 if (__get_user(lr, fp))
78 break;
79 if (fp && __get_user(new_fp, fp + 1))
80 break;
81 fp = (unsigned long __user *)new_fp;
82
83 printk(" [<%08lx>] ", lr);
84 print_symbol("%s\n", lr);
85 }
86 printk("\n");
87}
88#else
89static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
90 struct pt_regs *regs)
91{
92 unsigned long addr;
93
94 while (!kstack_end(sp)) {
95 addr = *sp++;
96 if (kernel_text_address(addr)) {
97 printk(" [<%08lx>] ", addr);
98 print_symbol("%s\n", addr);
99 }
100 }
101}
102#endif
103
104void show_trace(struct task_struct *tsk, unsigned long *sp,
105 struct pt_regs *regs)
106{
107 if (regs &&
108 (((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
109 ((regs->sr & MODE_MASK) == MODE_USER)))
110 return;
111
112 printk ("Call trace:");
113#ifdef CONFIG_KALLSYMS
114 printk("\n");
115#endif
116
117 __show_trace(tsk, sp, regs);
118 printk("\n");
119}
120
121void show_stack(struct task_struct *tsk, unsigned long *sp)
122{
123 unsigned long stack;
124
125 if (!tsk)
126 tsk = current;
127 if (sp == 0) {
128 if (tsk == current) {
129 register unsigned long *real_sp __asm__("sp");
130 sp = real_sp;
131 } else {
132 sp = (unsigned long *)tsk->thread.cpu_context.ksp;
133 }
134 }
135
136 stack = (unsigned long)sp;
137 dump_mem("Stack: ", stack,
138 THREAD_SIZE + (unsigned long)tsk->thread_info);
139 show_trace(tsk, sp, NULL);
140}
141
142void dump_stack(void)
143{
144 show_stack(NULL, NULL);
145}
146EXPORT_SYMBOL(dump_stack);
147
148ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
149
150int register_die_notifier(struct notifier_block *nb)
151{
152 pr_debug("register_die_notifier: %p\n", nb);
153
154 return atomic_notifier_chain_register(&avr32_die_chain, nb);
155}
156EXPORT_SYMBOL(register_die_notifier);
157
158int unregister_die_notifier(struct notifier_block *nb)
159{
160 return atomic_notifier_chain_unregister(&avr32_die_chain, nb);
161}
162EXPORT_SYMBOL(unregister_die_notifier);
163
164static DEFINE_SPINLOCK(die_lock);
165
166void __die(const char *str, struct pt_regs *regs, unsigned long err,
167 const char *file, const char *func, unsigned long line)
168{
169 struct task_struct *tsk = current;
170 static int die_counter;
171
172 console_verbose();
173 spin_lock_irq(&die_lock);
174 bust_spinlocks(1);
175
176 printk(KERN_ALERT "%s", str);
177 if (file && func)
178 printk(" in %s:%s, line %ld", file, func, line);
179 printk("[#%d]:\n", ++die_counter);
180 print_modules();
181 show_regs(regs);
182 printk("Process %s (pid: %d, stack limit = 0x%p)\n",
183 tsk->comm, tsk->pid, tsk->thread_info + 1);
184
185 if (!user_mode(regs) || in_interrupt()) {
186 dump_mem("Stack: ", regs->sp,
187 THREAD_SIZE + (unsigned long)tsk->thread_info);
188 }
189
190 bust_spinlocks(0);
191 spin_unlock_irq(&die_lock);
192 do_exit(SIGSEGV);
193}
194
195void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
196 const char *file, const char *func, unsigned long line)
197{
198 if (!user_mode(regs))
199 __die(str, regs, err, file, func, line);
200}
201
202asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
203{
204#ifdef CONFIG_SUBARCH_AVR32B
205 /*
206 * The exception entry always saves RSR_EX. For NMI, this is
207 * wrong; it should be RSR_NMI
208 */
209 regs->sr = sysreg_read(RSR_NMI);
210#endif
211
212 printk("NMI taken!!!!\n");
213 die("NMI", regs, ecr);
214 BUG();
215}
216
217asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
218{
219 printk("Unable to handle critical exception %lu at pc = %08lx!\n",
220 ecr, regs->pc);
221 die("Oops", regs, ecr);
222 BUG();
223}
224
225asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
226{
227 siginfo_t info;
228
229 die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
230
231#ifdef DEBUG
232 if (ecr == ECR_ADDR_ALIGN_X)
233 pr_debug("Instruction Address Exception at pc = %08lx\n",
234 regs->pc);
235 else if (ecr == ECR_ADDR_ALIGN_R)
236 pr_debug("Data Address Exception (Read) at pc = %08lx\n",
237 regs->pc);
238 else if (ecr == ECR_ADDR_ALIGN_W)
239 pr_debug("Data Address Exception (Write) at pc = %08lx\n",
240 regs->pc);
241 else
242 BUG();
243
244 show_regs(regs);
245#endif
246
247 info.si_signo = SIGBUS;
248 info.si_errno = 0;
249 info.si_code = BUS_ADRALN;
250 info.si_addr = (void __user *)regs->pc;
251
252 force_sig_info(SIGBUS, &info, current);
253}
254
255/* This way of handling undefined instructions is stolen from ARM */
256static LIST_HEAD(undef_hook);
257static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED;
258
259void register_undef_hook(struct undef_hook *hook)
260{
261 spin_lock_irq(&undef_lock);
262 list_add(&hook->node, &undef_hook);
263 spin_unlock_irq(&undef_lock);
264}
265
266void unregister_undef_hook(struct undef_hook *hook)
267{
268 spin_lock_irq(&undef_lock);
269 list_del(&hook->node);
270 spin_unlock_irq(&undef_lock);
271}
272
273static int do_cop_absent(u32 insn)
274{
275 int cop_nr;
276 u32 cpucr;
277 if ( (insn & 0xfdf00000) == 0xf1900000 )
278 /* LDC0 */
279 cop_nr = 0;
280 else
281 cop_nr = (insn >> 13) & 0x7;
282
283 /* Try enabling the coprocessor */
284 cpucr = sysreg_read(CPUCR);
285 cpucr |= (1 << (24 + cop_nr));
286 sysreg_write(CPUCR, cpucr);
287
288 cpucr = sysreg_read(CPUCR);
289 if ( !(cpucr & (1 << (24 + cop_nr))) ){
290 printk("Coprocessor #%i not found!\n", cop_nr);
291 return -1;
292 }
293
294 return 0;
295}
296
297#ifdef CONFIG_BUG
298#ifdef CONFIG_DEBUG_BUGVERBOSE
299static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
300{
301 char *file;
302 u16 line;
303 char c;
304
305 if (__get_user(line, (u16 __user *)(regs->pc + 2)))
306 return;
307 if (__get_user(file, (char * __user *)(regs->pc + 4))
308 || (unsigned long)file < PAGE_OFFSET
309 || __get_user(c, file))
310 file = "<bad filename>";
311
312 printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
313}
314#else
315static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
316{
317
318}
319#endif
320#endif
321
322asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
323{
324 u32 insn;
325 struct undef_hook *hook;
326 siginfo_t info;
327 void __user *pc;
328
329 if (!user_mode(regs))
330 goto kernel_trap;
331
332 local_irq_enable();
333
334 pc = (void __user *)instruction_pointer(regs);
335 if (__get_user(insn, (u32 __user *)pc))
336 goto invalid_area;
337
338 if (ecr == ECR_COPROC_ABSENT) {
339 if (do_cop_absent(insn) == 0)
340 return;
341 }
342
343 spin_lock_irq(&undef_lock);
344 list_for_each_entry(hook, &undef_hook, node) {
345 if ((insn & hook->insn_mask) == hook->insn_val) {
346 if (hook->fn(regs, insn) == 0) {
347 spin_unlock_irq(&undef_lock);
348 return;
349 }
350 }
351 }
352 spin_unlock_irq(&undef_lock);
353
354invalid_area:
355
356#ifdef DEBUG
357 printk("Illegal instruction at pc = %08lx\n", regs->pc);
358 if (regs->pc < TASK_SIZE) {
359 unsigned long ptbr, pgd, pte, *p;
360
361 ptbr = sysreg_read(PTBR);
362 p = (unsigned long *)ptbr;
363 pgd = p[regs->pc >> 22];
364 p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
365 pte = p[(regs->pc >> 12) & 0x3ff];
366 printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
367 }
368#endif
369
370 info.si_signo = SIGILL;
371 info.si_errno = 0;
372 info.si_addr = (void __user *)regs->pc;
373 switch (ecr) {
374 case ECR_ILLEGAL_OPCODE:
375 case ECR_UNIMPL_INSTRUCTION:
376 info.si_code = ILL_ILLOPC;
377 break;
378 case ECR_PRIVILEGE_VIOLATION:
379 info.si_code = ILL_PRVOPC;
380 break;
381 case ECR_COPROC_ABSENT:
382 info.si_code = ILL_COPROC;
383 break;
384 default:
385 BUG();
386 }
387
388 force_sig_info(SIGILL, &info, current);
389 return;
390
391kernel_trap:
392#ifdef CONFIG_BUG
393 if (__kernel_text_address(instruction_pointer(regs))) {
394 insn = *(u16 *)instruction_pointer(regs);
395 if (insn == AVR32_BUG_OPCODE) {
396 do_bug_verbose(regs, insn);
397 die("Kernel BUG", regs, 0);
398 return;
399 }
400 }
401#endif
402
403 die("Oops: Illegal instruction in kernel code", regs, ecr);
404}
405
406asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
407{
408 siginfo_t info;
409
410 printk("Floating-point exception at pc = %08lx\n", regs->pc);
411
412 /* We have no FPU... */
413 info.si_signo = SIGILL;
414 info.si_errno = 0;
415 info.si_addr = (void __user *)regs->pc;
416 info.si_code = ILL_COPROC;
417
418 force_sig_info(SIGILL, &info, current);
419}
420
421
422void __init trap_init(void)
423{
424
425}
This page took 0.048142 seconds and 5 git commands to generate.