[AVR32] Clean up exception handling code
authorHaavard Skinnemoen <hskinnemoen@atmel.com>
Tue, 13 Mar 2007 16:59:11 +0000 (17:59 +0100)
committerHaavard Skinnemoen <hskinnemoen@atmel.com>
Fri, 27 Apr 2007 11:44:13 +0000 (13:44 +0200)
  * 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>
arch/avr32/Kconfig
arch/avr32/kernel/module.c
arch/avr32/kernel/process.c
arch/avr32/kernel/traps.c
arch/avr32/kernel/vmlinux.lds.c
arch/avr32/mm/fault.c
include/asm-avr32/bug.h
include/asm-avr32/processor.h
include/asm-avr32/system.h

index 257b836..7a1eb44 100644 (file)
@@ -68,6 +68,11 @@ config GENERIC_CALIBRATE_DELAY
        bool
        default y
 
+config GENERIC_BUG
+       bool
+       default y
+       depends on BUG
+
 source "init/Kconfig"
 
 menu "System Type and features"
index b599eae..1167fe9 100644 (file)
  * published by the Free Software Foundation.
  */
 
-#include <linux/moduleloader.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
+#include <linux/bug.h>
 #include <linux/elf.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleloader.h>
 #include <linux/vmalloc.h>
 
 void *module_alloc(unsigned long size)
@@ -315,10 +316,10 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
        vfree(module->arch.syminfo);
        module->arch.syminfo = NULL;
 
-       return 0;
+       return module_bug_finalize(hdr, sechdrs, module);
 }
 
 void module_arch_cleanup(struct module *module)
 {
-
+       module_bug_cleanup(module);
 }
index 4f8d2d4..4e4181e 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/fs.h>
 #include <linux/ptrace.h>
 #include <linux/reboot.h>
+#include <linux/uaccess.h>
 #include <linux/unistd.h>
 
 #include <asm/sysreg.h>
@@ -115,39 +116,178 @@ void release_thread(struct task_struct *dead_task)
        /* do nothing */
 }
 
+static void dump_mem(const char *str, const char *log_lvl,
+                    unsigned long bottom, unsigned long top)
+{
+       unsigned long p;
+       int i;
+
+       printk("%s%s(0x%08lx to 0x%08lx)\n", log_lvl, str, bottom, top);
+
+       for (p = bottom & ~31; p < top; ) {
+               printk("%s%04lx: ", log_lvl, 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 void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
+                              struct pt_regs *regs, const char *log_lvl)
+{
+       unsigned long lr, fp;
+       struct thread_info *tinfo;
+
+       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.
+        */
+       tinfo = task_thread_info(tsk);
+       printk("%sCall trace:\n", log_lvl);
+       while (valid_stack_ptr(tinfo, fp)) {
+               unsigned long new_fp;
+
+               lr = *(unsigned long *)fp;
+#ifdef CONFIG_KALLSYMS
+               printk("%s [<%08lx>] ", log_lvl, lr);
+#else
+               printk(" [<%08lx>] ", lr);
+#endif
+               print_symbol("%s\n", lr);
+
+               new_fp = *(unsigned long *)(fp + 4);
+               if (new_fp <= fp)
+                       break;
+               fp = new_fp;
+       }
+       printk("\n");
+}
+#else
+static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
+                              struct pt_regs *regs, const char *log_lvl)
+{
+       unsigned long addr;
+
+       printk("%sCall trace:\n", log_lvl);
+
+       while (!kstack_end(sp)) {
+               addr = *sp++;
+               if (kernel_text_address(addr)) {
+#ifdef CONFIG_KALLSYMS
+                       printk("%s [<%08lx>] ", log_lvl, addr);
+#else
+                       printk(" [<%08lx>] ", addr);
+#endif
+                       print_symbol("%s\n", addr);
+               }
+       }
+       printk("\n");
+}
+#endif
+
+void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
+                       struct pt_regs *regs, const char *log_lvl)
+{
+       struct thread_info *tinfo;
+
+       if (sp == 0) {
+               if (tsk)
+                       sp = tsk->thread.cpu_context.ksp;
+               else
+                       sp = (unsigned long)&tinfo;
+       }
+       if (!tsk)
+               tsk = current;
+
+       tinfo = task_thread_info(tsk);
+
+       if (valid_stack_ptr(tinfo, sp)) {
+               dump_mem("Stack: ", log_lvl, sp,
+                        THREAD_SIZE + (unsigned long)tinfo);
+               show_trace_log_lvl(tsk, (unsigned long *)sp, regs, log_lvl);
+       }
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *stack)
+{
+       show_stack_log_lvl(tsk, (unsigned long)stack, NULL, "");
+}
+
+void dump_stack(void)
+{
+       unsigned long stack;
+
+       show_trace_log_lvl(current, &stack, NULL, "");
+}
+EXPORT_SYMBOL(dump_stack);
+
 static const char *cpu_modes[] = {
        "Application", "Supervisor", "Interrupt level 0", "Interrupt level 1",
        "Interrupt level 2", "Interrupt level 3", "Exception", "NMI"
 };
 
