Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[sfrench/cifs-2.6.git] / arch / blackfin / kernel / traps.c
index 3909f5b3553679ac1b6215b95e31f856aa34b2dd..8823e9ade5849b02968442e158990e3c02ade6c6 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/kallsyms.h>
+#include <linux/fs.h>
 #include <asm/traps.h>
 #include <asm/cacheflush.h>
 #include <asm/blackfin.h>
@@ -50,10 +51,9 @@ void __init trap_init(void)
        CSYNC();
 }
 
-asmlinkage void trap_c(struct pt_regs *fp);
-
 int kstack_depth_to_print = 48;
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
 static int printk_address(unsigned long address)
 {
        struct vm_list_struct *vml;
@@ -130,17 +130,29 @@ static int printk_address(unsigned long address)
        /* we were unable to find this address anywhere */
        return printk("[<0x%p>]", (void *)address);
 }
+#endif
+
+asmlinkage void double_fault_c(struct pt_regs *fp)
+{
+       printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n");
+       dump_bfin_regs(fp, (void *)fp->retx);
+       panic("Double Fault - unrecoverable event\n");
+
+}
 
 asmlinkage void trap_c(struct pt_regs *fp)
 {
-       int j, sig = 0;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+       int j;
+#endif
+       int sig = 0;
        siginfo_t info;
        unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
 
 #ifdef CONFIG_KGDB
 # define CHK_DEBUGGER_TRAP() \
        do { \
-               CHK_DEBUGGER(trapnr, sig, info.si_code, fp); \
+               CHK_DEBUGGER(trapnr, sig, info.si_code, fp); \
        } while (0)
 # define CHK_DEBUGGER_TRAP_MAYBE() \
        do { \
@@ -390,10 +402,6 @@ asmlinkage void trap_c(struct pt_regs *fp)
                break;
        }
 
-       info.si_signo = sig;
-       info.si_errno = 0;
-       info.si_addr = (void *)fp->pc;
-       force_sig_info(sig, &info, current);
        if (sig != 0 && sig != SIGTRAP) {
                unsigned long stack;
                dump_bfin_regs(fp, (void *)fp->retx);
@@ -402,6 +410,10 @@ asmlinkage void trap_c(struct pt_regs *fp)
                if (current->mm == NULL)
                        panic("Kernel exception");
        }
+       info.si_signo = sig;
+       info.si_errno = 0;
+       info.si_addr = (void *)fp->pc;
+       force_sig_info(sig, &info, current);
 
        /* if the address that we are about to return to is not valid, set it
         * to a valid address, if we have a current application or panic
@@ -428,24 +440,56 @@ asmlinkage void trap_c(struct pt_regs *fp)
 
 /* Typical exception handling routines */
 
+#define EXPAND_LEN ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 256 - 1)
+
 void dump_bfin_trace_buffer(void)
 {
-       int tflags;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+       int tflags, i = 0;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+       int j, index;
+#endif
+
        trace_buffer_save(tflags);
 
+       printk(KERN_EMERG "Hardware Trace:\n");
+
        if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) {
-               int i;
-               printk(KERN_EMERG "Hardware Trace:\n");
-               for (i = 0; bfin_read_TBUFSTAT() & TBUFCNT; i++) {
-                       printk(KERN_EMERG "%2i Target : ", i);
+               for (; bfin_read_TBUFSTAT() & TBUFCNT; i++) {
+                       printk(KERN_EMERG "%4i Target : ", i);
                        printk_address((unsigned long)bfin_read_TBUF());
-                       printk("\n" KERN_EMERG "   Source : ");
+                       printk("\n" KERN_EMERG "     Source : ");
                        printk_address((unsigned long)bfin_read_TBUF());
                        printk("\n");
                }
        }
 
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND
+       if (trace_buff_offset)
+               index = trace_buff_offset/4 - 1;
+       else
+               index = EXPAND_LEN;
+
+       j = (1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 128;
+       while (j) {
+               printk(KERN_EMERG "%4i Target : ", i);
+               printk_address(software_trace_buff[index]);
+               index -= 1;
+               if (index < 0 )
+                       index = EXPAND_LEN;
+               printk("\n" KERN_EMERG "     Source : ");
+               printk_address(software_trace_buff[index]);
+               index -= 1;
+               if (index < 0)
+                       index = EXPAND_LEN;
+               printk("\n");
+               j--;
+               i++;
+       }
+#endif
+
        trace_buffer_restore(tflags);
+#endif
 }
 EXPORT_SYMBOL(dump_bfin_trace_buffer);
 
@@ -509,7 +553,9 @@ void show_stack(struct task_struct *task, unsigned long *stack)
 void dump_stack(void)
 {
        unsigned long stack;
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
        int tflags;
+#endif
        trace_buffer_save(tflags);
        dump_bfin_trace_buffer();
        show_stack(current, &stack);
@@ -558,8 +604,7 @@ void dump_bfin_regs(struct pt_regs *fp, void *retaddr)
                unsigned short x = 0;
                for (; i < ((unsigned int)retaddr & 0xFFFFFFF0) + 32; i += 2) {
                        if (!(i & 0xF))
-                               printk(KERN_EMERG "\n" KERN_EMERG
-                                       "0x%08x: ", i);
+                               printk("\n" KERN_EMERG "0x%08x: ", i);
 
                        if (get_user(x, (unsigned short *)i))
                                break;
@@ -654,6 +699,42 @@ asmlinkage int sys_bfin_spinlock(int *spinlock)
        return ret;
 }
 
+int bfin_request_exception(unsigned int exception, void (*handler)(void))
+{
+       void (*curr_handler)(void);
+
+       if (exception > 0x3F)
+               return -EINVAL;
+
+       curr_handler = ex_table[exception];
+
+       if (curr_handler != ex_replaceable)
+               return -EBUSY;
+
+       ex_table[exception] = handler;
+
+       return 0;
+}
+EXPORT_SYMBOL(bfin_request_exception);
+
+int bfin_free_exception(unsigned int exception, void (*handler)(void))
+{
+       void (*curr_handler)(void);
+
+       if (exception > 0x3F)
+               return -EINVAL;
+
+       curr_handler = ex_table[exception];
+
+       if (curr_handler != handler)
+               return -EBUSY;
+
+       ex_table[exception] = ex_replaceable;
+
+       return 0;
+}
+EXPORT_SYMBOL(bfin_free_exception);
+
 void panic_cplb_error(int cplb_panic, struct pt_regs *fp)
 {
        switch (cplb_panic) {