Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / arch / mips / kernel / traps.c
index 3530561..d612c6d 100644 (file)
 #include <linux/ptrace.h>
 #include <linux/kgdb.h>
 #include <linux/kdebug.h>
+#include <linux/notifier.h>
 
 #include <asm/bootinfo.h>
 #include <asm/branch.h>
 #include <asm/break.h>
+#include <asm/cop2.h>
 #include <asm/cpu.h>
 #include <asm/dsp.h>
 #include <asm/fpu.h>
@@ -47,6 +49,8 @@
 #include <asm/mmu_context.h>
 #include <asm/types.h>
 #include <asm/stacktrace.h>
+#include <asm/irq.h>
+#include <asm/uasm.h>
 
 extern void check_wait(void);
 extern asmlinkage void r4k_wait(void);
@@ -348,9 +352,10 @@ void show_registers(const struct pt_regs *regs)
 
 static DEFINE_SPINLOCK(die_lock);
 
-void __noreturn die(const char * str, const struct pt_regs * regs)
+void __noreturn die(const char * str, struct pt_regs * regs)
 {
        static int die_counter;
+       int sig = SIGSEGV;
 #ifdef CONFIG_MIPS_MT_SMTC
        unsigned long dvpret = dvpe();
 #endif /* CONFIG_MIPS_MT_SMTC */
@@ -361,6 +366,10 @@ void __noreturn die(const char * str, const struct pt_regs * regs)
 #ifdef CONFIG_MIPS_MT_SMTC
        mips_mt_regdump(dvpret);
 #endif /* CONFIG_MIPS_MT_SMTC */
+
+       if (notify_die(DIE_OOPS, str, regs, 0, current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
+               sig = 0;
+
        printk("%s[#%d]:\n", str, ++die_counter);
        show_registers(regs);
        add_taint(TAINT_DIE);
@@ -375,7 +384,7 @@ void __noreturn die(const char * str, const struct pt_regs * regs)
                panic("Fatal exception");
        }
 
-       do_exit(SIGSEGV);
+       do_exit(sig);
 }
 
 extern struct exception_table_entry __start___dbe_table[];
@@ -461,9 +470,8 @@ asmlinkage void do_be(struct pt_regs *regs)
  * The ll_bit is cleared by r*_switch.S
  */
 
-unsigned long ll_bit;
-
-static struct task_struct *ll_task = NULL;
+unsigned int ll_bit;
+struct task_struct *ll_task;
 
 static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode)
 {
@@ -853,6 +861,44 @@ static void mt_ase_fp_affinity(void)
 #endif /* CONFIG_MIPS_MT_FPAFF */
 }
 
+/*
+ * No lock; only written during early bootup by CPU 0.
+ */
+static RAW_NOTIFIER_HEAD(cu2_chain);
+
+int __ref register_cu2_notifier(struct notifier_block *nb)
+{
+       return raw_notifier_chain_register(&cu2_chain, nb);
+}
+
+int cu2_notifier_call_chain(unsigned long val, void *v)
+{
+       return raw_notifier_call_chain(&cu2_chain, val, v);
+}
+
+static int default_cu2_call(struct notifier_block *nfb, unsigned long action,
+        void *data)
+{
+       struct pt_regs *regs = data;
+
+       switch (action) {
+       default:
+               die_if_kernel("Unhandled kernel unaligned access or invalid "
+                             "instruction", regs);
+               /* Fall through  */
+
+       case CU2_EXCEPTION:
+               force_sig(SIGILL, current);
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block default_cu2_notifier = {
+       .notifier_call  = default_cu2_call,
+       .priority       = 0x80000000,           /* Run last  */
+};
+
 asmlinkage void do_cpu(struct pt_regs *regs)
 {
        unsigned int __user *epc;
@@ -860,6 +906,7 @@ asmlinkage void do_cpu(struct pt_regs *regs)
        unsigned int opcode;
        unsigned int cpid;
        int status;
+       unsigned long __maybe_unused flags;
 
        die_if_kernel("do_cpu invoked from kernel context!", regs);
 
@@ -915,6 +962,9 @@ asmlinkage void do_cpu(struct pt_regs *regs)
                return;
 
        case 2:
+               raw_notifier_call_chain(&cu2_chain, CU2_EXCEPTION, regs);
+               break;
+
        case 3:
                break;
        }
@@ -927,6 +977,9 @@ asmlinkage void do_mdmx(struct pt_regs *regs)
        force_sig(SIGILL, current);
 }
 
+/*
+ * Called with interrupts disabled.
+ */
 asmlinkage void do_watch(struct pt_regs *regs)
 {
        u32 cause;
@@ -946,9 +999,12 @@ asmlinkage void do_watch(struct pt_regs *regs)
         */
        if (test_tsk_thread_flag(current, TIF_LOAD_WATCH)) {
                mips_read_watch_registers();
+               local_irq_enable();
                force_sig(SIGTRAP, current);
-       } else
+       } else {
                mips_clear_watch_registers();
+               local_irq_enable();
+       }
 }
 
 asmlinkage void do_mcheck(struct pt_regs *regs)
