Pull ec into release branch
[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 static 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
59 static 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
95 static 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
110 void 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
127 void 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
148 void dump_stack(void)
149 {
150         show_stack(NULL, NULL);
151 }
152 EXPORT_SYMBOL(dump_stack);
153
154 ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
155
156 int register_die_notifier(struct notifier_block *nb)
157 {
158         pr_debug("register_die_notifier: %p\n", nb);
159
160         return atomic_notifier_chain_register(&avr32_die_chain, nb);
161 }
162 EXPORT_SYMBOL(register_die_notifier);
163
164 int unregister_die_notifier(struct notifier_block *nb)
165 {
166         return atomic_notifier_chain_unregister(&avr32_die_chain, nb);
167 }
168 EXPORT_SYMBOL(unregister_die_notifier);
169
170 static DEFINE_SPINLOCK(die_lock);
171
172 void __die(const char *str, struct pt_regs *regs, unsigned long err,
173            const char *file, const char *func, unsigned long line)
174 {
175         struct task_struct *tsk = current;
176         static int die_counter;
177
178         console_verbose();
179         spin_lock_irq(&die_lock);
180         bust_spinlocks(1);
181
182         printk(KERN_ALERT "%s", str);
183         if (file && func)
184                 printk(" in %s:%s, line %ld", file, func, line);
185         printk("[#%d]:\n", ++die_counter);
186         print_modules();
187         show_regs(regs);
188         printk("Process %s (pid: %d, stack limit = 0x%p)\n",
189                tsk->comm, tsk->pid, tsk->thread_info + 1);
190
191         if (!user_mode(regs) || in_interrupt()) {
192                 dump_mem("Stack: ", regs->sp,
193                          THREAD_SIZE + (unsigned long)tsk->thread_info);
194         }
195
196         bust_spinlocks(0);
197         spin_unlock_irq(&die_lock);
198         do_exit(SIGSEGV);
199 }
200
201 void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
202                      const char *file, const char *func, unsigned long line)
203 {
204         if (!user_mode(regs))
205                 __die(str, regs, err, file, func, line);
206 }
207
208 asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
209 {
210 #ifdef CONFIG_SUBARCH_AVR32B
211         /*
212          * The exception entry always saves RSR_EX. For NMI, this is
213          * wrong; it should be RSR_NMI
214          */
215         regs->sr = sysreg_read(RSR_NMI);
216 #endif
217
218         printk("NMI taken!!!!\n");
219         die("NMI", regs, ecr);
220         BUG();
221 }
222
223 asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
224 {
225         printk("Unable to handle critical exception %lu at pc = %08lx!\n",
226                ecr, regs->pc);
227         die("Oops", regs, ecr);
228         BUG();
229 }
230
231 asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
232 {
233         siginfo_t info;
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 }
260
261 /* This way of handling undefined instructions is stolen from ARM */
262 static LIST_HEAD(undef_hook);
263 static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED;
264
265 void register_undef_hook(struct undef_hook *hook)
266 {
267         spin_lock_irq(&undef_lock);
268         list_add(&hook->node, &undef_hook);
269         spin_unlock_irq(&undef_lock);
270 }
271
272 void unregister_undef_hook(struct undef_hook *hook)
273 {
274         spin_lock_irq(&undef_lock);
275         list_del(&hook->node);
276         spin_unlock_irq(&undef_lock);
277 }
278
279 static int do_cop_absent(u32 insn)
280 {
281         int cop_nr;
282         u32 cpucr;
283         if ( (insn & 0xfdf00000) == 0xf1900000 )
284                 /* LDC0 */
285                 cop_nr = 0;
286         else
287                 cop_nr = (insn >> 13) & 0x7;
288
289         /* Try enabling the coprocessor */
290         cpucr = sysreg_read(CPUCR);
291         cpucr |= (1 << (24 + cop_nr));
292         sysreg_write(CPUCR, cpucr);
293
294         cpucr = sysreg_read(CPUCR);
295         if ( !(cpucr & (1 << (24 + cop_nr))) ){
296                 printk("Coprocessor #%i not found!\n", cop_nr);
297                 return -1;
298         }
299
300         return 0;
301 }
302
303 #ifdef CONFIG_BUG
304 #ifdef CONFIG_DEBUG_BUGVERBOSE
305 static 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
321 static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
322 {
323
324 }
325 #endif
326 #endif
327
328 asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
329 {
330         u32 insn;
331         struct undef_hook *hook;
332         siginfo_t info;
333         void __user *pc;
334
335         if (!user_mode(regs))
336                 goto kernel_trap;
337
338         local_irq_enable();
339
340         pc = (void __user *)instruction_pointer(regs);
341         if (__get_user(insn, (u32 __user *)pc))
342                 goto invalid_area;
343
344         if (ecr == ECR_COPROC_ABSENT) {
345                 if (do_cop_absent(insn) == 0)
346                         return;
347         }
348
349         spin_lock_irq(&undef_lock);
350         list_for_each_entry(hook, &undef_hook, node) {
351                 if ((insn & hook->insn_mask) == hook->insn_val) {
352                         if (hook->fn(regs, insn) == 0) {
353                                 spin_unlock_irq(&undef_lock);
354                                 return;
355                         }
356                 }
357         }
358         spin_unlock_irq(&undef_lock);
359
360 invalid_area:
361
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) {
380         case ECR_ILLEGAL_OPCODE:
381         case ECR_UNIMPL_INSTRUCTION:
382                 info.si_code = ILL_ILLOPC;
383                 break;
384         case ECR_PRIVILEGE_VIOLATION:
385                 info.si_code = ILL_PRVOPC;
386                 break;
387         case ECR_COPROC_ABSENT:
388                 info.si_code = ILL_COPROC;
389                 break;
390         default:
391                 BUG();
392         }
393
394         force_sig_info(SIGILL, &info, current);
395         return;
396
397 kernel_trap:
398 #ifdef CONFIG_BUG
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 }
411
412 asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
413 {
414         siginfo_t info;
415
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 }
426
427
428 void __init trap_init(void)
429 {
430
431 }