-void show_regs(struct pt_regs *regs)
+void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl)
 {
        unsigned long sp = regs->sp;
        unsigned long lr = regs->lr;
        unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT;
 
-       if (!user_mode(regs))
+       if (!user_mode(regs)) {
                sp = (unsigned long)regs + FRAME_SIZE_FULL;
 
-       print_symbol("PC is at %s\n", instruction_pointer(regs));
-       print_symbol("LR is at %s\n", lr);
-       printk("pc : [<%08lx>]    lr : [<%08lx>]    %s\n"
-              "sp : %08lx  r12: %08lx  r11: %08lx\n",
-              instruction_pointer(regs),
-              lr, print_tainted(), sp, regs->r12, regs->r11);
-       printk("r10: %08lx  r9 : %08lx  r8 : %08lx\n",
-              regs->r10, regs->r9, regs->r8);
-       printk("r7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n",
-              regs->r7, regs->r6, regs->r5, regs->r4);
-       printk("r3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n",
-              regs->r3, regs->r2, regs->r1, regs->r0);
-       printk("Flags: %c%c%c%c%c\n",
+               printk("%s", log_lvl);
+               print_symbol("PC is at %s\n", instruction_pointer(regs));
+               printk("%s", log_lvl);
+               print_symbol("LR is at %s\n", lr);
+       }
+
+       printk("%spc : [<%08lx>]    lr : [<%08lx>]    %s\n"
+              "%ssp : %08lx  r12: %08lx  r11: %08lx\n",
+              log_lvl, instruction_pointer(regs), lr, print_tainted(),
+              log_lvl, sp, regs->r12, regs->r11);
+       printk("%sr10: %08lx  r9 : %08lx  r8 : %08lx\n",
+              log_lvl, regs->r10, regs->r9, regs->r8);
+       printk("%sr7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n",
+              log_lvl, regs->r7, regs->r6, regs->r5, regs->r4);
+       printk("%sr3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n",
+              log_lvl, regs->r3, regs->r2, regs->r1, regs->r0);
+       printk("%sFlags: %c%c%c%c%c\n", log_lvl,
               regs->sr & SR_Q ? 'Q' : 'q',
               regs->sr & SR_V ? 'V' : 'v',
               regs->sr & SR_N ? 'N' : 'n',
               regs->sr & SR_Z ? 'Z' : 'z',
               regs->sr & SR_C ? 'C' : 'c');
-       printk("Mode bits: %c%c%c%c%c%c%c%c%c\n",
+       printk("%sMode bits: %c%c%c%c%c%c%c%c%c\n", log_lvl,
               regs->sr & SR_H ? 'H' : 'h',
               regs->sr & SR_R ? 'R' : 'r',
               regs->sr & SR_J ? 'J' : 'j',
@@ -157,9 +297,21 @@ void show_regs(struct pt_regs *regs)
               regs->sr & SR_I1M ? '1' : '.',
               regs->sr & SR_I0M ? '0' : '.',
               regs->sr & SR_GM ? 'G' : 'g');
-       printk("CPU Mode: %s\n", cpu_modes[mode]);
+       printk("%sCPU Mode: %s\n", log_lvl, cpu_modes[mode]);
+       printk("%sProcess: %s [%d] (task: %p thread: %p)\n",
+              log_lvl, current->comm, current->pid, current,
+              task_thread_info(current));
+}
+
+void show_regs(struct pt_regs *regs)
+{
+       unsigned long sp = regs->sp;
+
+       if (!user_mode(regs))
+               sp = (unsigned long)regs + FRAME_SIZE_FULL;
 
-       show_trace(NULL, (unsigned long *)sp, regs);
+       show_regs_log_lvl(regs, "");
+       show_trace_log_lvl(current, (unsigned long *)sp, regs, "");
 }
 EXPORT_SYMBOL(show_regs);
 
index adc01a1..4f0382d 100644 (file)
  * 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);
 }
 
 
