Pull thermal into release branch
[linux-drm-fsl-dcu.git] / arch / avr32 / kernel / traps.c
index 7e803f4d7a12902972da2ce1325acd0bc43cafc2..86d107511dd4dc1290e765714a2b560432e85a17 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");
-       }
+#include <asm/ocd.h>
+#include <asm/sysreg.h>
+#include <asm/traps.h>
 
-out:
-       return;
-}
+static DEFINE_SPINLOCK(die_lock);
 
-#ifdef CONFIG_FRAME_POINTER
-static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
-                               struct pt_regs *regs)
+void NORET_TYPE die(const char *str, struct pt_regs *regs, long err)
 {
-       unsigned long __user *fp;
-       unsigned long __user *last_fp = NULL;
-
-       if (regs) {
-               fp = (unsigned long __user *)regs->r7;
-       } else if (tsk == current) {
-               register unsigned long __user *real_fp __asm__("r7");
-               fp = real_fp;
-       } else {
-               fp = (unsigned long __user *)tsk->thread.cpu_context.r7;
-       }
-
-       /*
-        * Walk the stack until (a) we get an exception, (b) the frame
-        * pointer becomes zero, or (c) the frame pointer gets stuck
-        * at the same value.
-        */
-       while (fp && fp != last_fp) {
-               unsigned long lr, new_fp = 0;
-
-               last_fp = fp;
-               if (__get_user(lr, fp))
-                       break;
-               if (fp && __get_user(new_fp, fp + 1))
-                       break;
-               fp = (unsigned long __user *)new_fp;
+       static int die_counter;
 
-               printk(" [<%08lx>] ", lr);
-               print_symbol("%s\n", lr);
-       }
-       printk("\n");
-}
-#else
-static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
-                               struct pt_regs *regs)
-{
-       unsigned long addr;
+       console_verbose();
+       spin_lock_irq(&die_lock);
+       bust_spinlocks(1);
 
-       while (!kstack_end(sp)) {
-               addr = *sp++;
-               if (kernel_text_address(addr)) {
-                       printk(" [<%08lx>] ", addr);
-                       print_symbol("%s\n", addr);
-               }
-       }
-}
+       printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n" KERN_EMERG,
+              str, err, ++die_counter);
+#ifdef CONFIG_PREEMPT
+       printk("PREEMPT ");
 #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");
+#ifdef CONFIG_FRAME_POINTER
+       printk("FRAME_POINTER ");
 #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;
-               }
+       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);
        }
 
-       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);
+       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);
 
-ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
+       if (in_interrupt())
+               panic("Fatal exception in interrupt");
 
-int register_die_notifier(struct notifier_block *nb)
-{
-       pr_debug("register_die_notifier: %p\n", nb);
+       if (panic_on_oops)
+               panic("Fatal exception");
 
-       return atomic_notifier_chain_register(&avr32_die_chain, nb);
+       do_exit(err);
 }
-EXPORT_SYMBOL(register_die_notifier);
 
-int unregister_die_notifier(struct notifier_block *nb)
+void _exception(long signr, struct pt_regs *regs, int code,
+               unsigned long addr)
 {
-       return atomic_notifier_chain_unregister(&avr32_die_chain, nb);
-}
-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)
-{
-       struct task_struct *tsk = current;
-       static int die_counter;
+       siginfo_t info;
 
-       console_verbose();
-       spin_lock_irq(&die_lock);
-       bust_spinlocks(1);
+       if (!user_mode(regs))
+               die("Unhandled exception in kernel mode", regs, signr);
 
-       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);
+       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);
 
-       if (!user_mode(regs) || in_interrupt()) {
-               dump_mem("Stack: ", regs->sp,
-                        THREAD_SIZE + (unsigned long)tsk->thread_info);
+       /*
+        * 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.
+        */
+       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);
+               }
        }
-
-       bust_spinlocks(0);
-       spin_unlock_irq(&die_lock);
-       do_exit(SIGSEGV);
-}
-
-void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
-                    const char *file, const char *func, unsigned long line)
-{
-       if (!user_mode(regs))
-               __die(str, regs, err, file, func, line);
 }
 
 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
-        */
-       regs->sr = sysreg_read(RSR_NMI);
-#endif
-
-       printk("NMI taken!!!!\n");
-       die("NMI", regs, ecr);
-       BUG();
+       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 */
 static LIST_HEAD(undef_hook);
-static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(undef_lock);
 
 void register_undef_hook(struct undef_hook *hook)
 {
@@ -274,7 +143,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
@@ -286,136 +156,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);
 }