[PARISC] Fix kernel panic in check_ivt
[sfrench/cifs-2.6.git] / arch / parisc / kernel / traps.c
index 77b28cb8aca6f1cecab831f219308c787869e729..c3ec9f1ec0f3462d8d0cd3b81824c8bc9a61d8d2 100644 (file)
 #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>
-#include <linux/smp_lock.h>
 #include <linux/spinlock.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/console.h>
 #include <linux/kallsyms.h>
+#include <linux/bug.h>
 
 #include <asm/assembly.h>
 #include <asm/system.h>
@@ -38,6 +39,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() */
 
@@ -48,7 +51,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) {
@@ -60,7 +63,7 @@ int printbinary(char *buf, unsigned long x, int nbits)
        return nbits;
 }
 
-#ifdef __LP64__
+#ifdef CONFIG_64BIT
 #define RFMT "%016lx"
 #else
 #define RFMT "%08lx"
@@ -159,13 +162,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
@@ -184,18 +187,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);
        }
@@ -203,6 +207,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)) {
@@ -221,15 +230,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();
@@ -241,9 +250,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) {
@@ -251,8 +260,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);
 }
 
@@ -263,61 +284,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)
@@ -326,7 +331,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)
@@ -544,7 +549,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;
 
@@ -562,7 +568,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:
@@ -609,7 +615,7 @@ void handle_interruption(int code, struct pt_regs *regs)
                
        case 13:
                /* Conditional Trap
-                  The condition succees in an instruction which traps 
+                  The condition succeeds in an instruction which traps
                   on condition  */
                if(user_mode(regs)){
                        si.si_signo = SIGFPE;
@@ -796,13 +802,14 @@ void handle_interruption(int code, struct pt_regs *regs)
 
 int __init check_ivt(void *iva)
 {
+       extern const u32 os_hpmc[];
+       extern const u32 os_hpmc_end[];
+
        int i;
        u32 check = 0;
        u32 *ivap;
        u32 *hpmcp;
        u32 length;
-       extern void os_hpmc(void);
-       extern void os_hpmc_end(void);
 
        if (strcmp((char *)iva, "cows can fly"))
                return -1;
@@ -814,7 +821,7 @@ int __init check_ivt(void *iva)
 
        /* Compute Checksum for HPMC handler */
 
-       length = (u32)((unsigned long)os_hpmc_end - (unsigned long)os_hpmc);
+       length = os_hpmc_end - os_hpmc;
        ivap[7] = length;
 
        hpmcp = (u32 *)os_hpmc;
@@ -830,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;
@@ -842,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;