Blackfin arch: Random read/write errors are a bad thing
[safe/jmp/linux-2.6] / arch / powerpc / kernel / traps.c
index 98660ae..5457e95 100644 (file)
@@ -14,7 +14,6 @@
  * This file handles the architecture-dependent parts of hardware exceptions
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -24,7 +23,6 @@
 #include <linux/ptrace.h>
 #include <linux/slab.h>
 #include <linux/user.h>
-#include <linux/a.out.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/kprobes.h>
 #include <linux/kexec.h>
+#include <linux/backlight.h>
+#include <linux/bug.h>
+#include <linux/kdebug.h>
 
-#include <asm/kdebug.h>
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/firmware.h>
 #include <asm/processor.h>
 #endif
+#include <asm/kexec.h>
 
-#ifdef CONFIG_PPC64    /* XXX */
-#define _IO_BASE       pci_io_base
-#endif
-
-#ifdef CONFIG_DEBUGGER
+#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
 int (*__debugger)(struct pt_regs *regs);
 int (*__debugger_ipi)(struct pt_regs *regs);
 int (*__debugger_bpt)(struct pt_regs *regs);
@@ -74,115 +71,98 @@ EXPORT_SYMBOL(__debugger_dabr_match);
 EXPORT_SYMBOL(__debugger_fault_handler);
 #endif
 
-struct notifier_block *powerpc_die_chain;
-static DEFINE_SPINLOCK(die_notifier_lock);
-
-int register_die_notifier(struct notifier_block *nb)
-{
-       int err = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&die_notifier_lock, flags);
-       err = notifier_chain_register(&powerpc_die_chain, nb);
-       spin_unlock_irqrestore(&die_notifier_lock, flags);
-       return err;
-}
-
 /*
  * Trap & Exception support
  */
 