@@ -1221,21 +1277,25 @@ unsigned long ebase;
 unsigned long exception_handlers[32];
 unsigned long vi_handlers[64];
 
-/*
- * As a side effect of the way this is implemented we're limited
- * to interrupt handlers in the address range from
- * KSEG0 <= x < KSEG0 + 256mb on the Nevada.  Oh well ...
- */
-void *set_except_vector(int n, void *addr)
+void __init *set_except_vector(int n, void *addr)
 {
        unsigned long handler = (unsigned long) addr;
        unsigned long old_handler = exception_handlers[n];
 
        exception_handlers[n] = handler;
        if (n == 0 && cpu_has_divec) {
-               *(u32 *)(ebase + 0x200) = 0x08000000 |
-                                         (0x03ffffff & (handler >> 2));
-               local_flush_icache_range(ebase + 0x200, ebase + 0x204);
+               unsigned long jump_mask = ~((1 << 28) - 1);
+               u32 *buf = (u32 *)(ebase + 0x200);
+               unsigned int k0 = 26;
+               if ((handler & jump_mask) == ((ebase + 0x200) & jump_mask)) {
+                       uasm_i_j(&buf, handler & ~jump_mask);
+                       uasm_i_nop(&buf);
+               } else {
+                       UASM_i_LA(&buf, k0, handler);
+                       uasm_i_jr(&buf, k0);
+                       uasm_i_nop(&buf);
+               }
+               local_flush_icache_range(ebase + 0x200, (unsigned long)buf);
        }
        return (void *)old_handler;
 }
@@ -1254,8 +1314,7 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
        u32 *w;
        unsigned char *b;
 
-       if (!cpu_has_veic && !cpu_has_vint)
-               BUG();
+       BUG_ON(!cpu_has_veic && !cpu_has_vint);
 
        if (addr == NULL) {
                handler = (unsigned long) do_default_vi;
@@ -1346,77 +1405,6 @@ void *set_vi_handler(int n, vi_handler_t addr)
        return set_vi_srs_handler(n, addr, 0);
 }
 
-/*
- * This is used by native signal handling
- */
-asmlinkage int (*save_fp_context)(struct sigcontext __user *sc);
-asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc);
-
-extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
-extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);
-
-extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc);
-extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc);
-
-#ifdef CONFIG_SMP
-static int smp_save_fp_context(struct sigcontext __user *sc)
-{
-       return raw_cpu_has_fpu
-              ? _save_fp_context(sc)
-              : fpu_emulator_save_context(sc);
-}
-
-static int smp_restore_fp_context(struct sigcontext __user *sc)
-{
-       return raw_cpu_has_fpu
-              ? _restore_fp_context(sc)
-              : fpu_emulator_restore_context(sc);
-}
-#endif
-
-static inline void signal_init(void)
-{
-#ifdef CONFIG_SMP
-       /* For now just do the cpu_has_fpu check when the functions are invoked */
-       save_fp_context = smp_save_fp_context;
-       restore_fp_context = smp_restore_fp_context;
-#else
-       if (cpu_has_fpu) {
-               save_fp_context = _save_fp_context;
-               restore_fp_context = _restore_fp_context;
-       } else {
-               save_fp_context = fpu_emulator_save_context;
-               restore_fp_context = fpu_emulator_restore_context;
-       }
-#endif
-}
-
-#ifdef CONFIG_MIPS32_COMPAT
-
-/*
- * This is used by 32-bit signal stuff on the 64-bit kernel
- */
-asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc);
-asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc);
-
-extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc);
-extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc);
-
-extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc);
-extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc);
-
-static inline void signal32_init(void)
-{
-       if (cpu_has_fpu) {
-               save_fp_context32 = _save_fp_context32;
-               restore_fp_context32 = _restore_fp_context32;
-       } else {
-               save_fp_context32 = fpu_emulator_save_context32;
-               restore_fp_context32 = fpu_emulator_restore_context32;
-       }
-}
-#endif
-
 extern void cpu_cache_init(void);
 extern void tlb_init(void);
 extern void flush_tlb_handlers(void);
