Merge commit 'origin/master' into next
[pandora-kernel.git] / arch / blackfin / kernel / traps.c
index aa76dfb..d279552 100644 (file)
@@ -27,6 +27,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#include <linux/bug.h>
 #include <linux/uaccess.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -238,6 +239,11 @@ asmlinkage void double_fault_c(struct pt_regs *fp)
 
 }
 
+static int kernel_mode_regs(struct pt_regs *regs)
+{
+       return regs->ipend & 0xffc0;
+}
+
 asmlinkage void trap_c(struct pt_regs *fp)
 {
 #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
@@ -246,6 +252,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
 #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
        unsigned int cpu = smp_processor_id();
 #endif
+       const char *strerror = NULL;
        int sig = 0;
        siginfo_t info;
        unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
@@ -259,27 +266,10 @@ asmlinkage void trap_c(struct pt_regs *fp)
         * double faults if the stack has become corrupt
         */
 
-       /* If the fault was caused by a kernel thread, or interrupt handler
-        * we will kernel panic, so the system reboots.
-        * If KGDB is enabled, don't set this for kernel breakpoints
-       */
-
-       /* TODO: check to see if we are in some sort of deferred HWERR
-        * that we should be able to recover from, not kernel panic
-        */
-       if ((bfin_read_IPEND() & 0xFFC0) && (trapnr != VEC_STEP)
-#ifdef CONFIG_KGDB
-               && (trapnr != VEC_EXCPT02)
+#ifndef CONFIG_KGDB
+       /* IPEND is skipped if KGDB isn't enabled (see entry code) */
+       fp->ipend = bfin_read_IPEND();
 #endif
-       ){
-               console_verbose();
-               oops_in_progress = 1;
-       } else if (current) {
-               if (current->mm == NULL) {
-                       console_verbose();
-                       oops_in_progress = 1;
-               }
-       }
 
        /* trap_c() will be called for exceptions. During exceptions
         * processing, the pc value should be set with retx value.
@@ -307,15 +297,15 @@ asmlinkage void trap_c(struct pt_regs *fp)
                sig = SIGTRAP;
                CHK_DEBUGGER_TRAP_MAYBE();
                /* Check if this is a breakpoint in kernel space */
-               if (fp->ipend & 0xffc0)
-                       return;
+               if (kernel_mode_regs(fp))
+                       goto traps_done;
                else
                        break;
        /* 0x03 - User Defined, userspace stack overflow */
        case VEC_EXCPT03:
                info.si_code = SEGV_STACKFLOW;
                sig = SIGSEGV;
-               verbose_printk(KERN_NOTICE EXC_0x03(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x03(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x02 - KGDB initial connection and break signal trap */
@@ -324,7 +314,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
                info.si_code = TRAP_ILLTRAP;
                sig = SIGTRAP;
                CHK_DEBUGGER_TRAP();
-               return;
+               goto traps_done;
 #endif
        /* 0x04 - User Defined */
        /* 0x05 - User Defined */
@@ -344,7 +334,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
        case VEC_EXCPT04 ... VEC_EXCPT15:
                info.si_code = ILL_ILLPARAOP;
                sig = SIGILL;
-               verbose_printk(KERN_NOTICE EXC_0x04(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x04(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x10 HW Single step, handled here */
@@ -353,15 +343,15 @@ asmlinkage void trap_c(struct pt_regs *fp)
                sig = SIGTRAP;
                CHK_DEBUGGER_TRAP_MAYBE();
                /* Check if this is a single step in kernel space */
-               if (fp->ipend & 0xffc0)
-                       return;
+               if (kernel_mode_regs(fp))
+                       goto traps_done;
                else
                        break;
        /* 0x11 - Trace Buffer Full, handled here */
        case VEC_OVFLOW:
                info.si_code = TRAP_TRACEFLOW;
                sig = SIGTRAP;
-               verbose_printk(KERN_NOTICE EXC_0x11(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x11(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x12 - Reserved, Caught by default */
@@ -381,37 +371,54 @@ asmlinkage void trap_c(struct pt_regs *fp)
        /* 0x20 - Reserved, Caught by default */
        /* 0x21 - Undefined Instruction, handled here */
        case VEC_UNDEF_I:
+#ifdef CONFIG_BUG
+               if (kernel_mode_regs(fp)) {
+                       switch (report_bug(fp->pc, fp)) {
+                       case BUG_TRAP_TYPE_NONE:
+                               break;
+                       case BUG_TRAP_TYPE_WARN:
+                               dump_bfin_trace_buffer();
+                               fp->pc += 2;
+                               goto traps_done;
+                       case BUG_TRAP_TYPE_BUG:
+                               /* call to panic() will dump trace, and it is
+                                * off at this point, so it won't be clobbered
+                                */
+                               panic("BUG()");
+                       }
+               }
+#endif
                info.si_code = ILL_ILLOPC;
                sig = SIGILL;
-               verbose_printk(KERN_NOTICE EXC_0x21(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x21(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x22 - Illegal Instruction Combination, handled here */
        case VEC_ILGAL_I:
                info.si_code = ILL_ILLPARAOP;
                sig = SIGILL;
-               verbose_printk(KERN_NOTICE EXC_0x22(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x22(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x23 - Data CPLB protection violation, handled here */
        case VEC_CPLB_VL:
                info.si_code = ILL_CPLB_VI;
                sig = SIGBUS;
-               verbose_printk(KERN_NOTICE EXC_0x23(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x23(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x24 - Data access misaligned, handled here */
        case VEC_MISALI_D:
                info.si_code = BUS_ADRALN;
                sig = SIGBUS;
-               verbose_printk(KERN_NOTICE EXC_0x24(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x24(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x25 - Unrecoverable Event, handled here */
        case VEC_UNCOV:
                info.si_code = ILL_ILLEXCPT;
                sig = SIGILL;
-               verbose_printk(KERN_NOTICE EXC_0x25(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x25(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x26 - Data CPLB Miss, normal case is handled in _cplb_hdr,
@@ -419,7 +426,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
        case VEC_CPLB_M:
                info.si_code = BUS_ADRALN;
                sig = SIGBUS;
-               verbose_printk(KERN_NOTICE EXC_0x26(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x26(KERN_NOTICE);
                break;
        /* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero, handled here */
        case VEC_CPLB_MHIT:
@@ -427,10 +434,10 @@ asmlinkage void trap_c(struct pt_regs *fp)
                sig = SIGSEGV;
 #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
                if (cpu_pda[cpu].dcplb_fault_addr < FIXED_CODE_START)
-                       verbose_printk(KERN_NOTICE "NULL pointer access\n");
+                       strerror = KERN_NOTICE "NULL pointer access\n";
                else
 #endif
-                       verbose_printk(KERN_NOTICE EXC_0x27(KERN_NOTICE));
+                       strerror = KERN_NOTICE EXC_0x27(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x28 - Emulation Watchpoint, handled here */
@@ -440,8 +447,8 @@ asmlinkage void trap_c(struct pt_regs *fp)
                pr_debug(EXC_0x28(KERN_DEBUG));
                CHK_DEBUGGER_TRAP_MAYBE();
                /* Check if this is a watchpoint in kernel space */
-               if (fp->ipend & 0xffc0)
-                       return;
+               if (kernel_mode_regs(fp))
+                       goto traps_done;
                else
                        break;
 #ifdef CONFIG_BF535
@@ -449,7 +456,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
        case VEC_ISTRU_VL:      /* ADSP-BF535 only (MH) */
                info.si_code = BUS_OPFETCH;
                sig = SIGBUS;
-               verbose_printk(KERN_NOTICE "BF535: VEC_ISTRU_VL\n");
+               strerror = KERN_NOTICE "BF535: VEC_ISTRU_VL\n";
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
 #else
@@ -459,21 +466,21 @@ asmlinkage void trap_c(struct pt_regs *fp)
        case VEC_MISALI_I:
                info.si_code = BUS_ADRALN;
                sig = SIGBUS;
-               verbose_printk(KERN_NOTICE EXC_0x2A(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x2A(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x2B - Instruction CPLB protection violation, handled here */
        case VEC_CPLB_I_VL:
                info.si_code = ILL_CPLB_VI;
                sig = SIGBUS;
-               verbose_printk(KERN_NOTICE EXC_0x2B(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x2B(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x2C - Instruction CPLB miss, handled in _cplb_hdr */
        case VEC_CPLB_I_M:
                info.si_code = ILL_CPLB_MISS;
                sig = SIGBUS;
-               verbose_printk(KERN_NOTICE EXC_0x2C(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x2C(KERN_NOTICE);
                break;
        /* 0x2D - Instruction CPLB Multiple Hits, handled here */
        case VEC_CPLB_I_MHIT:
@@ -481,17 +488,17 @@ asmlinkage void trap_c(struct pt_regs *fp)
                sig = SIGSEGV;
 #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
                if (cpu_pda[cpu].icplb_fault_addr < FIXED_CODE_START)
-                       verbose_printk(KERN_NOTICE "Jump to NULL address\n");
+                       strerror = KERN_NOTICE "Jump to NULL address\n";
                else
 #endif
-                       verbose_printk(KERN_NOTICE EXC_0x2D(KERN_NOTICE));
+                       strerror = KERN_NOTICE EXC_0x2D(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x2E - Illegal use of Supervisor Resource, handled here */
        case VEC_ILL_RES:
                info.si_code = ILL_PRVOPC;
                sig = SIGILL;
-               verbose_printk(KERN_NOTICE EXC_0x2E(KERN_NOTICE));
+               strerror = KERN_NOTICE EXC_0x2E(KERN_NOTICE);
                CHK_DEBUGGER_TRAP_MAYBE();
                break;
        /* 0x2F - Reserved, Caught by default */
@@ -519,17 +526,17 @@ asmlinkage void trap_c(struct pt_regs *fp)
                case (SEQSTAT_HWERRCAUSE_SYSTEM_MMR):
                        info.si_code = BUS_ADRALN;
                        sig = SIGBUS;
-                       verbose_printk(KERN_NOTICE HWC_x2(KERN_NOTICE));
+                       strerror = KERN_NOTICE HWC_x2(KERN_NOTICE);
                        break;
                /* External Memory Addressing Error */
                case (SEQSTAT_HWERRCAUSE_EXTERN_ADDR):
                        info.si_code = BUS_ADRERR;
                        sig = SIGBUS;
-                       verbose_printk(KERN_NOTICE HWC_x3(KERN_NOTICE));
+                       strerror = KERN_NOTICE HWC_x3(KERN_NOTICE);
                        break;
                /* Performance Monitor Overflow */
                case (SEQSTAT_HWERRCAUSE_PERF_FLOW):
-                       verbose_printk(KERN_NOTICE HWC_x12(KERN_NOTICE));
+                       strerror = KERN_NOTICE HWC_x12(KERN_NOTICE);
                        break;
                /* RAISE 5 instruction */
                case (SEQSTAT_HWERRCAUSE_RAISE_5):
@@ -546,7 +553,6 @@ asmlinkage void trap_c(struct pt_regs *fp)
         * if we get here we hit a reserved one, so panic
         */
        default:
-               oops_in_progress = 1;
                info.si_code = ILL_ILLPARAOP;
                sig = SIGILL;
                verbose_printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n",
@@ -557,6 +563,16 @@ asmlinkage void trap_c(struct pt_regs *fp)
 
        BUG_ON(sig == 0);
 
+       /* If the fault was caused by a kernel thread, or interrupt handler
+        * we will kernel panic, so the system reboots.
+        */
+       if (kernel_mode_regs(fp) || (current && !current->mm)) {
+               console_verbose();
+               oops_in_progress = 1;
+               if (strerror)
+                       verbose_printk(strerror);
+       }
+
        if (sig != SIGTRAP) {
                dump_bfin_process(fp);
                dump_bfin_mem(fp);
@@ -606,8 +622,8 @@ asmlinkage void trap_c(struct pt_regs *fp)
        if (ANOMALY_05000461 && trapnr == VEC_HWERR && !access_ok(VERIFY_READ, fp->pc, 8))
                fp->pc = SAFE_USER_INSTRUCTION;
 
+ traps_done:
        trace_buffer_restore(j);
-       return;
 }
 
 /* Typical exception handling routines */
@@ -792,6 +808,18 @@ void dump_bfin_trace_buffer(void)
 }
 EXPORT_SYMBOL(dump_bfin_trace_buffer);
 
+#ifdef CONFIG_BUG
+int is_valid_bugaddr(unsigned long addr)
+{
+       unsigned short opcode;
+
+       if (!get_instruction(&opcode, (unsigned short *)addr))
+               return 0;
+
+       return opcode == BFIN_BUG_OPCODE;
+}
+#endif
+
 /*
  * Checks to see if the address pointed to is either a
  * 16-bit CALL instruction, or a 32-bit CALL instruction