-static DEFINE_SPINLOCK(die_lock);
+#ifdef CONFIG_PMAC_BACKLIGHT
+static void pmac_backlight_unblank(void)
+{
+       mutex_lock(&pmac_backlight_mutex);
+       if (pmac_backlight) {
+               struct backlight_properties *props;
+
+               props = &pmac_backlight->props;
+               props->brightness = props->max_brightness;
+               props->power = FB_BLANK_UNBLANK;
+               backlight_update_status(pmac_backlight);
+       }
+       mutex_unlock(&pmac_backlight_mutex);
+}
+#else
+static inline void pmac_backlight_unblank(void) { }
+#endif
 
 int die(const char *str, struct pt_regs *regs, long err)
 {
-       static int die_counter, crash_dump_start = 0;
-       int nl = 0;
+       static struct {
+               spinlock_t lock;
+               u32 lock_owner;
+               int lock_owner_depth;
+       } die = {
+               .lock =                 __SPIN_LOCK_UNLOCKED(die.lock),
+               .lock_owner =           -1,
+               .lock_owner_depth =     0
+       };
+       static int die_counter;
+       unsigned long flags;
 
        if (debugger(regs))
                return 1;
 
-       console_verbose();
-       spin_lock_irq(&die_lock);
-       bust_spinlocks(1);
-#ifdef CONFIG_PMAC_BACKLIGHT
-       if (_machine == _MACH_Pmac) {
-               set_backlight_enable(1);
-               set_backlight_level(BACKLIGHT_MAX);
+       oops_enter();
+
+       if (die.lock_owner != raw_smp_processor_id()) {
+               console_verbose();
+               spin_lock_irqsave(&die.lock, flags);
+               die.lock_owner = smp_processor_id();
+               die.lock_owner_depth = 0;
+               bust_spinlocks(1);
+               if (machine_is(powermac))
+                       pmac_backlight_unblank();
+       } else {
+               local_save_flags(flags);
        }
-#endif
-       printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
+
+       if (++die.lock_owner_depth < 3) {
+               printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
 #ifdef CONFIG_PREEMPT
-       printk("PREEMPT ");
-       nl = 1;
+               printk("PREEMPT ");
 #endif
 #ifdef CONFIG_SMP
-       printk("SMP NR_CPUS=%d ", NR_CPUS);
-       nl = 1;
+               printk("SMP NR_CPUS=%d ", NR_CPUS);
 #endif
 #ifdef CONFIG_DEBUG_PAGEALLOC
-       printk("DEBUG_PAGEALLOC ");
-       nl = 1;
+               printk("DEBUG_PAGEALLOC ");
 #endif
 #ifdef CONFIG_NUMA
-       printk("NUMA ");
-       nl = 1;
+               printk("NUMA ");
 #endif
-#ifdef CONFIG_PPC64
-       switch (_machine) {
-       case PLATFORM_PSERIES:
-               printk("PSERIES ");
-               nl = 1;
-               break;
-       case PLATFORM_PSERIES_LPAR:
-               printk("PSERIES LPAR ");
-               nl = 1;
-               break;
-       case PLATFORM_ISERIES_LPAR:
-               printk("ISERIES LPAR ");
-               nl = 1;
-               break;
-       case PLATFORM_POWERMAC:
-               printk("POWERMAC ");
-               nl = 1;
-               break;
-       case PLATFORM_CELL:
-               printk("CELL ");
-               nl = 1;
-               break;
+               printk("%s\n", ppc_md.name ? ppc_md.name : "");
+
+               print_modules();
+               show_regs(regs);
+       } else {
+               printk("Recursive die() failure, output suppressed\n");
        }
-#endif
-       if (nl)
-               printk("\n");
-       print_modules();
-       show_regs(regs);
+
        bust_spinlocks(0);
+       die.lock_owner = -1;
+       add_taint(TAINT_DIE);
+       spin_unlock_irqrestore(&die.lock, flags);
 
-       if (!crash_dump_start && kexec_should_crash(current)) {
-               crash_dump_start = 1;
-               spin_unlock_irq(&die_lock);
+       if (kexec_should_crash(current) ||
+               kexec_sr_activated(smp_processor_id()))
                crash_kexec(regs);
-               /* NOTREACHED */
-       }
-       spin_unlock_irq(&die_lock);
-       if (crash_dump_start)
-               /*
-                * Only for soft-reset: Other CPUs will be responded to an IPI
-                * sent by first kexec CPU.
-                */
-               for(;;)
-                       ;
+       crash_kexec_secondary(regs);
 
        if (in_interrupt())
                panic("Fatal exception in interrupt");
 
-       if (panic_on_oops) {
-#ifdef CONFIG_PPC64
-               printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n");
-               ssleep(5);
-#endif
+       if (panic_on_oops)
                panic("Fatal exception");
-       }
+
+       oops_exit();
        do_exit(err);
 
        return 0;
@@ -191,11 +171,21 @@ int die(const char *str, struct pt_regs *regs, long err)
 void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
 {
        siginfo_t info;
+       const char fmt32[] = KERN_INFO "%s[%d]: unhandled signal %d " \
+                       "at %08lx nip %08lx lr %08lx code %x\n";
+       const char fmt64[] = KERN_INFO "%s[%d]: unhandled signal %d " \
+                       "at %016lx nip %016lx lr %016lx code %x\n";
 
        if (!user_mode(regs)) {
                if (die("Exception in kernel mode", regs, signr))
                        return;
-       }
+       } else if (show_unhandled_signals &&
+                   unhandled_signal(current, signr) &&
+                   printk_ratelimit()) {
+                       printk(regs->msr & MSR_SF ? fmt64 : fmt32,
+                               current->comm, current->pid, signr,
+                               addr, regs->nip, regs->link, code);
+               }
 
        memset(&info, 0, sizeof(info));
        info.si_signo = signr;
@@ -210,7 +200,7 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
         * generate the same exception over and over again and we get
         * nowhere.  Better to kill it and let the kernel panic.
         */
-       if (current->pid == 1) {
+       if (is_global_init(current)) {
                __sighandler_t handler;
 
                spin_lock_irq(&current->sighand->siglock);
@@ -235,8 +225,25 @@ void system_reset_exception(struct pt_regs *regs)
                        return;
        }
 
+#ifdef CONFIG_KEXEC
+       cpu_set(smp_processor_id(), cpus_in_sr);
+#endif
+
        die("System Reset", regs, SIGABRT);
 
+       /*
+        * Some CPUs when released from the debugger will execute this path.
+        * These CPUs entered the debugger via a soft-reset. If the CPU was
+        * hung before entering the debugger it will return to the hung
+        * state when exiting this function.  This causes a problem in
+        * kdump since the hung CPU(s) will not respond to the IPI sent
+        * from kdump. To prevent the problem we call crash_kexec_secondary()
+        * here. If a kdump had not been initiated or we exit the debugger
+        * with the "exit and recover" command (x) crash_kexec_secondary()
+        * will return after 5ms and the CPU returns to its previous state.
+        */
+       crash_kexec_secondary(regs);
+
        /* Must die if the interrupt is not recoverable */
        if (!(regs->msr & MSR_RI))
                panic("Unrecoverable System Reset");
@@ -257,7 +264,7 @@ void system_reset_exception(struct pt_regs *regs)
  */
 static inline int check_io_access(struct pt_regs *regs)
 {
-#ifdef CONFIG_PPC_PMAC
+#ifdef CONFIG_PPC32
        unsigned long msr = regs->msr;
        const struct exception_table_entry *entry;
        unsigned int *nip = (unsigned int *)regs->nip;
@@ -290,7 +297,7 @@ static inline int check_io_access(struct pt_regs *regs)
                        return 1;
                }
        }
-#endif /* CONFIG_PPC_PMAC */
+#endif /* CONFIG_PPC32 */
        return 0;
 }
 
@@ -301,7 +308,7 @@ static inline int check_io_access(struct pt_regs *regs)
 #ifndef CONFIG_FSL_BOOKE
 #define get_mc_reason(regs)    ((regs)->dsisr)
 #else
-#define get_mc_reason(regs)    (mfspr(SPRN_MCSR))
+#define get_mc_reason(regs)    (mfspr(SPRN_MCSR) & MCSR_MASK)
 #endif
 #define REASON_FP              ESR_FP
 #define REASON_ILLEGAL         (ESR_PIL | ESR_PUO)
@@ -326,57 +333,25 @@ static inline int check_io_access(struct pt_regs *regs)
 #define clear_single_step(regs)        ((regs)->msr &= ~MSR_SE)
 #endif
 
-/*
- * This is "fall-back" implementation for configurations
- * which don't provide platform-specific machine check info
- */
-void __attribute__ ((weak))
-platform_machine_check(struct pt_regs *regs)
-{
-}
-
-void machine_check_exception(struct pt_regs *regs)
+#if defined(CONFIG_4xx)
+int machine_check_4xx(struct pt_regs *regs)
 {
-#ifdef CONFIG_PPC64
-       int recover = 0;
-
-       /* See if any machine dependent calls */
-       if (ppc_md.machine_check_exception)
-               recover = ppc_md.machine_check_exception(regs);
-
-       if (recover)
-               return;
-#else
        unsigned long reason = get_mc_reason(regs);
 
-       if (user_mode(regs)) {
-               regs->msr |= MSR_RI;
-               _exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
-               return;
-       }
-
-#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-       /* the qspan pci read routines can cause machine checks -- Cort */
-       bad_page_fault(regs, regs->dar, SIGBUS);
-       return;
-#endif
-
-       if (debugger_fault_handler(regs)) {
-               regs->msr |= MSR_RI;
-               return;
-       }
-
-       if (check_io_access(regs))
-               return;
-
-#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
        if (reason & ESR_IMCP) {
                printk("Instruction");
                mtspr(SPRN_ESR, reason & ~ESR_IMCP);
        } else
                printk("Data");
        printk(" machine check in kernel mode.\n");
-#elif defined(CONFIG_440A)
+
+       return 0;
+}
+
+int machine_check_440A(struct pt_regs *regs)
+{
+       unsigned long reason = get_mc_reason(regs);
+
        printk("Machine check in kernel mode.\n");
        if (reason & ESR_IMCP){
                printk("Instruction Synchronous Machine Check exception\n");
@@ -406,7 +381,13 @@ void machine_check_exception(struct pt_regs *regs)
                /* Clear MCSR */
                mtspr(SPRN_MCSR, mcsr);
        }
-#elif defined (CONFIG_E500)
+       return 0;
+}
+#elif defined(CONFIG_E500)
+int machine_check_e500(struct pt_regs *regs)
+{
+       unsigned long reason = get_mc_reason(regs);
+
        printk("Machine check in kernel mode.\n");
        printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -418,8 +399,6 @@ void machine_check_exception(struct pt_regs *regs)
                printk("Data Cache Push Parity Error\n");
        if (reason & MCSR_DCPERR)
                printk("Data Cache Parity Error\n");
-       if (reason & MCSR_GL_CI)
-               printk("Guarded Load or Cache-Inhibited stwcx.\n");
        if (reason & MCSR_BUS_IAERR)
                printk("Bus - Instruction Address Error\n");
        if (reason & MCSR_BUS_RAERR)
@@ -436,7 +415,14 @@ void machine_check_exception(struct pt_regs *regs)
                printk("Bus - Instruction Parity Error\n");
        if (reason & MCSR_BUS_RPERR)
                printk("Bus - Read Parity Error\n");
-#elif defined (CONFIG_E200)
+
+       return 0;
+}
+#elif defined(CONFIG_E200)
+int machine_check_e200(struct pt_regs *regs)
+{
+       unsigned long reason = get_mc_reason(regs);
+
        printk("Machine check in kernel mode.\n");
        printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -454,7 +440,14 @@ void machine_check_exception(struct pt_regs *regs)
                printk("Bus - Read Bus Error on data load\n");
        if (reason & MCSR_BUS_WRERR)
                printk("Bus - Write Bus Error on buffered store or cache line push\n");
-#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
+
+       return 0;
+}
+#else
+int machine_check_generic(struct pt_regs *regs)
+{
+       unsigned long reason = get_mc_reason(regs);
+
        printk("Machine check in kernel mode.\n");
        printk("Caused by (from SRR1=%lx): ", reason);
        switch (reason & 0x601F0000) {
@@ -484,14 +477,52 @@ void machine_check_exception(struct pt_regs *regs)
        default:
                printk("Unknown values in msr\n");
        }
-#endif /* CONFIG_4xx */
+       return 0;
+}
+#endif /* everything else */
 
-       /*
-        * Optional platform-provided routine to print out
-        * additional info, e.g. bus error registers.
+void machine_check_exception(struct pt_regs *regs)
+{
+       int recover = 0;
+
+       /* See if any machine dependent calls. In theory, we would want
+        * to call the CPU first, and call the ppc_md. one if the CPU
+        * one returns a positive number. However there is existing code
+        * that assumes the board gets a first chance, so let's keep it
+        * that way for now and fix things later. --BenH.
+        */
+       if (ppc_md.machine_check_exception)
+               recover = ppc_md.machine_check_exception(regs);
+       else if (cur_cpu_spec->machine_check)
+               recover = cur_cpu_spec->machine_check(regs);
+
+       if (recover > 0)
+               return;
+
+       if (user_mode(regs)) {
+               regs->msr |= MSR_RI;
+               _exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
+               return;
+       }
+
+#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
+       /* the qspan pci read routines can cause machine checks -- Cort
+        *
+        * yuck !!! that totally needs to go away ! There are better ways
+        * to deal with that than having a wart in the mcheck handler.
+        * -- BenH
         */
-       platform_machine_check(regs);
-#endif /* CONFIG_PPC64 */
+       bad_page_fault(regs, regs->dar, SIGBUS);
+       return;
+#endif
+
+       if (debugger_fault_handler(regs)) {
+               regs->msr |= MSR_RI;
+               return;
+       }
+
+       if (check_io_access(regs))
+               return;
 
        if (debugger_fault_handler(regs))
                return;
@@ -557,34 +588,40 @@ static void emulate_single_step(struct pt_regs *regs)
        }
 }
 
