2 * Copyright (C) 2004-2006 Atmel Corporation
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.
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>
15 #include <asm/traps.h>
16 #include <asm/sysreg.h>
17 #include <asm/addrspace.h>
19 #include <asm/mmu_context.h>
20 #include <asm/uaccess.h>
22 static void dump_mem(const char *str
, unsigned long bottom
, unsigned long top
)
27 printk("%s(0x%08lx to 0x%08lx)\n", str
, bottom
, top
);
29 for (p
= bottom
& ~31; p
< top
; ) {
30 printk("%04lx: ", p
& 0xffff);
32 for (i
= 0; i
< 8; i
++, p
+= 4) {
35 if (p
< bottom
|| p
>= top
)
38 if (__get_user(val
, (unsigned int __user
*)p
)) {
52 static inline int valid_stack_ptr(struct thread_info
*tinfo
, unsigned long p
)
54 return (p
> (unsigned long)tinfo
)
55 && (p
< (unsigned long)tinfo
+ THREAD_SIZE
- 3);
58 #ifdef CONFIG_FRAME_POINTER
59 static inline void __show_trace(struct task_struct
*tsk
, unsigned long *sp
,
63 struct thread_info
*tinfo
;
65 tinfo
= (struct thread_info
*)
66 ((unsigned long)sp
& ~(THREAD_SIZE
- 1));
70 else if (tsk
== current
)
71 asm("mov %0, r7" : "=r"(fp
));
73 fp
= tsk
->thread
.cpu_context
.r7
;
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
80 while (valid_stack_ptr(tinfo
, fp
)) {
83 lr
= *(unsigned long *)fp
;
84 printk(" [<%08lx>] ", lr
);
85 print_symbol("%s\n", lr
);
87 new_fp
= *(unsigned long *)(fp
+ 4);
95 static inline void __show_trace(struct task_struct
*tsk
, unsigned long *sp
,
100 while (!kstack_end(sp
)) {
102 if (kernel_text_address(addr
)) {
103 printk(" [<%08lx>] ", addr
);
104 print_symbol("%s\n", addr
);
110 void show_trace(struct task_struct
*tsk
, unsigned long *sp
,
111 struct pt_regs
*regs
)
114 (((regs
->sr
& MODE_MASK
) == MODE_EXCEPTION
) ||
115 ((regs
->sr
& MODE_MASK
) == MODE_USER
)))
118 printk ("Call trace:");
119 #ifdef CONFIG_KALLSYMS
123 __show_trace(tsk
, sp
, regs
);
127 void show_stack(struct task_struct
*tsk
, unsigned long *sp
)
134 if (tsk
== current
) {
135 register unsigned long *real_sp
__asm__("sp");
138 sp
= (unsigned long *)tsk
->thread
.cpu_context
.ksp
;
142 stack
= (unsigned long)sp
;
143 dump_mem("Stack: ", stack
,
144 THREAD_SIZE
+ (unsigned long)tsk
->thread_info
);
145 show_trace(tsk
, sp
, NULL
);
148 void dump_stack(void)
150 show_stack(NULL
, NULL
);
152 EXPORT_SYMBOL(dump_stack
);
154 ATOMIC_NOTIFIER_HEAD(avr32_die_chain
);
156 int register_die_notifier(struct notifier_block
*nb
)
158 pr_debug("register_die_notifier: %p\n", nb
);
160 return atomic_notifier_chain_register(&avr32_die_chain
, nb
);
162 EXPORT_SYMBOL(register_die_notifier
);
164 int unregister_die_notifier(struct notifier_block
*nb
)
166 return atomic_notifier_chain_unregister(&avr32_die_chain
, nb
);
168 EXPORT_SYMBOL(unregister_die_notifier
);
170 static DEFINE_SPINLOCK(die_lock
);
172 void __die(const char *str
, struct pt_regs
*regs
, unsigned long err
,
173 const char *file
, const char *func
, unsigned long line
)
175 struct task_struct
*tsk
= current
;
176 static int die_counter
;
179 spin_lock_irq(&die_lock
);
182 printk(KERN_ALERT
"%s", str
);
184 printk(" in %s:%s, line %ld", file
, func
, line
);
185 printk("[#%d]:\n", ++die_counter
);
188 printk("Process %s (pid: %d, stack limit = 0x%p)\n",
189 tsk
->comm
, tsk
->pid
, tsk
->thread_info
+ 1);
191 if (!user_mode(regs
) || in_interrupt()) {
192 dump_mem("Stack: ", regs
->sp
,
193 THREAD_SIZE
+ (unsigned long)tsk
->thread_info
);
197 spin_unlock_irq(&die_lock
);
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
)
204 if (!user_mode(regs
))
205 __die(str
, regs
, err
, file
, func
, line
);
208 asmlinkage
void do_nmi(unsigned long ecr
, struct pt_regs
*regs
)
210 #ifdef CONFIG_SUBARCH_AVR32B
212 * The exception entry always saves RSR_EX. For NMI, this is
213 * wrong; it should be RSR_NMI
215 regs
->sr
= sysreg_read(RSR_NMI
);
218 printk("NMI taken!!!!\n");
219 die("NMI", regs
, ecr
);
223 asmlinkage
void do_critical_exception(unsigned long ecr
, struct pt_regs
*regs
)
225 printk("Unable to handle critical exception %lu at pc = %08lx!\n",
227 die("Oops", regs
, ecr
);
231 asmlinkage
void do_address_exception(unsigned long ecr
, struct pt_regs
*regs
)
235 die_if_kernel("Oops: Address exception in kernel mode", regs
, ecr
);
238 if (ecr
== ECR_ADDR_ALIGN_X
)
239 pr_debug("Instruction Address Exception at pc = %08lx\n",
241 else if (ecr
== ECR_ADDR_ALIGN_R
)
242 pr_debug("Data Address Exception (Read) at pc = %08lx\n",
244 else if (ecr
== ECR_ADDR_ALIGN_W
)
245 pr_debug("Data Address Exception (Write) at pc = %08lx\n",
253 info
.si_signo
= SIGBUS
;
255 info
.si_code
= BUS_ADRALN
;
256 info
.si_addr
= (void __user
*)regs
->pc
;
258 force_sig_info(SIGBUS
, &info
, current
);
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
;
265 void register_undef_hook(struct undef_hook
*hook
)
267 spin_lock_irq(&undef_lock
);
268 list_add(&hook
->node
, &undef_hook
);
269 spin_unlock_irq(&undef_lock
);
272 void unregister_undef_hook(struct undef_hook
*hook
)
274 spin_lock_irq(&undef_lock
);
275 list_del(&hook
->node
);
276 spin_unlock_irq(&undef_lock
);
279 static int do_cop_absent(u32 insn
)
283 if ( (insn
& 0xfdf00000) == 0xf1900000 )
287 cop_nr
= (insn
>> 13) & 0x7;
289 /* Try enabling the coprocessor */
290 cpucr
= sysreg_read(CPUCR
);
291 cpucr
|= (1 << (24 + cop_nr
));
292 sysreg_write(CPUCR
, cpucr
);
294 cpucr
= sysreg_read(CPUCR
);
295 if ( !(cpucr
& (1 << (24 + cop_nr
))) ){
296 printk("Coprocessor #%i not found!\n", cop_nr
);
304 #ifdef CONFIG_DEBUG_BUGVERBOSE
305 static inline void do_bug_verbose(struct pt_regs
*regs
, u32 insn
)
311 if (__get_user(line
, (u16 __user
*)(regs
->pc
+ 2)))
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>";
318 printk(KERN_ALERT
"kernel BUG at %s:%d!\n", file
, line
);
321 static inline void do_bug_verbose(struct pt_regs
*regs
, u32 insn
)
328 asmlinkage
void do_illegal_opcode(unsigned long ecr
, struct pt_regs
*regs
)
331 struct undef_hook
*hook
;
335 if (!user_mode(regs
))
340 pc
= (void __user
*)instruction_pointer(regs
);
341 if (__get_user(insn
, (u32 __user
*)pc
))
344 if (ecr
== ECR_COPROC_ABSENT
) {
345 if (do_cop_absent(insn
) == 0)
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
);
358 spin_unlock_irq(&undef_lock
);
363 printk("Illegal instruction at pc = %08lx\n", regs
->pc
);
364 if (regs
->pc
< TASK_SIZE
) {
365 unsigned long ptbr
, pgd
, pte
, *p
;
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
);
376 info
.si_signo
= SIGILL
;
378 info
.si_addr
= (void __user
*)regs
->pc
;
380 case ECR_ILLEGAL_OPCODE
:
381 case ECR_UNIMPL_INSTRUCTION
:
382 info
.si_code
= ILL_ILLOPC
;
384 case ECR_PRIVILEGE_VIOLATION
:
385 info
.si_code
= ILL_PRVOPC
;
387 case ECR_COPROC_ABSENT
:
388 info
.si_code
= ILL_COPROC
;
394 force_sig_info(SIGILL
, &info
, current
);
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);
409 die("Oops: Illegal instruction in kernel code", regs
, ecr
);
412 asmlinkage
void do_fpe(unsigned long ecr
, struct pt_regs
*regs
)
416 printk("Floating-point exception at pc = %08lx\n", regs
->pc
);
418 /* We have no FPU... */
419 info
.si_signo
= SIGILL
;
421 info
.si_addr
= (void __user
*)regs
->pc
;
422 info
.si_code
= ILL_COPROC
;
424 force_sig_info(SIGILL
, &info
, current
);
428 void __init
trap_init(void)