Pull percpu-dtc into release branch
[pandora-kernel.git] / arch / powerpc / kernel / traps.c
index d9f10f2..f786222 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/kprobes.h>
 #include <linux/kexec.h>
 #include <linux/backlight.h>
+#include <linux/bug.h>
 
 #include <asm/kdebug.h>
 #include <asm/pgtable.h>
 #endif
 #include <asm/kexec.h>
 
-#ifdef CONFIG_PPC64    /* XXX */
-#define _IO_BASE       pci_io_base
-#endif
-
 #ifdef CONFIG_DEBUGGER
 int (*__debugger)(struct pt_regs *regs);
 int (*__debugger_ipi)(struct pt_regs *regs);
@@ -93,51 +90,80 @@ 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;
+       spin_unlock_irqrestore(&die.lock, flags);
 
        if (kexec_should_crash(current) ||
                kexec_sr_activated(smp_processor_id()))
@@ -150,6 +176,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;
@@ -177,7 +204,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_init(current)) {
                __sighandler_t handler;
 
                spin_lock_irq(&current->sighand->siglock);
@@ -241,7 +268,7 @@ void system_reset_exception(struct pt_regs *regs)
  */
 static inline int check_io_access(struct pt_regs *regs)
 {
-#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
+#ifdef CONFIG_PPC32
        unsigned long msr = regs->msr;
        const struct exception_table_entry *entry;
        unsigned int *nip = (unsigned int *)regs->nip;
@@ -274,7 +301,7 @@ static inline int check_io_access(struct pt_regs *regs)
                        return 1;
                }
        }
-#endif /* CONFIG_PPC_PMAC && CONFIG_PPC32 */
+#endif /* CONFIG_PPC32 */
        return 0;
 }
 
@@ -538,34 +565,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);
 }
@@ -731,54 +764,9 @@ static int emulate_instruction(struct pt_regs *regs)
        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
-
-struct bug_entry *find_bug(unsigned long bugaddr)
-{
-       struct bug_entry *bug;
-
-       for (bug = __start___bug_table; bug < __stop___bug_table; ++bug)
-               if (bugaddr == bug->bug_addr)
-                       return bug;
-       return module_find_bug(bugaddr);
-}
-
-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)
@@ -786,19 +774,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 */
@@ -812,7 +789,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) == BUG_TRAP_TYPE_WARN) {
                        regs->nip += 4;
                        return;
                }
@@ -822,6 +801,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)) {
@@ -843,7 +847,7 @@ void __kprobes program_check_exception(struct pt_regs *regs)
 
 void alignment_exception(struct pt_regs *regs)
 {
-       int fixed = 0;
+       int sig, code, fixed = 0;
 
        /* we don't implement logging of alignment exceptions */
        if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS))
@@ -857,14 +861,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)
@@ -900,14 +906,13 @@ 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);
@@ -934,18 +939,39 @@ void SoftwareEmulation(struct pt_regs *regs)
 
 #ifdef CONFIG_MATH_EMULATION
        errcode = do_mathemu(regs);
+
+       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;
+       }
+
 #else
        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;
+       }
+#endif
 }
 #endif /* CONFIG_8xx */