-static void parse_fpe(struct pt_regs *regs)
+static inline int __parse_fpscr(unsigned long fpscr)
 {
-       int code = 0;
-       unsigned long fpscr;
-
-       flush_fp_to_thread(current);
-
-       fpscr = current->thread.fpscr.val;
+       int ret = 0;
 
        /* Invalid operation */
        if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX))
-               code = FPE_FLTINV;
+               ret = FPE_FLTINV;
 
        /* Overflow */
        else if ((fpscr & FPSCR_OE) && (fpscr & FPSCR_OX))
-               code = FPE_FLTOVF;
+               ret = FPE_FLTOVF;
 
        /* Underflow */
        else if ((fpscr & FPSCR_UE) && (fpscr & FPSCR_UX))
-               code = FPE_FLTUND;
+               ret = FPE_FLTUND;
 
        /* Divide by zero */
        else if ((fpscr & FPSCR_ZE) && (fpscr & FPSCR_ZX))
-               code = FPE_FLTDIV;
+               ret = FPE_FLTDIV;
 
        /* Inexact result */
        else if ((fpscr & FPSCR_XE) && (fpscr & FPSCR_XX))
-               code = FPE_FLTRES;
+               ret = FPE_FLTRES;
+
+       return ret;
+}
+
+static void parse_fpe(struct pt_regs *regs)
+{
+       int code = 0;
+
+       flush_fp_to_thread(current);
+
+       code = __parse_fpscr(current->thread.fpscr.val);
 
        _exception(SIGFPE, regs, code, regs->nip);
 }
