summaryrefslogtreecommitdiffstats
path: root/arch/avr32/kernel/traps.c
diff options
context:
space:
mode:
authorHaavard Skinnemoen <hskinnemoen@atmel.com>2007-03-13 17:59:11 +0100
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2007-04-27 13:44:13 +0200
commit623b0355d5b1f9c6d05005b649a2f3a7b9fd7816 (patch)
tree43ef35d4f6e83a49c1fb72df4b538271b650c054 /arch/avr32/kernel/traps.c
parent3b328c98093702c584692bffabd440800b383d73 (diff)
downloadlinux-623b0355d5b1f9c6d05005b649a2f3a7b9fd7816.tar.gz
linux-623b0355d5b1f9c6d05005b649a2f3a7b9fd7816.tar.xz
[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 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#undef DEBUG
-#include <linux/sched.h>
+
+#include <linux/bug.h>
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/kallsyms.h>
+#include <linux/module.h>
#include <linux/notifier.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
-#include <asm/traps.h>
-#include <asm/sysreg.h>
#include <asm/addrspace.h>
-#include <asm/ocd.h>
#include <asm/mmu_context.h>
-#include <asm/uaccess.h>
-
-static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
-{
- unsigned long p;
- int i;
-
- printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
-
- for (p = bottom & ~31; p < top; ) {
- printk("%04lx: ", p & 0xffff);
-
- for (i = 0; i < 8; i++, p += 4) {
- unsigned int val;
-
- if (p < bottom || p >= top)
- printk(" ");
- else {
- if (__get_user(val, (unsigned int __user *)p)) {
- printk("\n");
- goto out;
- }
- printk("%08x ", val);
- }
- }
- printk("\n");
- }
-
-out:
- return;
-}
-
-static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
-{
- return (p > (unsigned long)tinfo)
- && (p < (unsigned long)tinfo + THREAD_SIZE - 3);
-}
-
-#ifdef CONFIG_FRAME_POINTER
-static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
- struct pt_regs *regs)
-{
- unsigned long lr, fp;
- struct thread_info *tinfo;
-
- tinfo = (struct thread_info *)
- ((unsigned long)sp & ~(THREAD_SIZE - 1));
-
- if (regs)
- fp = regs->r7;
- else if (tsk == current)
- asm("mov %0, r7" : "=r"(fp));
- else
- fp = tsk->thread.cpu_context.r7;
-
- /*
- * Walk the stack as long as the frame pointer (a) is within
- * the kernel stack of the task, and (b) it doesn't move
- * downwards.
- */
- while (valid_stack_ptr(tinfo, fp)) {
- unsigned long new_fp;
-
- lr = *(unsigned long *)fp;
- printk(" [<%08lx>] ", lr);
- print_symbol("%s\n", lr);
-
- new_fp = *(unsigned long *)(fp + 4);
- if (new_fp <= fp)
- break;
- fp = new_fp;
- }
- printk("\n");
-}
-#else
-static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
- struct pt_regs *regs)
-{
- unsigned long addr;
-
- while (!kstack_end(sp)) {
- addr = *sp++;
- if (kernel_text_address(addr)) {
- printk(" [<%08lx>] ", addr);
- print_symbol("%s\n", addr);
- }
- }
-}
-#endif
-
-void show_trace(struct task_struct *tsk, unsigned long *sp,
- struct pt_regs *regs)
-{
- if (regs &&
- (((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
- ((regs->sr & MODE_MASK) == MODE_USER)))
- return;
-
- printk ("Call trace:");
-#ifdef CONFIG_KALLSYMS
- printk("\n");
-#endif
-
- __show_trace(tsk, sp, regs);
- printk("\n");
-}
-
-void show_stack(struct task_struct *tsk, unsigned long *sp)
-{
- unsigned long stack;
-
- if (!tsk)
- tsk = current;
- if (sp == 0) {
- if (tsk == current) {
- register unsigned long *real_sp __asm__("sp");
- sp = real_sp;
- } else {
- sp = (unsigned long *)tsk->thread.cpu_context.ksp;
- }
- }
-
- stack = (unsigned long)sp;
- dump_mem("Stack: ", stack,
- THREAD_SIZE + (unsigned long)tsk->thread_info);
- show_trace(tsk, sp, NULL);
-}
-
-void dump_stack(void)
-{
- show_stack(NULL, NULL);
-}
-EXPORT_SYMBOL(dump_stack);
+#include <asm/ocd.h>
+#include <asm/sysreg.h>
+#include <asm/traps.h>
ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
int register_die_notifier(struct notifier_block *nb)
{
- pr_debug("register_die_notifier: %p\n", nb);
-
return atomic_notifier_chain_register(&avr32_die_chain, nb);
}
EXPORT_SYMBOL(register_die_notifier);
@@ -169,93 +36,103 @@ EXPORT_SYMBOL(unregister_die_notifier);
static DEFINE_SPINLOCK(die_lock);
-void __die(const char *str, struct pt_regs *regs, unsigned long err,
- const char *file, const char *func, unsigned long line)
+void NORET_TYPE die(const char *str, struct pt_regs *regs, long err)
{
- struct task_struct *tsk = current;
static int die_counter;
console_verbose();
spin_lock_irq(&die_lock);
bust_spinlocks(1);
- printk(KERN_ALERT "%s", str);
- if (file && func)
- printk(" in %s:%s, line %ld", file, func, line);
- printk("[#%d]:\n", ++die_counter);
- print_modules();
- show_regs(regs);
- printk("Process %s (pid: %d, stack limit = 0x%p)\n",
- tsk->comm, tsk->pid, tsk->thread_info + 1);
-
- if (!user_mode(regs) || in_interrupt()) {
- dump_mem("Stack: ", regs->sp,
- THREAD_SIZE + (unsigned long)tsk->thread_info);
+ printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n" KERN_EMERG,
+ str, err, ++die_counter);
+#ifdef CONFIG_PREEMPT
+ printk("PREEMPT ");
+#endif
+#ifdef CONFIG_FRAME_POINTER
+ printk("FRAME_POINTER ");
+#endif
+ if (current_cpu_data.features & AVR32_FEATURE_OCD) {
+ unsigned long did = __mfdr(DBGREG_DID);
+ printk("chip: 0x%03lx:0x%04lx rev %lu\n",
+ (did >> 1) & 0x7ff,
+ (did >> 12) & 0x7fff,
+ (did >> 28) & 0xf);
+ } else {
+ printk("cpu: arch %u r%u / core %u r%u\n",
+ current_cpu_data.arch_type,
+ current_cpu_data.arch_revision,
+ current_cpu_data.cpu_type,
+ current_cpu_data.cpu_revision);
}
+ print_modules();
+ show_regs_log_lvl(regs, KERN_EMERG);
+ show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG);
bust_spinlocks(0);
spin_unlock_irq(&die_lock);
- do_exit(SIGSEGV);
+
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+
+ if (panic_on_oops)
+ panic("Fatal exception");
+
+ do_exit(err);
}
-void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
- const char *file, const char *func, unsigned long line)
+void _exception(long signr, struct pt_regs *regs, int code,
+ unsigned long addr)
{
+ siginfo_t info;
+
if (!user_mode(regs))
- __die(str, regs, err, file, func, line);
-}
+ die("Unhandled exception in kernel mode", regs, signr);
+
+ memset(&info, 0, sizeof(info));
+ info.si_signo = signr;
+ info.si_code = code;
+ info.si_addr = (void __user *)addr;
+ force_sig_info(signr, &info, current);
-asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
-{
-#ifdef CONFIG_SUBARCH_AVR32B
/*
- * The exception entry always saves RSR_EX. For NMI, this is
- * wrong; it should be RSR_NMI
+ * Init gets no signals that it doesn't have a handler for.
+ * That's all very well, but if it has caused a synchronous
+ * exception and we ignore the resulting signal, it will just
+ * generate the same exception over and over again and we get
+ * nowhere. Better to kill it and let the kernel panic.
*/
- regs->sr = sysreg_read(RSR_NMI);
-#endif
+ if (is_init(current)) {
+ __sighandler_t handler;
+
+ spin_lock_irq(&current->sighand->siglock);
+ handler = current->sighand->action[signr-1].sa.sa_handler;
+ spin_unlock_irq(&current->sighand->siglock);
+ if (handler == SIG_DFL) {
+ /* init has generated a synchronous exception
+ and it doesn't have a handler for the signal */
+ printk(KERN_CRIT "init has generated signal %ld "
+ "but has no handler for it\n", signr);
+ do_exit(signr);
+ }
+ }
+}
- printk("NMI taken!!!!\n");
- die("NMI", regs, ecr);
- BUG();
+asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
+{
+ printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n");
+ show_regs_log_lvl(regs, KERN_ALERT);
+ show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT);
}
asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
{
- printk("Unable to handle critical exception %lu at pc = %08lx!\n",
- ecr, regs->pc);
- die("Oops", regs, ecr);
- BUG();
+ die("Critical exception", regs, SIGKILL);
}
asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
{
- siginfo_t info;
-
- die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
-
-#ifdef DEBUG
- if (ecr == ECR_ADDR_ALIGN_X)
- pr_debug("Instruction Address Exception at pc = %08lx\n",
- regs->pc);
- else if (ecr == ECR_ADDR_ALIGN_R)
- pr_debug("Data Address Exception (Read) at pc = %08lx\n",
- regs->pc);
- else if (ecr == ECR_ADDR_ALIGN_W)
- pr_debug("Data Address Exception (Write) at pc = %08lx\n",
- regs->pc);
- else
- BUG();
-
- show_regs(regs);
-#endif
-
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = BUS_ADRALN;
- info.si_addr = (void __user *)regs->pc;
-
- force_sig_info(SIGBUS, &info, current);
+ _exception(SIGBUS, regs, BUS_ADRALN, regs->pc);
}
/* This way of handling undefined instructions is stolen from ARM */
@@ -280,7 +157,8 @@ static int do_cop_absent(u32 insn)
{
int cop_nr;
u32 cpucr;
- if ( (insn & 0xfdf00000) == 0xf1900000 )
+
+ if ((insn & 0xfdf00000) == 0xf1900000)
/* LDC0 */
cop_nr = 0;
else
@@ -292,136 +170,91 @@ static int do_cop_absent(u32 insn)
sysreg_write(CPUCR, cpucr);
cpucr = sysreg_read(CPUCR);
- if ( !(cpucr & (1 << (24 + cop_nr))) ){
- printk("Coprocessor #%i not found!\n", cop_nr);
- return -1;
- }
+ if (!(cpucr & (1 << (24 + cop_nr))))
+ return -ENODEV;
return 0;
}
-#ifdef CONFIG_BUG
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
-{
- char *file;
- u16 line;
- char c;
-
- if (__get_user(line, (u16 __user *)(regs->pc + 2)))
- return;
- if (__get_user(file, (char * __user *)(regs->pc + 4))
- || (unsigned long)file < PAGE_OFFSET
- || __get_user(c, file))
- file = "<bad filename>";
-
- printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
-}
-#else
-static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
+int is_valid_bugaddr(unsigned long pc)
{
+ unsigned short opcode;
+
+ if (pc < PAGE_OFFSET)
+ return 0;
+ if (probe_kernel_address((u16 *)pc, opcode))
+ return 0;
+ return opcode == AVR32_BUG_OPCODE;
}
-#endif
-#endif
asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
{
u32 insn;
struct undef_hook *hook;
- siginfo_t info;
void __user *pc;
+ long code;
- if (!user_mode(regs))
- goto kernel_trap;
+ if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) {
+ enum bug_trap_type type;
+
+ type = report_bug(regs->pc);
+ switch (type) {
+ case BUG_TRAP_TYPE_NONE:
+ break;
+ case BUG_TRAP_TYPE_WARN:
+ regs->pc += 2;
+ return;
+ case BUG_TRAP_TYPE_BUG:
+ die("Kernel BUG", regs, SIGKILL);
+ }
+ }
local_irq_enable();
- pc = (void __user *)instruction_pointer(regs);
- if (__get_user(insn, (u32 __user *)pc))
- goto invalid_area;
+ if (user_mode(regs)) {
+ pc = (void __user *)instruction_pointer(regs);
+ if (get_user(insn, (u32 __user *)pc))
+ goto invalid_area;
- if (ecr == ECR_COPROC_ABSENT) {
- if (do_cop_absent(insn) == 0)
+ if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn))
return;
- }
- spin_lock_irq(&undef_lock);
- list_for_each_entry(hook, &undef_hook, node) {
- if ((insn & hook->insn_mask) == hook->insn_val) {
- if (hook->fn(regs, insn) == 0) {
- spin_unlock_irq(&undef_lock);
- return;
+ spin_lock_irq(&undef_lock);
+ list_for_each_entry(hook, &undef_hook, node) {
+ if ((insn & hook->insn_mask) == hook->insn_val) {
+ if (hook->fn(regs, insn) == 0) {
+ spin_unlock_irq(&undef_lock);
+ return;
+ }
}
}
+ spin_unlock_irq(&undef_lock);
}
- spin_unlock_irq(&undef_lock);
-
-invalid_area:
-#ifdef DEBUG
- printk("Illegal instruction at pc = %08lx\n", regs->pc);
- if (regs->pc < TASK_SIZE) {
- unsigned long ptbr, pgd, pte, *p;
-
- ptbr = sysreg_read(PTBR);
- p = (unsigned long *)ptbr;
- pgd = p[regs->pc >> 22];
- p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
- pte = p[(regs->pc >> 12) & 0x3ff];
- printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
- }
-#endif
-
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_addr = (void __user *)regs->pc;
switch (ecr) {
- case ECR_ILLEGAL_OPCODE:
- case ECR_UNIMPL_INSTRUCTION:
- info.si_code = ILL_ILLOPC;
- break;
case ECR_PRIVILEGE_VIOLATION:
- info.si_code = ILL_PRVOPC;
+ code = ILL_PRVOPC;
break;
case ECR_COPROC_ABSENT:
- info.si_code = ILL_COPROC;
+ code = ILL_COPROC;
break;
default:
- BUG();
+ code = ILL_ILLOPC;
+ break;
}
- force_sig_info(SIGILL, &info, current);
+ _exception(SIGILL, regs, code, regs->pc);
return;
-kernel_trap:
-#ifdef CONFIG_BUG
- if (__kernel_text_address(instruction_pointer(regs))) {
- insn = *(u16 *)instruction_pointer(regs);
- if (insn == AVR32_BUG_OPCODE) {
- do_bug_verbose(regs, insn);
- die("Kernel BUG", regs, 0);
- return;
- }
- }
-#endif
-
- die("Oops: Illegal instruction in kernel code", regs, ecr);
+invalid_area:
+ _exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc);
}
asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
{
- siginfo_t info;
-
- printk("Floating-point exception at pc = %08lx\n", regs->pc);
-
- /* We have no FPU... */
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_addr = (void __user *)regs->pc;
- info.si_code = ILL_COPROC;
-
- force_sig_info(SIGILL, &info, current);
+ /* We have no FPU yet */
+ _exception(SIGILL, regs, ILL_COPROC, regs->pc);
}