Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfashe...
[pandora-kernel.git] / arch / powerpc / kernel / traps.c
index 59c464e..878fbdd 100644 (file)
@@ -54,7 +54,7 @@
 #endif
 #include <asm/kexec.h>
 
-#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);
@@ -334,18 +334,25 @@ static inline int check_io_access(struct pt_regs *regs)
 #define clear_single_step(regs)        ((regs)->msr &= ~MSR_SE)
 #endif
 
-static int generic_machine_check_exception(struct pt_regs *regs)
+#if defined(CONFIG_4xx)
+int machine_check_4xx(struct pt_regs *regs)
 {
        unsigned long reason = get_mc_reason(regs);
 
-#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");
@@ -375,7 +382,13 @@ static int generic_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);
 
@@ -403,7 +416,14 @@ static int generic_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);
 
@@ -421,7 +441,14 @@ static int generic_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) {
@@ -451,22 +478,26 @@ static int generic_machine_check_exception(struct pt_regs *regs)
        default:
                printk("Unknown values in msr\n");
        }
-#endif /* CONFIG_4xx */
-
        return 0;
 }
+#endif /* everything else */
 
 void machine_check_exception(struct pt_regs *regs)
 {
        int recover = 0;
 
-       /* See if any machine dependent calls */
+       /* 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
-               recover = generic_machine_check_exception(regs);
+       else if (cur_cpu_spec->machine_check)
+               recover = cur_cpu_spec->machine_check(regs);
 
-       if (recover)
+       if (recover > 0)
                return;
 
        if (user_mode(regs)) {
@@ -476,7 +507,12 @@ void machine_check_exception(struct pt_regs *regs)
        }
 
 #if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-       /* the qspan pci read routines can cause machine checks -- Cort */
+       /* 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
+        */
        bad_page_fault(regs, regs->dar, SIGBUS);
        return;
 #endif
@@ -622,6 +658,9 @@ static void parse_fpe(struct pt_regs *regs)
 #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;
@@ -707,6 +746,23 @@ static int emulate_popcntb_inst(struct pt_regs *regs, u32 instword)
        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;
@@ -749,6 +805,11 @@ static int emulate_instruction(struct pt_regs *regs)
                return emulate_popcntb_inst(regs, instword);
        }
 
+       /* Emulate isel (Integer Select) instruction */
+       if ((instword & INST_ISEL_MASK) == INST_ISEL) {
+               return emulate_isel(regs, instword);
+       }
+
        return -EINVAL;
 }
 
@@ -906,6 +967,20 @@ void altivec_unavailable_exception(struct pt_regs *regs)
        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);
@@ -969,21 +1044,29 @@ void SoftwareEmulation(struct pt_regs *regs)
 
 #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;
-               } 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;
                }
-               _exception(SIGTRAP, regs, TRAP_TRACE, 0);
+
+               _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
        }
 }
 #endif /* CONFIG_4xx || CONFIG_BOOKE */
@@ -1030,6 +1113,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)