@@ -604,19 +641,25 @@ static void parse_fpe(struct pt_regs *regs)
 #define INST_MFSPR_PVR_MASK    0xfc1fffff
 
 #define INST_DCBA              0x7c0005ec
-#define INST_DCBA_MASK         0x7c0007fe
+#define INST_DCBA_MASK         0xfc0007fe
 
 #define INST_MCRXR             0x7c000400
-#define INST_MCRXR_MASK                0x7c0007fe
+#define INST_MCRXR_MASK                0xfc0007fe
 
 #define INST_STRING            0x7c00042a
-#define INST_STRING_MASK       0x7c0007fe
-#define INST_STRING_GEN_MASK   0x7c00067e
+#define INST_STRING_MASK       0xfc0007fe
+#define INST_STRING_GEN_MASK   0xfc00067e
 #define INST_LSWI              0x7c0004aa
 #define INST_LSWX              0x7c00042a
 #define INST_STSWI             0x7c0005aa
 #define INST_STSWX             0x7c00052a
 
+#define INST_POPCNTB           0x7c0000f4
+#define INST_POPCNTB_MASK      0xfc0007fe
+
+#define INST_ISEL              0x7c00001e
+#define INST_ISEL_MASK         0xfc00003e
+
 static int emulate_string_inst(struct pt_regs *regs, u32 instword)
 {
        u8 rT = (instword >> 21) & 0x1f;
@@ -685,12 +728,46 @@ static int emulate_string_inst(struct pt_regs *regs, u32 instword)
        return 0;
 }
 