index ef13b7c..7ad20cf 100644 (file)
@@ -26,6 +26,12 @@ SECTIONS
                        _sinittext = .;
                        *(.text.reset)
                        *(.init.text)
+                       /*
+                        * .exit.text is discarded at runtime, not
+                        * link time, to deal with references from
+                        * __bug_table
+                        */
+                       *(.exit.text)
                        _einittext = .;
                . = ALIGN(4);
                __tagtable_begin = .;
@@ -86,6 +92,8 @@ SECTIONS
                __stop___ex_table = .;
        }
 
+       BUG_TABLE
+
        RODATA
 
        . = ALIGN(8192);
@@ -126,7 +134,6 @@ SECTIONS
         * thrown away, as cleanup code is never called unless it's a module.
         */
        /DISCARD/               : {
-               *(.exit.text)
                *(.exit.data)
                *(.exitcall.exit)
        }
index 6785572..146ebdb 100644 (file)
 #include <asm/kdebug.h>
 #include <asm/mmu_context.h>
 #include <asm/sysreg.h>
-#include <asm/uaccess.h>
 #include <asm/tlb.h>
-
-#ifdef DEBUG
-static void dump_code(unsigned long pc)
-{
-       char *p = (char *)pc;
-       char val;
-       int i;
-
-
-       printk(KERN_DEBUG "Code:");
-       for (i = 0; i < 16; i++) {
-               if (__get_user(val, p + i))
-                       break;
-               printk(" %02x", val);
-       }
-       printk("\n");
-}
-#endif
+#include <asm/uaccess.h>
 
 #ifdef CONFIG_KPROBES
 ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
@@ -68,17 +50,19 @@ static inline int notify_page_fault(enum die_val val, struct pt_regs *regs,
 }
 #endif
 
+int exception_trace = 1;
+
 /*
  * This routine handles page faults. It determines the address and the
  * problem, and then passes it off to one of the appropriate routines.
  *
  * ecr is the Exception Cause Register. Possible values are:
- *   5:  Page not found (instruction access)
  *   6:  Protection fault (instruction access)
- *   12: Page not found (read access)
- *   13: Page not found (write access)
- *   14: Protection fault (read access)
- *   15: Protection fault (write access)
+ *   15: Protection fault (read access)
+ *   16: Protection fault (write access)
+ *   20: Page not found (instruction access)
+ *   24: Page not found (read access)
+ *   28: Page not found (write access)
  */
 asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
 {
@@ -88,7 +72,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
        const struct exception_table_entry *fixup;
        unsigned long address;
        unsigned long page;
-       int writeaccess = 0;
+       int writeaccess;
+       long signr;
+       int code;
 
        if (notify_page_fault(DIE_PAGE_FAULT, regs,
                              ecr, SIGSEGV) == NOTIFY_STOP)
@@ -99,6 +85,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
        tsk = current;
        mm = tsk->mm;
 
+       signr = SIGSEGV;
+       code = SEGV_MAPERR;
+
        /*
         * If we're in an interrupt or have no user context, we must
         * not take the fault...
@@ -125,7 +114,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
         * can handle it...
         */
 good_area:
-       //pr_debug("good area: vm_flags = 0x%lx\n", vma->vm_flags);
+       code = SEGV_ACCERR;
+       writeaccess = 0;
+
        switch (ecr) {
        case ECR_PROTECTION_X:
        case ECR_TLB_MISS_X:
@@ -176,46 +167,24 @@ survive:
         * map. Fix it, but check if it's kernel or user first...
         */
 bad_area:
-       pr_debug("Bad area [%s:%u]: addr %08lx, ecr %lu\n",
-                tsk->comm, tsk->pid, address, ecr);
-
        up_read(&mm->mmap_sem);
 
        if (user_mode(regs)) {
-               /* Hmm...we have to pass address and ecr somehow... */
-               /* tsk->thread.address = address;
-                  tsk->thread.error_code = ecr; */
-#ifdef DEBUG
-               show_regs(regs);
-               dump_code(regs->pc);
-
-               page = sysreg_read(PTBR);
-               printk("ptbr = %08lx", page);
-               if (page) {
-                       page = ((unsigned long *)page)[address >> 22];
-                       printk(" pgd = %08lx", page);
-                       if (page & _PAGE_PRESENT) {
-                               page &= PAGE_MASK;
-                               address &= 0x003ff000;
-                               page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
-                               printk(" pte = %08lx\n", page);
-                       }
-               }
-#endif
-               pr_debug("Sending SIGSEGV to PID %d...\n",
-                       tsk->pid);
-               force_sig(SIGSEGV, tsk);
+               if (exception_trace)
+                       printk("%s%s[%d]: segfault at %08lx pc %08lx "
+                              "sp %08lx ecr %lu\n",
+                              is_init(tsk) ? KERN_EMERG : KERN_INFO,
+                              tsk->comm, tsk->pid, address, regs->pc,
+                              regs->sp, ecr);
+               _exception(SIGSEGV, regs, code, address);
                return;
        }
 
 no_context:
-       pr_debug("No context\n");
-
        /* Are we prepared to handle this kernel fault? */
        fixup = search_exception_tables(regs->pc);
        if (fixup) {
                regs->pc = fixup->fixup;
-               pr_debug("Found fixup at %08lx\n", fixup->fixup);
                return;
        }
 
@@ -230,7 +199,6 @@ no_context:
                printk(KERN_ALERT
                       "Unable to handle kernel paging request");
        printk(" at virtual address %08lx\n", address);
-       printk(KERN_ALERT "pc = %08lx\n", regs->pc);
 
        page = sysreg_read(PTBR);
        printk(KERN_ALERT "ptbr = %08lx", page);
@@ -241,20 +209,20 @@ no_context:
                        page &= PAGE_MASK;
                        address &= 0x003ff000;
                        page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
-                       printk(" pte = %08lx\n", page);
+                       printk(" pte = %08lx", page);
                }
        }
