Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-mmc
[sfrench/cifs-2.6.git] / arch / mips / kernel / traps.c
index a7564b08eb4da273c3252d64cd46f8daf00a45d9..e51d8fd9a15234ec4d3e5fabcafb22b24010f6ed 100644 (file)
@@ -11,7 +11,6 @@
  * Copyright (C) 2000, 01 MIPS Technologies, Inc.
  * Copyright (C) 2002, 2003, 2004, 2005  Maciej W. Rozycki
  */
-#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/module.h>
@@ -21,6 +20,7 @@
 #include <linux/spinlock.h>
 #include <linux/kallsyms.h>
 #include <linux/bootmem.h>
+#include <linux/interrupt.h>
 
 #include <asm/bootinfo.h>
 #include <asm/branch.h>
@@ -65,7 +65,7 @@ extern asmlinkage void handle_mcheck(void);
 extern asmlinkage void handle_reserved(void);
 
 extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
-       struct mips_fpu_soft_struct *ctx);
+       struct mips_fpu_struct *ctx);
 
 void (*board_be_init)(void);
 int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
@@ -73,28 +73,68 @@ void (*board_nmi_handler_setup)(void);
 void (*board_ejtag_handler_setup)(void);
 void (*board_bind_eic_interrupt)(int irq, int regset);
 
-/*
- * These constant is for searching for possible module text segments.
- * MODULE_RANGE is a guess of how much space is likely to be vmalloced.
- */
-#define MODULE_RANGE (8*1024*1024)
+
+static void show_raw_backtrace(unsigned long reg29)
+{
+       unsigned long *sp = (unsigned long *)reg29;
+       unsigned long addr;
+
+       printk("Call Trace:");
+#ifdef CONFIG_KALLSYMS
+       printk("\n");
+#endif
+       while (!kstack_end(sp)) {
+               addr = *sp++;
+               if (__kernel_text_address(addr))
+                       print_ip_sym(addr);
+       }
+       printk("\n");
+}
+
+#ifdef CONFIG_KALLSYMS
+static int raw_show_trace;
+static int __init set_raw_show_trace(char *str)
+{
+       raw_show_trace = 1;
+       return 1;
+}
+__setup("raw_show_trace", set_raw_show_trace);
+
+extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
+                                 unsigned long pc, unsigned long ra);
+
+static void show_backtrace(struct task_struct *task, struct pt_regs *regs)
+{
+       unsigned long sp = regs->regs[29];
+       unsigned long ra = regs->regs[31];
+       unsigned long pc = regs->cp0_epc;
+
+       if (raw_show_trace || !__kernel_text_address(pc)) {
+               show_raw_backtrace(sp);
+               return;
+       }
+       printk("Call Trace:\n");
+       do {
+               print_ip_sym(pc);
+               pc = unwind_stack(task, &sp, pc, ra);
+               ra = 0;
+       } while (pc);
+       printk("\n");
+}
+#else
+#define show_backtrace(task, r) show_raw_backtrace((r)->regs[29]);
+#endif
 
 /*
  * This routine abuses get_user()/put_user() to reference pointers
  * with at least a bit of error checking ...
  */
-void show_stack(struct task_struct *task, unsigned long *sp)
+static void show_stacktrace(struct task_struct *task, struct pt_regs *regs)
 {
        const int field = 2 * sizeof(unsigned long);
        long stackdata;
        int i;
-
-       if (!sp) {
-               if (task && task != current)
-                       sp = (unsigned long *) task->thread.reg29;
-               else
-                       sp = (unsigned long *) &sp;
-       }
+       unsigned long *sp = (unsigned long *)regs->regs[29];
 
        printk("Stack :");
        i = 0;
@@ -115,32 +155,48 @@ void show_stack(struct task_struct *task, unsigned long *sp)
                i++;
        }
        printk("\n");
+       show_backtrace(task, regs);
 }
 