+static int emulate_popcntb_inst(struct pt_regs *regs, u32 instword)
+{
+       u32 ra,rs;
+       unsigned long tmp;
+
+       ra = (instword >> 16) & 0x1f;
+       rs = (instword >> 21) & 0x1f;
+
+       tmp = regs->gpr[rs];
+       tmp = tmp - ((tmp >> 1) & 0x5555555555555555ULL);
+       tmp = (tmp & 0x3333333333333333ULL) + ((tmp >> 2) & 0x3333333333333333ULL);
+       tmp = (tmp + (tmp >> 4)) & 0x0f0f0f0f0f0f0f0fULL;
+       regs->gpr[ra] = tmp;
+
+       return 0;
+}
+
+static int emulate_isel(struct pt_regs *regs, u32 instword)
+{
+       u8 rT = (instword >> 21) & 0x1f;
+       u8 rA = (instword >> 16) & 0x1f;
+       u8 rB = (instword >> 11) & 0x1f;
+       u8 BC = (instword >> 6) & 0x1f;
+       u8 bit;
+       unsigned long tmp;
+
+       tmp = (rA == 0) ? 0 : regs->gpr[rA];
+       bit = (regs->ccr >> (31 - BC)) & 0x1;
+
+       regs->gpr[rT] = bit ? tmp : regs->gpr[rB];
+
+       return 0;
+}
+
 static int emulate_instruction(struct pt_regs *regs)
 {
        u32 instword;
        u32 rd;
 
-       if (!user_mode(regs))
+       if (!user_mode(regs) || (regs->msr & MSR_LE))
                return -EINVAL;
        CHECK_FULL_REGS(regs);
 
@@ -722,57 +799,22 @@ static int emulate_instruction(struct pt_regs *regs)
        if ((instword & INST_STRING_GEN_MASK) == INST_STRING)
                return emulate_string_inst(regs, instword);
 
-       return -EINVAL;
-}
-
-/*
- * Look through the list of trap instructions that are used for BUG(),
- * BUG_ON() and WARN_ON() and see if we hit one.  At this point we know
- * that the exception was caused by a trap instruction of some kind.
- * Returns 1 if we should continue (i.e. it was a WARN_ON) or 0
- * otherwise.
- */
-extern struct bug_entry __start___bug_table[], __stop___bug_table[];
-
-#ifndef CONFIG_MODULES
-#define module_find_bug(x)     NULL
-#endif
+       /* Emulate the popcntb (Population Count Bytes) instruction. */
+       if ((instword & INST_POPCNTB_MASK) == INST_POPCNTB) {
+               return emulate_popcntb_inst(regs, instword);
+       }
 
-struct bug_entry *find_bug(unsigned long bugaddr)
-{
-       struct bug_entry *bug;
+       /* Emulate isel (Integer Select) instruction */
+       if ((instword & INST_ISEL_MASK) == INST_ISEL) {
+               return emulate_isel(regs, instword);
+       }
 
-       for (bug = __start___bug_table; bug < __stop___bug_table; ++bug)
-               if (bugaddr == bug->bug_addr)
-                       return bug;
-       return module_find_bug(bugaddr);
+       return -EINVAL;
 }
 
-static int check_bug_trap(struct pt_regs *regs)
+int is_valid_bugaddr(unsigned long addr)
 {
-       struct bug_entry *bug;
-       unsigned long addr;
-
-       if (regs->msr & MSR_PR)
-               return 0;       /* not in kernel */
-       addr = regs->nip;       /* address of trap instruction */
-       if (addr < PAGE_OFFSET)
-               return 0;
-       bug = find_bug(regs->nip);
-       if (bug == NULL)
-               return 0;
-       if (bug->line & BUG_WARNING_TRAP) {
-               /* this is a WARN_ON rather than BUG/BUG_ON */
-               printk(KERN_ERR "Badness in %s at %s:%ld\n",
-                      bug->function, bug->file,
-                      bug->line & ~BUG_WARNING_TRAP);
-               dump_stack();
-               return 1;
-       }
-       printk(KERN_CRIT "kernel BUG in %s at %s:%ld!\n",
-              bug->function, bug->file, bug->line);
-
-       return 0;
+       return is_kernel_addr(addr);
 }
 
 void __kprobes program_check_exception(struct pt_regs *regs)
