[POWERPC] Make default cputable entries reflect selected CPU family
[safe/jmp/linux-2.6] / arch / powerpc / kernel / traps.c
index dcc6f15..4b5b7ff 100644 (file)
@@ -33,8 +33,8 @@
 #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>
@@ -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);
@@ -72,69 +72,85 @@ EXPORT_SYMBOL(__debugger_dabr_match);
 EXPORT_SYMBOL(__debugger_fault_handler);
 #endif
 
-ATOMIC_NOTIFIER_HEAD(powerpc_die_chain);
-
-int register_die_notifier(struct notifier_block *nb)
-{
-       return atomic_notifier_chain_register(&powerpc_die_chain, nb);
-}
-EXPORT_SYMBOL(register_die_notifier);
-
-int unregister_die_notifier(struct notifier_block *nb)
-{
-       return atomic_notifier_chain_unregister(&powerpc_die_chain, nb);
-}
-EXPORT_SYMBOL(unregister_die_notifier);
-
 /*
  * 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 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
-       mutex_lock(&pmac_backlight_mutex);
-       if (machine_is(powermac) && pmac_backlight) {
-               struct backlight_properties *props;
+       oops_enter();
 
-               down(&pmac_backlight->sem);
-               props = pmac_backlight->props;
-               props->brightness = props->max_brightness;
-               props->power = FB_BLANK_UNBLANK;
-               props->update_status(pmac_backlight);
-               up(&pmac_backlight->sem);
+       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);
        }
-       mutex_unlock(&pmac_backlight_mutex);
-#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 ");
+               printk("PREEMPT ");
 #endif
 #ifdef CONFIG_SMP
-       printk("SMP NR_CPUS=%d ", NR_CPUS);
+               printk("SMP NR_CPUS=%d ", NR_CPUS);
 #endif
 #ifdef CONFIG_DEBUG_PAGEALLOC
-       printk("DEBUG_PAGEALLOC ");
+               printk("DEBUG_PAGEALLOC ");
 #endif
 #ifdef CONFIG_NUMA
-       printk("NUMA ");
+               printk("NUMA ");
 #endif
-       printk("%s\n", ppc_md.name ? "" : ppc_md.name);
+               printk("%s\n", ppc_md.name ? ppc_md.name : "");
+
+               print_modules();
+               show_regs(regs);
+       } else {
+               printk("Recursive die() failure, output suppressed\n");
+       }
 
-       print_modules();
-       show_regs(regs);
        bust_spinlocks(0);
-       spin_unlock_irq(&die_lock);
+       die.lock_owner = -1;
+       add_taint(TAINT_DIE);
+       spin_unlock_irqrestore(&die.lock, flags);
 
        if (kexec_should_crash(current) ||
                kexec_sr_activated(smp_processor_id()))
@@ -147,6 +163,7 @@ int die(const char *str, struct pt_regs *regs, long err)
        if (panic_on_oops)
                panic("Fatal exception");
 
+       oops_exit();
        do_exit(err);
 
        return 0;
@@ -155,11 +172,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;
@@ -174,7 +201,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 (is_init(current)) {
+       if (is_global_init(current)) {
                __sighandler_t handler;
 
                spin_lock_irq(&current->sighand->siglock);
@@ -282,7 +309,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)
@@ -307,55 +334,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)
 {
-       int recover = 0;
        unsigned long reason = get_mc_reason(regs);
 
-       /* See if any machine dependent calls */
-       if (ppc_md.machine_check_exception)
-               recover = ppc_md.machine_check_exception(regs);
-
-       if (recover)
-               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 */
-       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");
@@ -385,7 +382,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);
 
@@ -397,8 +400,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)
@@ -415,7 +416,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);
 
@@ -433,7 +441,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) {
@@ -463,13 +478,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.
         */
-       platform_machine_check(regs);
+       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
+        */
+       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;
@@ -604,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;
@@ -689,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;
@@ -731,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;
 }
 
@@ -761,7 +840,7 @@ void __kprobes program_check_exception(struct pt_regs *regs)
                        return;
 
                if (!(regs->msr & MSR_PR) &&  /* not user-mode */
-                   report_bug(regs->nip) == BUG_TRAP_TYPE_WARN) {
+                   report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
                        regs->nip += 4;
                        return;
                }
@@ -863,7 +942,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());
 }
 
@@ -898,7 +977,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);
 
@@ -928,7 +1009,7 @@ void SoftwareEmulation(struct pt_regs *regs)
                return;
        }
 
-#else
+#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
        errcode = Soft_emulate_8xx(regs);
        switch (errcode) {
        case 0:
@@ -941,6 +1022,8 @@ void SoftwareEmulation(struct pt_regs *regs)
                _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
                return;
        }
+#else
+       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
 #endif
 }
 #endif /* CONFIG_8xx */