-void show_trace(struct task_struct *task, unsigned long *stack)
+static __always_inline void prepare_frametrace(struct pt_regs *regs)
 {
-       const int field = 2 * sizeof(unsigned long);
-       unsigned long addr;
-
-       if (!stack) {
-               if (task && task != current)
-                       stack = (unsigned long *) task->thread.reg29;
-               else
-                       stack = (unsigned long *) &stack;
-       }
-
-       printk("Call Trace:");
-#ifdef CONFIG_KALLSYMS
-       printk("\n");
+       __asm__ __volatile__(
+               ".set push\n\t"
+               ".set noat\n\t"
+#ifdef CONFIG_64BIT
+               "1: dla $1, 1b\n\t"
+               "sd $1, %0\n\t"
+               "sd $29, %1\n\t"
+               "sd $31, %2\n\t"
+#else
+               "1: la $1, 1b\n\t"
+               "sw $1, %0\n\t"
+               "sw $29, %1\n\t"
+               "sw $31, %2\n\t"
 #endif
-       while (!kstack_end(stack)) {
-               addr = *stack++;
-               if (__kernel_text_address(addr)) {
-                       printk(" [<%0*lx>] ", field, addr);
-                       print_symbol("%s\n", addr);
+               ".set pop\n\t"
+               : "=m" (regs->cp0_epc),
+               "=m" (regs->regs[29]), "=m" (regs->regs[31])
+               : : "memory");
+}
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+       struct pt_regs regs;
+       if (sp) {
+               regs.regs[29] = (unsigned long)sp;
+               regs.regs[31] = 0;
+               regs.cp0_epc = 0;
+       } else {
+               if (task && task != current) {
+                       regs.regs[29] = task->thread.reg29;
+                       regs.regs[31] = 0;
+                       regs.cp0_epc = task->thread.reg31;
+               } else {
+                       prepare_frametrace(&regs);
                }
        }
-       printk("\n");
+       show_stacktrace(task, &regs);
 }
 
 /*
@@ -148,9 +204,15 @@ void show_trace(struct task_struct *task, unsigned long *stack)
  */
 void dump_stack(void)
 {
-       unsigned long stack;
+       struct pt_regs regs;
 
-       show_trace(current, &stack);
+       /*
+        * Remove any garbage that may be in regs (specially func
+        * addresses) to avoid show_raw_backtrace() to report them
+        */
+       memset(&regs, 0, sizeof(regs));
+       prepare_frametrace(&regs);
+       show_backtrace(current, &regs);
 }
 
 EXPORT_SYMBOL(dump_stack);
@@ -269,8 +331,7 @@ void show_registers(struct pt_regs *regs)
        print_modules();
        printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
                current->comm, current->pid, current_thread_info(), current);
-       show_stack(current, (long *) regs->regs[29]);
-       show_trace(current, (long *) regs->regs[29]);
+       show_stacktrace(current, regs);
        show_code((unsigned int *) regs->cp0_epc);
        printk("\n");
 }
@@ -293,6 +354,16 @@ NORET_TYPE void ATTRIB_NORET die(const char * str, struct pt_regs * regs)
        printk("%s[#%d]:\n", str, ++die_counter);
        show_registers(regs);
        spin_unlock_irq(&die_lock);
+
+       if (in_interrupt())
+               panic("Fatal exception in interrupt");
+
+       if (panic_on_oops) {
+               printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n");
+               ssleep(5);
+               panic("Fatal exception");
+       }
+
        do_exit(SIGSEGV);
 }
 