@@ -780,19 +822,8 @@ void __kprobes program_check_exception(struct pt_regs *regs)
        unsigned int reason = get_reason(regs);
        extern int do_mathemu(struct pt_regs *regs);
 
-#ifdef CONFIG_MATH_EMULATION
-       /* (reason & REASON_ILLEGAL) would be the obvious thing here,
-        * but there seems to be a hardware bug on the 405GP (RevD)
-        * that means ESR is sometimes set incorrectly - either to
-        * ESR_DST (!?) or 0.  In the process of chasing this with the
-        * hardware people - not sure if it can happen on any illegal
-        * instruction or only on FP instructions, whether there is a
-        * pattern to occurences etc. -dgibson 31/Mar/2003 */
-       if (!(reason & REASON_TRAP) && do_mathemu(regs) == 0) {
-               emulate_single_step(regs);
-               return;
-       }
-#endif /* CONFIG_MATH_EMULATION */
+       /* We can now get here via a FP Unavailable exception if the core
+        * has no FPU, in that case the reason flags will be 0 */
 
        if (reason & REASON_FP) {
                /* IEEE FP exception */
@@ -806,7 +837,9 @@ void __kprobes program_check_exception(struct pt_regs *regs)
                        return;
                if (debugger_bpt(regs))
                        return;
-               if (check_bug_trap(regs)) {
+
+               if (!(regs->msr & MSR_PR) &&  /* not user-mode */
+                   report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
                        regs->nip += 4;
                        return;
                }
@@ -816,6 +849,31 @@ void __kprobes program_check_exception(struct pt_regs *regs)
 
        local_irq_enable();
 
+#ifdef CONFIG_MATH_EMULATION
+       /* (reason & REASON_ILLEGAL) would be the obvious thing here,
+        * but there seems to be a hardware bug on the 405GP (RevD)
+        * that means ESR is sometimes set incorrectly - either to
+        * ESR_DST (!?) or 0.  In the process of chasing this with the
+        * hardware people - not sure if it can happen on any illegal
+        * instruction or only on FP instructions, whether there is a
+        * pattern to occurences etc. -dgibson 31/Mar/2003 */
+       switch (do_mathemu(regs)) {
+       case 0:
+               emulate_single_step(regs);
+               return;
+       case 1: {
+                       int code = 0;
+                       code = __parse_fpscr(current->thread.fpscr.val);
+                       _exception(SIGFPE, regs, code, regs->nip);
+                       return;
+               }
+       case -EFAULT:
+               _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
+               return;
+       }
+       /* fall through on any other errors */
+#endif /* CONFIG_MATH_EMULATION */
+
        /* Try to emulate it if we should. */
        if (reason & (REASON_ILLEGAL | REASON_PRIVILEGED)) {
                switch (emulate_instruction(regs)) {
@@ -837,9 +895,11 @@ void __kprobes program_check_exception(struct pt_regs *regs)
 
 void alignment_exception(struct pt_regs *regs)
 {
-       int fixed;
+       int sig, code, fixed = 0;
 
-       fixed = fix_alignment(regs);
+       /* we don't implement logging of alignment exceptions */
+       if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS))
+               fixed = fix_alignment(regs);
 
        if (fixed == 1) {
                regs->nip += 4; /* skip over emulated instruction */
@@ -849,14 +909,16 @@ void alignment_exception(struct pt_regs *regs)
 
        /* Operand address was bad */
        if (fixed == -EFAULT) {
-               if (user_mode(regs))
-                       _exception(SIGSEGV, regs, SEGV_ACCERR, regs->dar);
-               else
-                       /* Search exception table */
-                       bad_page_fault(regs, regs->dar, SIGSEGV);
-               return;
+               sig = SIGSEGV;
+               code = SEGV_ACCERR;
+       } else {
+               sig = SIGBUS;
+               code = BUS_ADRALN;
        }
-       _exception(SIGBUS, regs, BUS_ADRALN, regs->dar);
+       if (user_mode(regs))
+               _exception(sig, regs, code, regs->dar);
+       else
+               bad_page_fault(regs, regs->dar, sig);
 }
 
 void StackOverflow(struct pt_regs *regs)
@@ -879,7 +941,7 @@ void nonrecoverable_exception(struct pt_regs *regs)
 void trace_syscall(struct pt_regs *regs)
 {
        printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld    %s\n",
-              current, current->pid, regs->nip, regs->link, regs->gpr[0],
+              current, task_pid_nr(current), regs->nip, regs->link, regs->gpr[0],
               regs->ccr&0x10000000?"Error=":"", regs->gpr[3], print_tainted());
 }
 
@@ -892,19 +954,32 @@ void kernel_fp_unavailable_exception(struct pt_regs *regs)
 
 void altivec_unavailable_exception(struct pt_regs *regs)
 {
-#if !defined(CONFIG_ALTIVEC)
        if (user_mode(regs)) {
                /* A user program has executed an altivec instruction,
                   but this kernel doesn't support altivec. */
                _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
                return;
        }
-#endif
+
        printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
                        "%lx at %lx\n", regs->trap, regs->nip);
        die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
 }
 
+void vsx_unavailable_exception(struct pt_regs *regs)
+{
+       if (user_mode(regs)) {
+               /* A user program has executed an vsx instruction,
+                  but this kernel doesn't support vsx. */
+               _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+               return;
+       }
+
+       printk(KERN_EMERG "Unrecoverable VSX Unavailable Exception "
+                       "%lx at %lx\n", regs->trap, regs->nip);
+       die("Unrecoverable VSX Unavailable Exception", regs, SIGABRT);
+}
+
 void performance_monitor_exception(struct pt_regs *regs)
 {
        perf_irq(regs);
@@ -915,7 +990,9 @@ void SoftwareEmulation(struct pt_regs *regs)
 {
        extern int do_mathemu(struct pt_regs *);
        extern int Soft_emulate_8xx(struct pt_regs *);
+#if defined(CONFIG_MATH_EMULATION) || defined(CONFIG_8XX_MINIMAL_FPEMU)
        int errcode;
+#endif
 
        CHECK_FULL_REGS(regs);
 
@@ -926,38 +1003,85 @@ void SoftwareEmulation(struct pt_regs *regs)
 
 #ifdef CONFIG_MATH_EMULATION
        errcode = do_mathemu(regs);
-#else
+
+       switch (errcode) {
+       case 0:
+               emulate_single_step(regs);
+               return;
+       case 1: {
+                       int code = 0;
+                       code = __parse_fpscr(current->thread.fpscr.val);
+                       _exception(SIGFPE, regs, code, regs->nip);
+                       return;
+               }
+       case -EFAULT:
+               _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
+               return;
+       default:
+               _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+               return;
+       }
+
+#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
        errcode = Soft_emulate_8xx(regs);
-#endif
-       if (errcode) {
-               if (errcode > 0)
-                       _exception(SIGFPE, regs, 0, 0);
-               else if (errcode == -EFAULT)
-                       _exception(SIGSEGV, regs, 0, 0);
-               else
-                       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
-       } else
+       switch (errcode) {
+       case 0:
                emulate_single_step(regs);
+               return;
+       case 1:
+               _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+               return;
+       case -EFAULT:
+               _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
+               return;
+       }
+#else
+       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+#endif
 }
 #endif /* CONFIG_8xx */
 
 #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
 
-void DebugException(struct pt_regs *regs, unsigned long debug_status)
+void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
 {
        if (debug_status & DBSR_IC) {   /* instruction completion */
                regs->msr &= ~MSR_DE;
+
+               /* Disable instruction completion */
+               mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_IC);
+               /* Clear the instruction completion event */
+               mtspr(SPRN_DBSR, DBSR_IC);
+
+               if (notify_die(DIE_SSTEP, "single_step", regs, 5,
+                              5, SIGTRAP) == NOTIFY_STOP) {
+                       return;
+               }
+
+               if (debugger_sstep(regs))
+                       return;
+
                if (user_mode(regs)) {
                        current->thread.dbcr0 &= ~DBCR0_IC;
+               }
+
+               _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
+       } else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) {
+               regs->msr &= ~MSR_DE;
+
+               if (user_mode(regs)) {
+                       current->thread.dbcr0 &= ~(DBSR_DAC1R | DBSR_DAC1W |
+                                                               DBCR0_IDM);
                } else {
-                       /* Disable instruction completion */
-                       mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_IC);
-                       /* Clear the instruction completion event */
-                       mtspr(SPRN_DBSR, DBSR_IC);
-                       if (debugger_sstep(regs))
-                               return;
+                       /* Disable DAC interupts */
+                       mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~(DBSR_DAC1R |
+                                               DBSR_DAC1W | DBCR0_IDM));
+
+                       /* Clear the DAC event */
+                       mtspr(SPRN_DBSR, (DBSR_DAC1R | DBSR_DAC1W));
                }
