]> Pileus Git - ~andy/linux/blobdiff - arch/parisc/kernel/traps.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
[~andy/linux] / arch / parisc / kernel / traps.c
index ff200608c851d5ef948908ff8b1b38f6fe2e56f2..55bc1471967d2221badf806b78aada441760f0c7 100644 (file)
  * state in 'asm.s'.
  */
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/timer.h>
+#include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/smp.h>
@@ -26,6 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/console.h>
 #include <linux/kallsyms.h>
+#include <linux/bug.h>
 
 #include <asm/assembly.h>
 #include <asm/system.h>
@@ -39,6 +40,8 @@
 #include <asm/pdc.h>
 #include <asm/pdc_chassis.h>
 #include <asm/unwind.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
 
 #include "../math-emu/math-emu.h"      /* for handle_fpe() */
 
@@ -49,7 +52,7 @@
 DEFINE_SPINLOCK(pa_dbit_lock);
 #endif
 
-int printbinary(char *buf, unsigned long x, int nbits)
+static int printbinary(char *buf, unsigned long x, int nbits)
 {
        unsigned long mask = 1UL << (nbits - 1);
        while (mask != 0) {
@@ -61,62 +64,47 @@ int printbinary(char *buf, unsigned long x, int nbits)
        return nbits;
 }
 
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
 #define RFMT "%016lx"
 #else
 #define RFMT "%08lx"
 #endif
+#define FFMT "%016llx" /* fpregs are 64-bit always */
 
-void show_regs(struct pt_regs *regs)
+#define PRINTREGS(lvl,r,f,fmt,x)       \
+       printk("%s%s%02d-%02d  " fmt " " fmt " " fmt " " fmt "\n",      \
+               lvl, f, (x), (x+3), (r)[(x)+0], (r)[(x)+1],             \
+               (r)[(x)+2], (r)[(x)+3])
+
+static void print_gr(char *level, struct pt_regs *regs)
 {
        int i;
-       char buf[128], *p;
-       char *level;
-       unsigned long cr30;
-       unsigned long cr31;
-       /* carlos says that gcc understands better memory in a struct,
-        * and it makes our life easier with fpregs -- T-Bone */
-       struct { u32 sw[2]; } s;
-       
-       level = user_mode(regs) ? KERN_DEBUG : KERN_CRIT;
-
-       printk("%s\n", level); /* don't want to have that pretty register dump messed up */
+       char buf[64];
 
+       printk("%s\n", level);
        printk("%s     YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI\n", level);
        printbinary(buf, regs->gr[0], 32);
        printk("%sPSW: %s %s\n", level, buf, print_tainted());
 
-       for (i = 0; i < 32; i += 4) {
-               int j;
-               p = buf;
-               p += sprintf(p, "%sr%02d-%02d ", level, i, i + 3);
-               for (j = 0; j < 4; j++) {
-                       p += sprintf(p, " " RFMT, (i+j) == 0 ? 0 : regs->gr[i + j]);
-               }
-               printk("%s\n", buf);
-       }
+       for (i = 0; i < 32; i += 4)
+               PRINTREGS(level, regs->gr, "r", RFMT, i);
+}
 
-       for (i = 0; i < 8; i += 4) {
-               int j;
-               p = buf;
-               p += sprintf(p, "%ssr%d-%d  ", level, i, i + 3);
-               for (j = 0; j < 4; j++) {
-                       p += sprintf(p, " " RFMT, regs->sr[i + j]);
-               }
-               printk("%s\n", buf);
-       }
+static void print_fr(char *level, struct pt_regs *regs)
+{
+       int i;
+       char buf[64];
+       struct { u32 sw[2]; } s;
 
        /* FR are 64bit everywhere. Need to use asm to get the content
         * of fpsr/fper1, and we assume that we won't have a FP Identify
         * in our way, otherwise we're screwed.
         * The fldd is used to restore the T-bit if there was one, as the
         * store clears it anyway.
-        * BTW, PA2.0 book says "thou shall not use fstw on FPSR/FPERs". */ 
-       __asm__ (
-               "fstd %%fr0,0(%1)       \n\t"
-               "fldd 0(%1),%%fr0       \n\t"
-               : "=m" (s) : "r" (&s) : "%r0"
-               );
+        * PA2.0 book says "thou shall not use fstw on FPSR/FPERs" - T-Bone */
+       asm volatile ("fstd %%fr0,0(%1) \n\t"
+                     "fldd 0(%1),%%fr0 \n\t"
+                     : "=m" (s) : "r" (&s) : "r0");
 
        printk("%s\n", level);
        printk("%s      VZOUICununcqcqcqcqcqcrmunTDVZOUI\n", level);
@@ -125,14 +113,25 @@ void show_regs(struct pt_regs *regs)
        printk("%sFPER1: %08x\n", level, s.sw[1]);
 
        /* here we'll print fr0 again, tho it'll be meaningless */
-       for (i = 0; i < 32; i += 4) {
-               int j;
-               p = buf;
-               p += sprintf(p, "%sfr%02d-%02d ", level, i, i + 3);
-               for (j = 0; j < 4; j++)
-                       p += sprintf(p, " %016llx", (i+j) == 0 ? 0 : regs->fr[i+j]);
-               printk("%s\n", buf);
-       }
+       for (i = 0; i < 32; i += 4)
+               PRINTREGS(level, regs->fr, "fr", FFMT, i);
+}
+
+void show_regs(struct pt_regs *regs)
+{
+       int i;
+       char *level;
+       unsigned long cr30, cr31;
+
+       level = user_mode(regs) ? KERN_DEBUG : KERN_CRIT;
+
+       print_gr(level, regs);
+
+       for (i = 0; i < 8; i += 4)
+               PRINTREGS(level, regs->sr, "sr", RFMT, i);
+
+       if (user_mode(regs))
+               print_fr(level, regs);
 
        cr30 = mfctl(30);
        cr31 = mfctl(31);
@@ -164,13 +163,13 @@ static void do_show_stack(struct unwind_frame_info *info)
 {
        int i = 1;
 
-       printk("Backtrace:\n");
+       printk(KERN_CRIT "Backtrace:\n");
        while (i <= 16) {
                if (unwind_once(info) < 0 || info->ip == 0)
                        break;
 
                if (__kernel_text_address(info->ip)) {
-                       printk(" [<" RFMT ">] ", info->ip);
+                       printk("%s [<" RFMT ">] ", (i&0x3)==1 ? KERN_CRIT : "", info->ip);
 #ifdef CONFIG_KALLSYMS
                        print_symbol("%s\n", info->ip);
 #else
@@ -189,18 +188,19 @@ void show_stack(struct task_struct *task, unsigned long *s)
 
        if (!task) {
                unsigned long sp;
-               struct pt_regs *r;
 
 HERE:
                asm volatile ("copy %%r30, %0" : "=r"(sp));
-               r = kzalloc(sizeof(struct pt_regs), GFP_KERNEL);
-               if (!r)
-                       return;
-               r->iaoq[0] = (unsigned long)&&HERE;
-               r->gr[2] = (unsigned long)__builtin_return_address(0);
-               r->gr[30] = sp;
-               unwind_frame_init(&info, current, r);
-               kfree(r);
+               {
+                       struct pt_regs r;
+
+                       memset(&r, 0, sizeof(struct pt_regs));
+                       r.iaoq[0] = (unsigned long)&&HERE;
+                       r.gr[2] = (unsigned long)__builtin_return_address(0);
+                       r.gr[30] = sp;
+
+                       unwind_frame_init(&info, current, &r);
+               }
        } else {
                unwind_frame_init_from_blocked_task(&info, task);
        }
@@ -208,6 +208,11 @@ HERE:
        do_show_stack(&info);
 }
 
+int is_valid_bugaddr(unsigned long iaoq)
+{
+       return 1;
+}
+
 void die_if_kernel(char *str, struct pt_regs *regs, long err)
 {
        if (user_mode(regs)) {
@@ -226,15 +231,15 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
        oops_in_progress = 1;
 
        /* Amuse the user in a SPARC fashion */
-       printk(
-"      _______________________________ \n"
-"     < Your System ate a SPARC! Gah! >\n"
-"      ------------------------------- \n"
-"             \\   ^__^\n"
-"              \\  (xx)\\_______\n"
-"                 (__)\\       )\\/\\\n"
-"                  U  ||----w |\n"
-"                     ||     ||\n");
+       if (err) printk(
+KERN_CRIT "      _______________________________ \n"
+KERN_CRIT "     < Your System ate a SPARC! Gah! >\n"
+KERN_CRIT "      ------------------------------- \n"
+KERN_CRIT "             \\   ^__^\n"
+KERN_CRIT "              \\  (xx)\\_______\n"
+KERN_CRIT "                 (__)\\       )\\/\\\n"
+KERN_CRIT "                  U  ||----w |\n"
+KERN_CRIT "                     ||     ||\n");
        
        /* unlock the pdc lock if necessary */
        pdc_emergency_unlock();
@@ -246,9 +251,9 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
        if (!console_drivers)
                pdc_console_restart();
        
-       printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n",
-               current->comm, current->pid, str, err);
-       show_regs(regs);
+       if (err)
+               printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n",
+                       current->comm, current->pid, str, err);
 
        /* Wot's wrong wif bein' racy? */
        if (current->thread.flags & PARISC_KERNEL_DEATH) {
@@ -256,8 +261,20 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
                local_irq_enable();
                while (1);
        }
-
        current->thread.flags |= PARISC_KERNEL_DEATH;
+
+       show_regs(regs);
+       dump_stack();
+
+       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);
 }
 
@@ -268,61 +285,45 @@ int syscall_ipi(int (*syscall) (struct pt_regs *), struct pt_regs *regs)
 
 /* gdb uses break 4,8 */
 #define GDB_BREAK_INSN 0x10004
-void handle_gdb_break(struct pt_regs *regs, int wot)
+static void handle_gdb_break(struct pt_regs *regs, int wot)
 {
        struct siginfo si;
 
-       si.si_code = wot;
-       si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
        si.si_signo = SIGTRAP;
        si.si_errno = 0;
+       si.si_code = wot;
+       si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
        force_sig_info(SIGTRAP, &si, current);
 }
 
-void handle_break(unsigned iir, struct pt_regs *regs)
+static void handle_break(struct pt_regs *regs)
 {
-       struct siginfo si;
-
-       switch(iir) {
-       case 0x00:
-#ifdef PRINT_USER_FAULTS
-               printk(KERN_DEBUG "break 0,0: pid=%d command='%s'\n",
-                      current->pid, current->comm);
-#endif
-               die_if_kernel("Breakpoint", regs, 0);
-#ifdef PRINT_USER_FAULTS
-               show_regs(regs);
-#endif
-               si.si_code = TRAP_BRKPT;
-               si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
-               si.si_signo = SIGTRAP;
-               force_sig_info(SIGTRAP, &si, current);
-               break;
-
-       case GDB_BREAK_INSN:
-               die_if_kernel("Breakpoint", regs, 0);
-               handle_gdb_break(regs, TRAP_BRKPT);
-               break;
+       unsigned iir = regs->iir;
+
+       if (unlikely(iir == PARISC_BUG_BREAK_INSN && !user_mode(regs))) {
+               /* check if a BUG() or WARN() trapped here.  */
+               enum bug_trap_type tt;
+               tt = report_bug(regs->iaoq[0] & ~3);
+               if (tt == BUG_TRAP_TYPE_WARN) {
+                       regs->iaoq[0] += 4;
+                       regs->iaoq[1] += 4;
+                       return; /* return to next instruction when WARN_ON().  */
+               }
+               die_if_kernel("Unknown kernel breakpoint", regs,
+                       (tt == BUG_TRAP_TYPE_NONE) ? 9 : 0);
+       }
 
-       default:
 #ifdef PRINT_USER_FAULTS
-               printk(KERN_DEBUG "break %#08x: pid=%d command='%s'\n",
-                      iir, current->pid, current->comm);
+       if (unlikely(iir != GDB_BREAK_INSN)) {
+               printk(KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
+                       iir & 31, (iir>>13) & ((1<<13)-1),
+                       current->pid, current->comm);
                show_regs(regs);
-#endif
-               si.si_signo = SIGTRAP;
-               si.si_code = TRAP_BRKPT;
-               si.si_addr = (void __user *) (regs->iaoq[0] & ~3);
-               force_sig_info(SIGTRAP, &si, current);
-               return;
        }
-}
-
+#endif
 
-int handle_toc(void)
-{
-       printk(KERN_CRIT "TOC call.\n");
-       return 0;
+       /* send standard GDB signal */
+       handle_gdb_break(regs, TRAP_BRKPT);
 }
 
 static void default_trap(int code, struct pt_regs *regs)
@@ -331,7 +332,7 @@ static void default_trap(int code, struct pt_regs *regs)
        show_regs(regs);
 }
 
-void (*cpu_lpmc) (int code, struct pt_regs *regs) = default_trap;
+void (*cpu_lpmc) (int code, struct pt_regs *regs) __read_mostly = default_trap;
 
 
 void transfer_pim_to_trap_frame(struct pt_regs *regs)
@@ -549,7 +550,8 @@ void handle_interruption(int code, struct pt_regs *regs)
                /* Low-priority machine check */
                pdc_chassis_send_status(PDC_CHASSIS_DIRECT_LPMC);
                
-               flush_all_caches();
+               flush_cache_all();
+               flush_tlb_all();
                cpu_lpmc(5, regs);
                return;
 
@@ -567,7 +569,7 @@ void handle_interruption(int code, struct pt_regs *regs)
 
        case  9:
                /* Break instruction trap */
-               handle_break(regs->iir,regs);
+               handle_break(regs);
                return;
        
        case 10:
@@ -835,7 +837,7 @@ int __init check_ivt(void *iva)
        return 0;
 }
        
-#ifndef __LP64__
+#ifndef CONFIG_64BIT
 extern const void fault_vector_11;
 #endif
 extern const void fault_vector_20;
@@ -847,7 +849,7 @@ void __init trap_init(void)
        if (boot_cpu_data.cpu_type >= pcxu)
                iva = (void *) &fault_vector_20;
        else
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
                panic("Can't boot 64-bit OS on PA1.1 processor!");
 #else
                iva = (void *) &fault_vector_11;