@@ -570,6 +641,8 @@ asmlinkage void do_ov(struct pt_regs *regs)
  */
 asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
 {
+       die_if_kernel("FP exception in kernel code", regs);
+
        if (fcr31 & FPU_CSR_UNI_X) {
                int sig;
 
@@ -600,8 +673,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
                preempt_enable();
 
                /* Run the emulator */
-               sig = fpu_emulator_cop1Handler (regs,
-                       &current->thread.fpu.soft);
+               sig = fpu_emulator_cop1Handler (regs, &current->thread.fpu);
 
                preempt_disable();
 
@@ -610,7 +682,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
                 * We can't allow the emulated instruction to leave any of
                 * the cause bit set in $fcr31.
                 */
-               current->thread.fpu.soft.fcr31 &= ~FPU_CSR_ALL_X;
+               current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
 
                /* Restore the hardware register state */
                restore_fp(current);
@@ -755,7 +827,7 @@ asmlinkage void do_cpu(struct pt_regs *regs)
 
                if (!cpu_has_fpu) {
                        int sig = fpu_emulator_cop1Handler(regs,
-                                               &current->thread.fpu.soft);
+                                               &current->thread.fpu);
                        if (sig)
                                force_sig(sig, current);
 #ifdef CONFIG_MIPS_MT_FPAFF
@@ -849,31 +921,29 @@ asmlinkage void do_mt(struct pt_regs *regs)
 {
        int subcode;
 
-       die_if_kernel("MIPS MT Thread exception in kernel", regs);
-
        subcode = (read_vpe_c0_vpecontrol() & VPECONTROL_EXCPT)
                        >> VPECONTROL_EXCPT_SHIFT;
        switch (subcode) {
        case 0:
-               printk(KERN_ERR "Thread Underflow\n");
+               printk(KERN_DEBUG "Thread Underflow\n");
                break;
        case 1:
-               printk(KERN_ERR "Thread Overflow\n");
+               printk(KERN_DEBUG "Thread Overflow\n");
                break;
        case 2:
-               printk(KERN_ERR "Invalid YIELD Qualifier\n");
+               printk(KERN_DEBUG "Invalid YIELD Qualifier\n");
                break;
        case 3:
-               printk(KERN_ERR "Gating Storage Exception\n");
+               printk(KERN_DEBUG "Gating Storage Exception\n");
                break;
        case 4:
-               printk(KERN_ERR "YIELD Scheduler Exception\n");
+               printk(KERN_DEBUG "YIELD Scheduler Exception\n");
                break;
        case 5:
-               printk(KERN_ERR "Gating Storage Schedulier Exception\n");
+               printk(KERN_DEBUG "Gating Storage Schedulier Exception\n");
                break;
        default:
-               printk(KERN_ERR "*** UNKNOWN THREAD EXCEPTION %d ***\n",
+               printk(KERN_DEBUG "*** UNKNOWN THREAD EXCEPTION %d ***\n",
                        subcode);
                break;
        }
@@ -982,10 +1052,10 @@ void ejtag_exception_handler(struct pt_regs *regs)
        unsigned long depc, old_epc;
        unsigned int debug;
 
-       printk("SDBBP EJTAG debug exception - not handled yet, just ignored!\n");
+       printk(KERN_DEBUG "SDBBP EJTAG debug exception - not handled yet, just ignored!\n");
        depc = read_c0_depc();
        debug = read_c0_debug();
-       printk("c0_depc = %0*lx, DEBUG = %08x\n", field, depc, debug);
+       printk(KERN_DEBUG "c0_depc = %0*lx, DEBUG = %08x\n", field, depc, debug);
        if (debug & 0x80000000) {
                /*
                 * In branch delay slot.
@@ -1003,7 +1073,7 @@ void ejtag_exception_handler(struct pt_regs *regs)
        write_c0_depc(depc);
 
 #if 0
-       printk("\n\n----- Enable EJTAG single stepping ----\n\n");
+       printk(KERN_DEBUG "\n\n----- Enable EJTAG single stepping ----\n\n");
        write_c0_debug(debug | 0x100);
 #endif
 }
@@ -1051,7 +1121,7 @@ void *set_except_vector(int n, void *addr)
        return (void *)old_handler;
 }
 
-#ifdef CONFIG_CPU_MIPSR2
+#ifdef CONFIG_CPU_MIPSR2_SRS
 /*
  * MIPSR2 shadow register set allocation
  * FIXME: SMP...
@@ -1070,11 +1140,9 @@ static struct shadow_registers {
 
 static void mips_srs_init(void)
 {
-#ifdef CONFIG_CPU_MIPSR2_SRS
        shadow_registers.sr_supported = ((read_c0_srsctl() >> 26) & 0x0f) + 1;
        printk(KERN_INFO "%d MIPSR2 register sets available\n",
               shadow_registers.sr_supported);
-#endif
        shadow_registers.sr_allocated = 1;      /* Set 0 used by kernel */
 }
 
@@ -1199,7 +1267,14 @@ void *set_vi_handler(int n, void *addr)
 {
        return set_vi_srs_handler(n, addr, 0);
 }
-#endif
+
+#else
+
+static inline void mips_srs_init(void)
+{
+}
+
+#endif /* CONFIG_CPU_MIPSR2_SRS */
 
 /*
  * This is used by native signal handling
@@ -1389,9 +1464,7 @@ void __init trap_init(void)
        else
                ebase = CAC_BASE;
 
-#ifdef CONFIG_CPU_MIPSR2
        mips_srs_init();
-#endif
 
        per_cpu_trap_init();