-               _exception(SIGTRAP, regs, TRAP_TRACE, 0);
+               /* Setup and send the trap to the handler */
+               do_dabr(regs, mfspr(SPRN_DAC1), debug_status);
        }
 }
 #endif /* CONFIG_4xx || CONFIG_BOOKE */
@@ -1004,6 +1128,21 @@ void altivec_assist_exception(struct pt_regs *regs)
 }
 #endif /* CONFIG_ALTIVEC */
 
+#ifdef CONFIG_VSX
+void vsx_assist_exception(struct pt_regs *regs)
+{
+       if (!user_mode(regs)) {
+               printk(KERN_EMERG "VSX assist exception in kernel mode"
+                      " at %lx\n", regs->nip);
+               die("Kernel VSX assist exception", regs, SIGILL);
+       }
+
+       flush_vsx_to_thread(current);
+       printk(KERN_INFO "VSX assist not supported at %lx\n", regs->nip);
+       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+}
+#endif /* CONFIG_VSX */
+
 #ifdef CONFIG_FSL_BOOKE
 void CacheLockingException(struct pt_regs *regs, unsigned long address,
                           unsigned long error_code)
@@ -1021,37 +1160,85 @@ void CacheLockingException(struct pt_regs *regs, unsigned long address,
 #ifdef CONFIG_SPE
 void SPEFloatingPointException(struct pt_regs *regs)
 {
+       extern int do_spe_mathemu(struct pt_regs *regs);
        unsigned long spefscr;
        int fpexc_mode;
        int code = 0;
+       int err;
+
+       preempt_disable();
+       if (regs->msr & MSR_SPE)
+               giveup_spe(current);
+       preempt_enable();
 
        spefscr = current->thread.spefscr;
        fpexc_mode = current->thread.fpexc_mode;
 
-       /* Hardware does not neccessarily set sticky
-        * underflow/overflow/invalid flags */
        if ((spefscr & SPEFSCR_FOVF) && (fpexc_mode & PR_FP_EXC_OVF)) {
                code = FPE_FLTOVF;
-               spefscr |= SPEFSCR_FOVFS;
        }
        else if ((spefscr & SPEFSCR_FUNF) && (fpexc_mode & PR_FP_EXC_UND)) {
                code = FPE_FLTUND;
-               spefscr |= SPEFSCR_FUNFS;
        }
        else if ((spefscr & SPEFSCR_FDBZ) && (fpexc_mode & PR_FP_EXC_DIV))
                code = FPE_FLTDIV;
        else if ((spefscr & SPEFSCR_FINV) && (fpexc_mode & PR_FP_EXC_INV)) {
                code = FPE_FLTINV;
-               spefscr |= SPEFSCR_FINVS;
        }
        else if ((spefscr & (SPEFSCR_FG | SPEFSCR_FX)) && (fpexc_mode & PR_FP_EXC_RES))
                code = FPE_FLTRES;
 
-       current->thread.spefscr = spefscr;
+       err = do_spe_mathemu(regs);
+       if (err == 0) {
+               regs->nip += 4;         /* skip emulated instruction */
+               emulate_single_step(regs);
+               return;
+       }
+
+       if (err == -EFAULT) {
+               /* got an error reading the instruction */
+               _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip);
+       } else if (err == -EINVAL) {
+               /* didn't recognize the instruction */
+               printk(KERN_ERR "unrecognized spe instruction "
+                      "in %s at %lx\n", current->comm, regs->nip);
+       } else {
+               _exception(SIGFPE, regs, code, regs->nip);
+       }
 
-       _exception(SIGFPE, regs, code, regs->nip);
        return;
 }
+
+void SPEFloatingPointRoundException(struct pt_regs *regs)
+{
+       extern int speround_handler(struct pt_regs *regs);
+       int err;
+
+       preempt_disable();
+       if (regs->msr & MSR_SPE)
+               giveup_spe(current);
+       preempt_enable();
+
+       regs->nip -= 4;
+       err = speround_handler(regs);
+       if (err == 0) {
+               regs->nip += 4;         /* skip emulated instruction */
+               emulate_single_step(regs);
+               return;
+       }
+
+       if (err == -EFAULT) {
+               /* got an error reading the instruction */
+               _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip);
+       } else if (err == -EINVAL) {
+               /* didn't recognize the instruction */
+               printk(KERN_ERR "unrecognized spe instruction "
+                      "in %s at %lx\n", current->comm, regs->nip);
+       } else {
+               _exception(SIGFPE, regs, 0, regs->nip);
+               return;
+       }
+}
 #endif
 
 /*