-       die("\nOops", regs, ecr);
-       do_exit(SIGKILL);
+       printk("\n");
+       die("Kernel access of bad area", regs, signr);
+       return;
 
        /*
         * We ran out of memory, or some other thing happened to us
         * that made us unable to handle the page fault gracefully.
         */
 out_of_memory:
-       printk("Out of memory\n");
        up_read(&mm->mmap_sem);
-       if (current->pid == 1) {
+       if (is_init(current)) {
                yield();
                down_read(&mm->mmap_sem);
                goto survive;
@@ -267,21 +235,20 @@ out_of_memory:
 do_sigbus:
        up_read(&mm->mmap_sem);
 
-       /*
-        * Send a sigbus, regardless of whether we were in kernel or
-        * user mode.
-        */
-       /* address, error_code, trap_no, ... */
-#ifdef DEBUG
-       show_regs(regs);
-       dump_code(regs->pc);
-#endif
-       pr_debug("Sending SIGBUS to PID %d...\n", tsk->pid);
-       force_sig(SIGBUS, tsk);
-
        /* Kernel mode? Handle exceptions or die */
+       signr = SIGBUS;
+       code = BUS_ADRERR;
        if (!user_mode(regs))
                goto no_context;
+
+       if (exception_trace)
+               printk("%s%s[%d]: bus error at %08lx pc %08lx "
+                      "sp %08lx ecr %lu\n",
+                      is_init(tsk) ? KERN_EMERG : KERN_INFO,
+                      tsk->comm, tsk->pid, address, regs->pc,
+                      regs->sp, ecr);
+
+       _exception(SIGBUS, regs, BUS_ADRERR, address);
 }
 
 asmlinkage void do_bus_error(unsigned long addr, int write_access,
@@ -292,8 +259,7 @@ asmlinkage void do_bus_error(unsigned long addr, int write_access,
               addr, write_access ? "write" : "read");
        printk(KERN_INFO "DTLB dump:\n");
        dump_dtlb();
-       die("Bus Error", regs, write_access);
-       do_exit(SIGKILL);
+       die("Bus Error", regs, SIGKILL);
 }
 
 /*
index 521766b..afdcd79 100644 (file)
 
 #ifdef CONFIG_DEBUG_BUGVERBOSE
 
-#define BUG()                                                          \
-       do {                                                            \
-               asm volatile(".hword    %0\n\t"                         \
-                            ".hword    %1\n\t"                         \
-                            ".long     %2"                             \
-                            :                                          \
-                            : "n"(AVR32_BUG_OPCODE),                   \
-                              "i"(__LINE__), "X"(__FILE__));           \
-       } while (0)
+#define _BUG_OR_WARN(flags)                                            \
+       asm volatile(                                                   \
+               "1:     .hword  %0\n"                                   \
+               "       .section __bug_table,\"a\",@progbits\n"         \
+               "2:     .long   1b\n"                                   \
+               "       .long   %1\n"                                   \
+               "       .short  %2\n"                                   \
+               "       .short  %3\n"                                   \
+               "       .org    2b + %4\n"                              \
+               "       .previous"                                      \
+               :                                                       \
+               : "i"(AVR32_BUG_OPCODE), "i"(__FILE__),                 \
+                 "i"(__LINE__), "i"(flags),                            \
+                 "i"(sizeof(struct bug_entry)))
 
 #else
 
+#define _BUG_OR_WARN(flags)                                            \
+       asm volatile(                                                   \
+               "1:     .hword  %0\n"                                   \
+               "       .section __bug_table,\"a\",@progbits\n"         \
+               "2:     .long   1b\n"                                   \
+               "       .short  %1\n"                                   \
+               "       .org    2b + %2\n"                              \
+               "       .previous"                                      \
+               :                                                       \
+               : "i"(AVR32_BUG_OPCODE), "i"(flags),                    \
+                 "i"(sizeof(struct bug_entry)))
+
+#endif /* CONFIG_DEBUG_BUGVERBOSE */
+
 #define BUG()                                                          \
        do {                                                            \
-               asm volatile(".hword    %0\n\t"                         \
-                            : : "n"(AVR32_BUG_OPCODE));                \
+               _BUG_OR_WARN(0);                                        \
+               for (;;);                                               \
        } while (0)
 
-#endif /* CONFIG_DEBUG_BUGVERBOSE */
+#define WARN_ON(condition)                                                     \
+       ({                                                              \
+               typeof(condition) __ret_warn_on = (condition);          \
+               if (unlikely(__ret_warn_on))                            \
+                       _BUG_OR_WARN(BUGFLAG_WARNING);                  \
+               unlikely(__ret_warn_on);                                \
+       })
 
 #define HAVE_ARCH_BUG
+#define HAVE_ARCH_WARN_ON
 
 #endif /* CONFIG_BUG */
 
index cbb89cc..6a64833 100644 (file)
@@ -134,10 +134,10 @@ extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 #define thread_saved_pc(tsk)    ((tsk)->thread.cpu_context.pc)
 
 struct pt_regs;
-void show_trace(struct task_struct *task, unsigned long *stack,
-               struct pt_regs *regs);
-
 extern unsigned long get_wchan(struct task_struct *p);
+extern void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl);
+extern void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
+                              struct pt_regs *regs, const char *log_lvl);
 
 #define KSTK_EIP(tsk)  ((tsk)->thread.cpu_context.pc)
 #define KSTK_ESP(tsk)  ((tsk)->thread.cpu_context.ksp)
index ac59605..a8236ba 100644 (file)
@@ -9,6 +9,7 @@
 #define __ASM_AVR32_SYSTEM_H
 
 #include <linux/compiler.h>
+#include <linux/linkage.h>
 #include <linux/types.h>
 
 #include <asm/ptrace.h>
@@ -140,15 +141,9 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
                                   sizeof(*(ptr))))
 
 struct pt_regs;
-extern void __die(const char *, struct pt_regs *, unsigned long,
-                 const char *, const char *, unsigned long);
-extern void __die_if_kernel(const char *, struct pt_regs *, unsigned long,
-                           const char *, const char *, unsigned long);
-
-#define die(msg, regs, err)                                    \
-       __die(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
-#define die_if_kernel(msg, regs, err)                                  \
-       __die_if_kernel(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
+void NORET_TYPE die(const char *str, struct pt_regs *regs, long err);
+void _exception(long signr, struct pt_regs *regs, int code,
+               unsigned long addr);
 
 #define arch_align_stack(x)    (x)