@@ -1425,6 +1413,7 @@ extern void flush_tlb_handlers(void);
  * Timer interrupt
  */
 int cp0_compare_irq;
+int cp0_compare_irq_shift;
 
 /*
  * Performance counter IRQ or -1 if shared with timer
@@ -1480,7 +1469,7 @@ void __cpuinit per_cpu_trap_init(void)
                         status_set);
 
        if (cpu_has_mips_r2) {
-               unsigned int enable = 0x0000000f;
+               unsigned int enable = 0x0000000f | cpu_hwrena_impl_bits;
 
                if (!noulri && cpu_has_userlocal)
                        enable |= (1 << 29);
@@ -1493,7 +1482,9 @@ void __cpuinit per_cpu_trap_init(void)
 #endif /* CONFIG_MIPS_MT_SMTC */
 
        if (cpu_has_veic || cpu_has_vint) {
+               unsigned long sr = set_c0_status(ST0_BEV);
                write_c0_ebase(ebase);
+               write_c0_status(sr);
                /* Setting vector spacing enables EI/VI mode  */
                change_c0_intctl(0x3e0, VECTORSPACING);
        }
@@ -1513,12 +1504,14 @@ void __cpuinit per_cpu_trap_init(void)
         *  o read IntCtl.IPPCI to determine the performance counter interrupt
         */
        if (cpu_has_mips_r2) {
-               cp0_compare_irq = (read_c0_intctl() >> 29) & 7;
-               cp0_perfcount_irq = (read_c0_intctl() >> 26) & 7;
+               cp0_compare_irq_shift = CAUSEB_TI - CAUSEB_IP;
+               cp0_compare_irq = (read_c0_intctl() >> INTCTLB_IPTI) & 7;
+               cp0_perfcount_irq = (read_c0_intctl() >> INTCTLB_IPPCI) & 7;
                if (cp0_perfcount_irq == cp0_compare_irq)
                        cp0_perfcount_irq = -1;
        } else {
                cp0_compare_irq = CP0_LEGACY_COMPARE_IRQ;
+               cp0_compare_irq_shift = cp0_compare_irq;
                cp0_perfcount_irq = -1;
        }
 
@@ -1561,18 +1554,15 @@ void __init set_handler(unsigned long offset, void *addr, unsigned long size)
 static char panic_null_cerr[] __cpuinitdata =
        "Trying to set NULL cache error exception handler";
 
-/* Install uncached CPU exception handler */
+/*
+ * Install uncached CPU exception handler.
+ * This is suitable only for the cache error exception which is the only
+ * exception handler that is being run uncached.
+ */
 void __cpuinit set_uncached_handler(unsigned long offset, void *addr,
        unsigned long size)
 {
-#ifdef CONFIG_32BIT
-       unsigned long uncached_ebase = KSEG1ADDR(ebase);
-#endif
-#ifdef CONFIG_64BIT
-       unsigned long uncached_ebase = TO_UNCAC(ebase);
-#endif
-       if (cpu_has_mips_r2)
-               ebase += (read_c0_ebase() & 0x3ffff000);
+       unsigned long uncached_ebase = CKSEG1ADDR(ebase);
 
        if (!addr)
                panic(panic_null_cerr);
@@ -1604,10 +1594,12 @@ void __init trap_init(void)
                return; /* Already done */
 #endif
 
-       if (cpu_has_veic || cpu_has_vint)
-               ebase = (unsigned long) alloc_bootmem_low_pages(0x200 + VECTORSPACING*64);
-       else {
-               ebase = CAC_BASE;
+       if (cpu_has_veic || cpu_has_vint) {
+               unsigned long size = 0x200 + VECTORSPACING*64;
+               ebase = (unsigned long)
+                       __alloc_bootmem(size, 1 << fls(size), 0);
+       } else {
+               ebase = CKSEG0;
                if (cpu_has_mips_r2)
                        ebase += (read_c0_ebase() & 0x3ffff000);
        }
@@ -1724,13 +1716,10 @@ void __init trap_init(void)
        else
                memcpy((void *)(ebase + 0x080), &except_vec3_generic, 0x80);
 
-       signal_init();
-#ifdef CONFIG_MIPS32_COMPAT
-       signal32_init();
-#endif
-
        local_flush_icache_range(ebase, ebase + 0x400);
        flush_tlb_handlers();
 
        sort_extable(__start___dbe_table, __stop___dbe_table);
+
+       register_cu2_notifier(&default_cu2_notifier);
 }