Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee13...
[pandora-kernel.git] / arch / avr32 / kernel / traps.c
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
22 static 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
48 out:
49         return;
50 }
51
52 #ifdef CONFIG_FRAME_POINTER
53 static 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
89 static 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
104 void 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
121 void 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
142 void dump_stack(void)
143 {
144         show_stack(NULL, NULL);
145 }
146 EXPORT_SYMBOL(dump_stack);
147
148 ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
149
150 int 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 }
156 EXPORT_SYMBOL(register_die_notifier);
157
158 int unregister_die_notifier(struct notifier_block *nb)
159 {
160         return atomic_notifier_chain_unregister(&avr32_die_chain, nb);
161 }
162 EXPORT_SYMBOL(unregister_die_notifier);
163
164 static DEFINE_SPINLOCK(die_lock);
165
166 void __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
195 void __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
202 asmlinkage 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
217 asmlinkage 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
225 asmlinkage 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 */
256 static LIST_HEAD(undef_hook);
257 static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED;
258
259 void 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
266 void 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
273 static 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
299 static 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
315 static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
316 {
317
318 }
319 #endif
320 #endif
321
322 asmlinkage 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
354 invalid_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
391 kernel_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
406 asmlinkage 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
422 void __init trap_init(void)
423 {
424
425 }