Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / mips / kernel / traps.c
index 56c36e4..a7564b0 100644 (file)
@@ -3,13 +3,13 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 1994 - 1999, 2000, 01 Ralf Baechle
+ * Copyright (C) 1994 - 1999, 2000, 01, 06 Ralf Baechle
  * Copyright (C) 1995, 1996 Paul M. Antoine
  * Copyright (C) 1998 Ulf Carlsson
  * Copyright (C) 1999 Silicon Graphics, Inc.
  * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
  * Copyright (C) 2000, 01 MIPS Technologies, Inc.
- * Copyright (C) 2002, 2003, 2004  Maciej W. Rozycki
+ * Copyright (C) 2002, 2003, 2004, 2005  Maciej W. Rozycki
  */
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 #include <linux/spinlock.h>
 #include <linux/kallsyms.h>
+#include <linux/bootmem.h>
 
 #include <asm/bootinfo.h>
 #include <asm/branch.h>
 #include <asm/break.h>
 #include <asm/cpu.h>
+#include <asm/dsp.h>
 #include <asm/fpu.h>
+#include <asm/mipsregs.h>
+#include <asm/mipsmtregs.h>
 #include <asm/module.h>
 #include <asm/pgtable.h>
 #include <asm/ptrace.h>
@@ -38,6 +42,7 @@
 #include <asm/watch.h>
 #include <asm/types.h>
 
+extern asmlinkage void handle_int(void);
 extern asmlinkage void handle_tlbm(void);
 extern asmlinkage void handle_tlbl(void);
 extern asmlinkage void handle_tlbs(void);
@@ -54,14 +59,19 @@ extern asmlinkage void handle_tr(void);
 extern asmlinkage void handle_fpe(void);
 extern asmlinkage void handle_mdmx(void);
 extern asmlinkage void handle_watch(void);
+extern asmlinkage void handle_mt(void);
+extern asmlinkage void handle_dsp(void);
 extern asmlinkage void handle_mcheck(void);
 extern asmlinkage void handle_reserved(void);
 
-extern int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp,
+extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
        struct mips_fpu_soft_struct *ctx);
 
 void (*board_be_init)(void);
 int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
+void (*board_nmi_handler_setup)(void);
+void (*board_ejtag_handler_setup)(void);
+void (*board_bind_eic_interrupt)(int irq, int regset);
 
 /*
  * These constant is for searching for possible module text segments.
@@ -201,32 +211,47 @@ void show_regs(struct pt_regs *regs)
 
        printk("Status: %08x    ", (uint32_t) regs->cp0_status);
 
-       if (regs->cp0_status & ST0_KX)
-               printk("KX ");
-       if (regs->cp0_status & ST0_SX)
-               printk("SX ");
-       if (regs->cp0_status & ST0_UX)
-               printk("UX ");
-       switch (regs->cp0_status & ST0_KSU) {
-       case KSU_USER:
-               printk("USER ");
-               break;
-       case KSU_SUPERVISOR:
-               printk("SUPERVISOR ");
-               break;
-       case KSU_KERNEL:
-               printk("KERNEL ");
-               break;
-       default:
-               printk("BAD_MODE ");
-               break;
+       if (current_cpu_data.isa_level == MIPS_CPU_ISA_I) {
+               if (regs->cp0_status & ST0_KUO)
+                       printk("KUo ");
+               if (regs->cp0_status & ST0_IEO)
+                       printk("IEo ");
+               if (regs->cp0_status & ST0_KUP)
+                       printk("KUp ");
+               if (regs->cp0_status & ST0_IEP)
+                       printk("IEp ");
+               if (regs->cp0_status & ST0_KUC)
+                       printk("KUc ");
+               if (regs->cp0_status & ST0_IEC)
+                       printk("IEc ");
+       } else {
+               if (regs->cp0_status & ST0_KX)
+                       printk("KX ");
+               if (regs->cp0_status & ST0_SX)
+                       printk("SX ");
+               if (regs->cp0_status & ST0_UX)
+                       printk("UX ");
+               switch (regs->cp0_status & ST0_KSU) {
+               case KSU_USER:
+                       printk("USER ");
+                       break;
+               case KSU_SUPERVISOR:
+                       printk("SUPERVISOR ");
+                       break;
+               case KSU_KERNEL:
+                       printk("KERNEL ");
+                       break;
+               default:
+                       printk("BAD_MODE ");
+                       break;
+               }
+               if (regs->cp0_status & ST0_ERL)
+                       printk("ERL ");
+               if (regs->cp0_status & ST0_EXL)
+                       printk("EXL ");
+               if (regs->cp0_status & ST0_IE)
+                       printk("IE ");
        }
-       if (regs->cp0_status & ST0_ERL)
-               printk("ERL ");
-       if (regs->cp0_status & ST0_EXL)
-               printk("EXL ");
-       if (regs->cp0_status & ST0_IE)
-               printk("IE ");
        printk("\n");
 
        printk("Cause : %08x\n", cause);
@@ -252,29 +277,25 @@ void show_registers(struct pt_regs *regs)
 
 static DEFINE_SPINLOCK(die_lock);
 
-NORET_TYPE void __die(const char * str, struct pt_regs * regs,
-       const char * file, const char * func, unsigned long line)
+NORET_TYPE void ATTRIB_NORET die(const char * str, struct pt_regs * regs)
 {
        static int die_counter;
+#ifdef CONFIG_MIPS_MT_SMTC
+       unsigned long dvpret = dvpe();
+#endif /* CONFIG_MIPS_MT_SMTC */
 
        console_verbose();
        spin_lock_irq(&die_lock);
-       printk("%s", str);
-       if (file && func)
-               printk(" in %s:%s, line %ld", file, func, line);
-       printk("[#%d]:\n", ++die_counter);
+       bust_spinlocks(1);
+#ifdef CONFIG_MIPS_MT_SMTC
+       mips_mt_regdump(dvpret);
+#endif /* CONFIG_MIPS_MT_SMTC */
+       printk("%s[#%d]:\n", str, ++die_counter);
        show_registers(regs);
        spin_unlock_irq(&die_lock);
        do_exit(SIGSEGV);
 }
 
-void __die_if_kernel(const char * str, struct pt_regs * regs,
-                    const char * file, const char * func, unsigned long line)
-{
-       if (!user_mode(regs))
-               __die(str, regs, file, func, line);
-}
-
 extern const struct exception_table_entry __start___dbe_table[];
 extern const struct exception_table_entry __stop___dbe_table[];
 
@@ -339,9 +360,9 @@ asmlinkage void do_be(struct pt_regs *regs)
 
 static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode)
 {
-       unsigned int *epc;
+       unsigned int __user *epc;
 
-       epc = (unsigned int *) regs->cp0_epc +
+       epc = (unsigned int __user *) regs->cp0_epc +
              ((regs->cp0_cause & CAUSEF_BD) != 0);
        if (!get_user(*opcode, epc))
                return 0;
@@ -360,6 +381,10 @@ static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode)
 #define OFFSET 0x0000ffff
 #define LL     0xc0000000
 #define SC     0xe0000000
+#define SPEC3  0x7c000000
+#define RD     0x0000f800
+#define FUNC   0x0000003f
+#define RDHWR  0x0000003b
 
 /*
  * The ll_bit is cleared by r*_switch.S
@@ -371,7 +396,7 @@ static struct task_struct *ll_task = NULL;
 
 static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode)
 {
-       unsigned long value, *vaddr;
+       unsigned long value, __user *vaddr;
        long offset;
        int signal = 0;
 
@@ -385,7 +410,8 @@ static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode)
        offset <<= 16;
        offset >>= 16;
 
-       vaddr = (unsigned long *)((long)(regs->regs[(opcode & BASE) >> 21]) + offset);
+       vaddr = (unsigned long __user *)
+               ((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset);
 
        if ((unsigned long)vaddr & 3) {
                signal = SIGBUS;
@@ -407,9 +433,10 @@ static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode)
 
        preempt_enable();
 
+       compute_return_epc(regs);
+
        regs->regs[(opcode & RT) >> 16] = value;
 
-       compute_return_epc(regs);
        return;
 
 sig:
@@ -418,7 +445,8 @@ sig:
 
 static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode)
 {
-       unsigned long *vaddr, reg;
+       unsigned long __user *vaddr;
+       unsigned long reg;
        long offset;
        int signal = 0;
 
@@ -432,7 +460,8 @@ static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode)
        offset <<= 16;
        offset >>= 16;
 
-       vaddr = (unsigned long *)((long)(regs->regs[(opcode & BASE) >> 21]) + offset);
+       vaddr = (unsigned long __user *)
+               ((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset);
        reg = (opcode & RT) >> 16;
 
        if ((unsigned long)vaddr & 3) {
@@ -443,9 +472,9 @@ static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode)
        preempt_disable();
 
        if (ll_bit == 0 || ll_task != current) {
+               compute_return_epc(regs);
                regs->regs[reg] = 0;
                preempt_enable();
-               compute_return_epc(regs);
                return;
        }
 
@@ -456,9 +485,9 @@ static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode)
                goto sig;
        }
 
+       compute_return_epc(regs);
        regs->regs[reg] = 1;
 
-       compute_return_epc(regs);
        return;
 
 sig:
@@ -491,14 +520,48 @@ static inline int simulate_llsc(struct pt_regs *regs)
        return -EFAULT;                 /* Strange things going on ... */
 }
 
+/*
+ * Simulate trapping 'rdhwr' instructions to provide user accessible
+ * registers not implemented in hardware.  The only current use of this
+ * is the thread area pointer.
+ */
+static inline int simulate_rdhwr(struct pt_regs *regs)
+{
+       struct thread_info *ti = task_thread_info(current);
+       unsigned int opcode;
+
+       if (unlikely(get_insn_opcode(regs, &opcode)))
+               return -EFAULT;
+
+       if (unlikely(compute_return_epc(regs)))
+               return -EFAULT;
+
+       if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) {
+               int rd = (opcode & RD) >> 11;
+               int rt = (opcode & RT) >> 16;
+               switch (rd) {
+                       case 29:
+                               regs->regs[rt] = ti->tp_value;
+                               return 0;
+                       default:
+                               return -EFAULT;
+               }
+       }
+
+       /* Not ours.  */
+       return -EFAULT;
+}
+
 asmlinkage void do_ov(struct pt_regs *regs)
 {
        siginfo_t info;
 
+       die_if_kernel("Integer overflow", regs);
+
        info.si_code = FPE_INTOVF;
        info.si_signo = SIGFPE;
        info.si_errno = 0;
-       info.si_addr = (void *)regs->cp0_epc;
+       info.si_addr = (void __user *) regs->cp0_epc;
        force_sig_info(SIGFPE, &info, current);
 }
 
@@ -512,8 +575,16 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
 
                preempt_disable();
 
+#ifdef CONFIG_PREEMPT
+               if (!is_fpu_owner()) {
+                       /* We might lose fpu before disabling preempt... */
+                       own_fpu();
+                       BUG_ON(!used_math());
+                       restore_fp(current);
+               }
+#endif
                /*
-                * Unimplemented operation exception.  If we've got the full
+                * Unimplemented operation exception.  If we've got the full
                 * software emulator on-board, let's use it...
                 *
                 * Force FPU to dump state into task/thread context.  We're
@@ -523,11 +594,18 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
                 * a bit extreme for what should be an infrequent event.
                 */
                save_fp(current);
+               /* Ensure 'resume' not overwrite saved fp context again. */
+               lose_fpu();
+
+               preempt_enable();
 
                /* Run the emulator */
-               sig = fpu_emulator_cop1Handler (0, regs,
+               sig = fpu_emulator_cop1Handler (regs,
                        &current->thread.fpu.soft);
 
+               preempt_disable();
+
+               own_fpu();      /* Using the FPU again.  */
                /*
                 * We can't allow the emulated instruction to leave any of
                 * the cause bit set in $fcr31.
@@ -584,7 +662,7 @@ asmlinkage void do_bp(struct pt_regs *regs)
                        info.si_code = FPE_INTOVF;
                info.si_signo = SIGFPE;
                info.si_errno = 0;
-               info.si_addr = (void *)regs->cp0_epc;
+               info.si_addr = (void __user *) regs->cp0_epc;
                force_sig_info(SIGFPE, &info, current);
                break;
        default:
@@ -621,7 +699,7 @@ asmlinkage void do_tr(struct pt_regs *regs)
                        info.si_code = FPE_INTOVF;
                info.si_signo = SIGFPE;
                info.si_errno = 0;
-               info.si_addr = (void *)regs->cp0_epc;
+               info.si_addr = (void __user *) regs->cp0_epc;
                force_sig_info(SIGFPE, &info, current);
                break;
        default:
@@ -637,6 +715,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
                if (!simulate_llsc(regs))
                        return;
 
+       if (!simulate_rdhwr(regs))
+               return;
+
        force_sig(SIGILL, current);
 }
 
@@ -650,11 +731,13 @@ asmlinkage void do_cpu(struct pt_regs *regs)
 
        switch (cpid) {
        case 0:
-               if (cpu_has_llsc)
-                       break;
+               if (!cpu_has_llsc)
+                       if (!simulate_llsc(regs))
+                               return;
 
-               if (!simulate_llsc(regs))
+               if (!simulate_rdhwr(regs))
                        return;
+
                break;
 
        case 1:
@@ -668,19 +751,50 @@ asmlinkage void do_cpu(struct pt_regs *regs)
                        set_used_math();
                }
 
+               preempt_enable();
+
                if (!cpu_has_fpu) {
-                       int sig = fpu_emulator_cop1Handler(0, regs,
+                       int sig = fpu_emulator_cop1Handler(regs,
                                                &current->thread.fpu.soft);
                        if (sig)
                                force_sig(sig, current);
+#ifdef CONFIG_MIPS_MT_FPAFF
+                       else {
+                       /*
+                        * MIPS MT processors may have fewer FPU contexts
+                        * than CPU threads. If we've emulated more than
+                        * some threshold number of instructions, force
+                        * migration to a "CPU" that has FP support.
+                        */
+                        if(mt_fpemul_threshold > 0
+                        && ((current->thread.emulated_fp++
+                           > mt_fpemul_threshold))) {
+                         /*
+                          * If there's no FPU present, or if the
+                          * application has already restricted
+                          * the allowed set to exclude any CPUs
+                          * with FPUs, we'll skip the procedure.
+                          */
+                         if (cpus_intersects(current->cpus_allowed,
+                                               mt_fpu_cpumask)) {
+                           cpumask_t tmask;
+
+                           cpus_and(tmask,
+                                       current->thread.user_cpus_allowed,
+                                       mt_fpu_cpumask);
+                           set_cpus_allowed(current, tmask);
+                           current->thread.mflags |= MF_FPUBOUND;
+                         }
+                        }
+                       }
+#endif /* CONFIG_MIPS_MT_FPAFF */
                }
 
-               preempt_enable();
-
                return;
 
        case 2:
        case 3:
+               die_if_kernel("do_cpu invoked from kernel context!", regs);
                break;
        }
 
@@ -705,15 +819,76 @@ asmlinkage void do_watch(struct pt_regs *regs)
 
 asmlinkage void do_mcheck(struct pt_regs *regs)
 {
+       const int field = 2 * sizeof(unsigned long);
+       int multi_match = regs->cp0_status & ST0_TS;
+
        show_regs(regs);
-       dump_tlb_all();
+
+       if (multi_match) {
+               printk("Index   : %0x\n", read_c0_index());
+               printk("Pagemask: %0x\n", read_c0_pagemask());
+               printk("EntryHi : %0*lx\n", field, read_c0_entryhi());
+               printk("EntryLo0: %0*lx\n", field, read_c0_entrylo0());
+               printk("EntryLo1: %0*lx\n", field, read_c0_entrylo1());
+               printk("\n");
+               dump_tlb_all();
+       }
+
+       show_code((unsigned int *) regs->cp0_epc);
+
        /*
         * Some chips may have other causes of machine check (e.g. SB1
         * graduation timer)
         */
        panic("Caught Machine Check exception - %scaused by multiple "
              "matching entries in the TLB.",
-             (regs->cp0_status & ST0_TS) ? "" : "not ");
+             (multi_match) ? "" : "not ");
+}
+
+asmlinkage void do_mt(struct pt_regs *regs)
+{
+       int subcode;
+
+       die_if_kernel("MIPS MT Thread exception in kernel", regs);
+
+       subcode = (read_vpe_c0_vpecontrol() & VPECONTROL_EXCPT)
+                       >> VPECONTROL_EXCPT_SHIFT;
+       switch (subcode) {
+       case 0:
+               printk(KERN_ERR "Thread Underflow\n");
+               break;
+       case 1:
+               printk(KERN_ERR "Thread Overflow\n");
+               break;
+       case 2:
+               printk(KERN_ERR "Invalid YIELD Qualifier\n");
+               break;
+       case 3:
+               printk(KERN_ERR "Gating Storage Exception\n");
+               break;
+       case 4:
+               printk(KERN_ERR "YIELD Scheduler Exception\n");
+               break;
+       case 5:
+               printk(KERN_ERR "Gating Storage Schedulier Exception\n");
+               break;
+       default:
+               printk(KERN_ERR "*** UNKNOWN THREAD EXCEPTION %d ***\n",
+                       subcode);
+               break;
+       }
+       die_if_kernel("MIPS MT Thread exception in kernel", regs);
+
+       force_sig(SIGILL, current);
+}
+
+
+asmlinkage void do_dsp(struct pt_regs *regs)
+{
+       if (cpu_has_dsp)
+               panic("Unexpected DSP exception\n");
+
+       force_sig(SIGILL, current);
 }
 
 asmlinkage void do_reserved(struct pt_regs *regs)
@@ -728,6 +903,12 @@ asmlinkage void do_reserved(struct pt_regs *regs)
              (regs->cp0_cause & 0x7f) >> 2);
 }
 
+asmlinkage void do_default_vi(struct pt_regs *regs)
+{
+       show_regs(regs);
+       panic("Caught unexpected vectored interrupt.");
+}
+
 /*
  * Some MIPS CPUs can enable/disable for cache parity detection, but do
  * it different ways.
@@ -736,16 +917,13 @@ static inline void parity_protection_init(void)
 {
        switch (current_cpu_data.cputype) {
        case CPU_24K:
-               /* 24K cache parity not currently implemented in FPGA */
-               printk(KERN_INFO "Disable cache parity protection for "
-                      "MIPS 24K CPU.\n");
-               write_c0_ecc(read_c0_ecc() & ~0x80000000);
-               break;
+       case CPU_34K:
        case CPU_5KC:
-               /* Set the PE bit (bit 31) in the c0_ecc register. */
-               printk(KERN_INFO "Enable cache parity protection for "
-                      "MIPS 5KC/24K CPUs.\n");
-               write_c0_ecc(read_c0_ecc() | 0x80000000);
+               write_c0_ecc(0x80000000);
+               back_to_back_c0_hazard();
+               /* Set the PE bit (bit 31) in the c0_errctl register. */
+               printk(KERN_INFO "Cache parity protection %sabled\n",
+                      (read_c0_ecc() & 0x80000000) ? "en" : "dis");
                break;
        case CPU_20KC:
        case CPU_25KF:
@@ -783,7 +961,7 @@ asmlinkage void cache_parity_error(void)
               reg_val & (1<<22) ? "E0 " : "");
        printk("IDX: 0x%08x\n", reg_val & ((1<<22)-1));
 
-#if defined(CONFIG_CPU_MIPS32) || defined (CONFIG_CPU_MIPS64)
+#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64)
        if (reg_val & (1<<22))
                printk("DErrAddr0: 0x%0*lx\n", field, read_c0_derraddr0());
 
@@ -835,12 +1013,24 @@ void ejtag_exception_handler(struct pt_regs *regs)
  */
 void nmi_exception_handler(struct pt_regs *regs)
 {
+#ifdef CONFIG_MIPS_MT_SMTC
+       unsigned long dvpret = dvpe();
+       bust_spinlocks(1);
        printk("NMI taken!!!!\n");
+       mips_mt_regdump(dvpret);
+#else
+       bust_spinlocks(1);
+       printk("NMI taken!!!!\n");
+#endif /* CONFIG_MIPS_MT_SMTC */
        die("NMI", regs);
        while(1) ;
 }
 
+#define VECTORSPACING 0x100    /* for EI/VI mode */
+
+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
@@ -854,13 +1044,163 @@ void *set_except_vector(int n, void *addr)
 
        exception_handlers[n] = handler;
        if (n == 0 && cpu_has_divec) {
-               *(volatile u32 *)(CAC_BASE + 0x200) = 0x08000000 |
+               *(volatile u32 *)(ebase + 0x200) = 0x08000000 |
                                                 (0x03ffffff & (handler >> 2));
-               flush_icache_range(CAC_BASE + 0x200, CAC_BASE + 0x204);
+               flush_icache_range(ebase + 0x200, ebase + 0x204);
+       }
+       return (void *)old_handler;
+}
+
+#ifdef CONFIG_CPU_MIPSR2
+/*
+ * MIPSR2 shadow register set allocation
+ * FIXME: SMP...
+ */
+
+static struct shadow_registers {
+       /*
+        * Number of shadow register sets supported
+        */
+       unsigned long sr_supported;
+       /*
+        * Bitmap of allocated shadow registers
+        */
+       unsigned long sr_allocated;
+} shadow_registers;
+
+static void mips_srs_init(void)
+{
+#ifdef CONFIG_CPU_MIPSR2_SRS
+       shadow_registers.sr_supported = ((read_c0_srsctl() >> 26) & 0x0f) + 1;
+       printk(KERN_INFO "%d MIPSR2 register sets available\n",
+              shadow_registers.sr_supported);
+#endif
+       shadow_registers.sr_allocated = 1;      /* Set 0 used by kernel */
+}
+
+int mips_srs_max(void)
+{
+       return shadow_registers.sr_supported;
+}
+
+int mips_srs_alloc(void)
+{
+       struct shadow_registers *sr = &shadow_registers;
+       int set;
+
+again:
+       set = find_first_zero_bit(&sr->sr_allocated, sr->sr_supported);
+       if (set >= sr->sr_supported)
+               return -1;
+
+       if (test_and_set_bit(set, &sr->sr_allocated))
+               goto again;
+
+       return set;
+}
+
+void mips_srs_free(int set)
+{
+       struct shadow_registers *sr = &shadow_registers;
+
+       clear_bit(set, &sr->sr_allocated);
+}
+
+static void *set_vi_srs_handler(int n, void *addr, int srs)
+{
+       unsigned long handler;
+       unsigned long old_handler = vi_handlers[n];
+       u32 *w;
+       unsigned char *b;
+
+       if (!cpu_has_veic && !cpu_has_vint)
+               BUG();
+
+       if (addr == NULL) {
+               handler = (unsigned long) do_default_vi;
+               srs = 0;
+       } else
+               handler = (unsigned long) addr;
+       vi_handlers[n] = (unsigned long) addr;
+
+       b = (unsigned char *)(ebase + 0x200 + n*VECTORSPACING);
+
+       if (srs >= mips_srs_max())
+               panic("Shadow register set %d not supported", srs);
+
+       if (cpu_has_veic) {
+               if (board_bind_eic_interrupt)
+                       board_bind_eic_interrupt (n, srs);
+       } else if (cpu_has_vint) {
+               /* SRSMap is only defined if shadow sets are implemented */
+               if (mips_srs_max() > 1)
+                       change_c0_srsmap (0xf << n*4, srs << n*4);
        }
+
+       if (srs == 0) {
+               /*
+                * If no shadow set is selected then use the default handler
+                * that does normal register saving and a standard interrupt exit
+                */
+
+               extern char except_vec_vi, except_vec_vi_lui;
+               extern char except_vec_vi_ori, except_vec_vi_end;
+#ifdef CONFIG_MIPS_MT_SMTC
+               /*
+                * We need to provide the SMTC vectored interrupt handler
+                * not only with the address of the handler, but with the
+                * Status.IM bit to be masked before going there.
+                */
+               extern char except_vec_vi_mori;
+               const int mori_offset = &except_vec_vi_mori - &except_vec_vi;
+#endif /* CONFIG_MIPS_MT_SMTC */
+               const int handler_len = &except_vec_vi_end - &except_vec_vi;
+               const int lui_offset = &except_vec_vi_lui - &except_vec_vi;
+               const int ori_offset = &except_vec_vi_ori - &except_vec_vi;
+
+               if (handler_len > VECTORSPACING) {
+                       /*
+                        * Sigh... panicing won't help as the console
+                        * is probably not configured :(
+                        */
+                       panic ("VECTORSPACING too small");
+               }
+
+               memcpy (b, &except_vec_vi, handler_len);
+#ifdef CONFIG_MIPS_MT_SMTC
+               if (n > 7)
+                       printk("Vector index %d exceeds SMTC maximum\n", n);
+               w = (u32 *)(b + mori_offset);
+               *w = (*w & 0xffff0000) | (0x100 << n);
+#endif /* CONFIG_MIPS_MT_SMTC */
+               w = (u32 *)(b + lui_offset);
+               *w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff);
+               w = (u32 *)(b + ori_offset);
+               *w = (*w & 0xffff0000) | ((u32)handler & 0xffff);
+               flush_icache_range((unsigned long)b, (unsigned long)(b+handler_len));
+       }
+       else {
+               /*
+                * In other cases jump directly to the interrupt handler
+                *
+                * It is the handlers responsibility to save registers if required
+                * (eg hi/lo) and return from the exception using "eret"
+                */
+               w = (u32 *)b;
+               *w++ = 0x08000000 | (((u32)handler >> 2) & 0x03fffff); /* j handler */
+               *w = 0;
+               flush_icache_range((unsigned long)b, (unsigned long)(b+8));
+       }
+
        return (void *)old_handler;
 }
 
+void *set_vi_handler(int n, void *addr)
+{
+       return set_vi_srs_handler(n, addr, 0);
+}
+#endif
+
 /*
  * This is used by native signal handling
  */
@@ -873,8 +1213,29 @@ extern asmlinkage int _restore_fp_context(struct sigcontext *sc);
 extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc);
 extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc);
 
+#ifdef CONFIG_SMP
+static int smp_save_fp_context(struct sigcontext *sc)
+{
+       return cpu_has_fpu
+              ? _save_fp_context(sc)
+              : fpu_emulator_save_context(sc);
+}
+
+static int smp_restore_fp_context(struct sigcontext *sc)
+{
+       return 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;
@@ -882,6 +1243,7 @@ static inline void signal_init(void)
                save_fp_context = fpu_emulator_save_context;
                restore_fp_context = fpu_emulator_restore_context;
        }
+#endif
 }
 
 #ifdef CONFIG_MIPS32_COMPAT
@@ -912,11 +1274,26 @@ static inline void signal32_init(void)
 
 extern void cpu_cache_init(void);
 extern void tlb_init(void);
+extern void flush_tlb_handlers(void);
 
 void __init per_cpu_trap_init(void)
 {
        unsigned int cpu = smp_processor_id();
        unsigned int status_set = ST0_CU0;
+#ifdef CONFIG_MIPS_MT_SMTC
+       int secondaryTC = 0;
+       int bootTC = (cpu == 0);
+
+       /*
+        * Only do per_cpu_trap_init() for first TC of Each VPE.
+        * Note that this hack assumes that the SMTC init code
+        * assigns TCs consecutively and in ascending order.
+        */
+
+       if (((read_c0_tcbind() & TCBIND_CURTC) != 0) &&
+           ((read_c0_tcbind() & TCBIND_CURVPE) == cpu_data[cpu - 1].vpe_id))
+               secondaryTC = 1;
+#endif /* CONFIG_MIPS_MT_SMTC */
 
        /*
         * Disable coprocessors and select 32-bit or 64-bit addressing
@@ -924,20 +1301,44 @@ void __init per_cpu_trap_init(void)
         * flag that some firmware may have left set and the TS bit (for
         * IP27).  Set XX for ISA IV code to work.
         */
-#ifdef CONFIG_MIPS64
+#ifdef CONFIG_64BIT
        status_set |= ST0_FR|ST0_KX|ST0_SX|ST0_UX;
 #endif
        if (current_cpu_data.isa_level == MIPS_CPU_ISA_IV)
                status_set |= ST0_XX;
-       change_c0_status(ST0_CU|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX,
+       change_c0_status(ST0_CU|ST0_MX|ST0_RE|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX,
                         status_set);
 
+       if (cpu_has_dsp)
+               set_c0_status(ST0_MX);
+
+#ifdef CONFIG_CPU_MIPSR2
+       write_c0_hwrena (0x0000000f); /* Allow rdhwr to all registers */
+#endif
+
+#ifdef CONFIG_MIPS_MT_SMTC
+       if (!secondaryTC) {
+#endif /* CONFIG_MIPS_MT_SMTC */
+
        /*
-        * Some MIPS CPUs have a dedicated interrupt vector which reduces the
-        * interrupt processing overhead.  Use it where available.
+        * Interrupt handling.
         */
-       if (cpu_has_divec)
-               set_c0_cause(CAUSEF_IV);
+       if (cpu_has_veic || cpu_has_vint) {
+               write_c0_ebase (ebase);
+               /* Setting vector spacing enables EI/VI mode  */
+               change_c0_intctl (0x3e0, VECTORSPACING);
+       }
+       if (cpu_has_divec) {
+               if (cpu_has_mipsmt) {
+                       unsigned int vpflags = dvpe();
+                       set_c0_cause(CAUSEF_IV);
+                       evpe(vpflags);
+               } else
+                       set_c0_cause(CAUSEF_IV);
+       }
+#ifdef CONFIG_MIPS_MT_SMTC
+       }
+#endif /* CONFIG_MIPS_MT_SMTC */
 
        cpu_data[cpu].asid_cache = ASID_FIRST_VERSION;
        TLBMISS_HANDLER_SETUP();
@@ -947,17 +1348,51 @@ void __init per_cpu_trap_init(void)
        BUG_ON(current->mm);
        enter_lazy_tlb(&init_mm, current);
 
-       cpu_cache_init();
-       tlb_init();
+#ifdef CONFIG_MIPS_MT_SMTC
+       if (bootTC) {
+#endif /* CONFIG_MIPS_MT_SMTC */
+               cpu_cache_init();
+               tlb_init();
+#ifdef CONFIG_MIPS_MT_SMTC
+       }
+#endif /* CONFIG_MIPS_MT_SMTC */
+}
+
+/* Install CPU exception handler */
+void __init set_handler (unsigned long offset, void *addr, unsigned long size)
+{
+       memcpy((void *)(ebase + offset), addr, size);
+       flush_icache_range(ebase + offset, ebase + offset + size);
+}
+
+/* Install uncached CPU exception handler */
+void __init 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
+
+       memcpy((void *)(uncached_ebase + offset), addr, size);
 }
 
 void __init trap_init(void)
 {
        extern char except_vec3_generic, except_vec3_r4000;
-       extern char except_vec_ejtag_debug;
        extern char except_vec4;
        unsigned long i;
 
+       if (cpu_has_veic || cpu_has_vint)
+               ebase = (unsigned long) alloc_bootmem_low_pages (0x200 + VECTORSPACING*64);
+       else
+               ebase = CAC_BASE;
+
+#ifdef CONFIG_CPU_MIPSR2
+       mips_srs_init();
+#endif
+
        per_cpu_trap_init();
 
        /*
@@ -965,7 +1400,7 @@ void __init trap_init(void)
         * This will be overriden later as suitable for a particular
         * configuration.
         */
-       memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80);
+       set_handler(0x180, &except_vec3_generic, 0x80);
 
        /*
         * Setup default vectors
@@ -977,8 +1412,8 @@ void __init trap_init(void)
         * Copy the EJTAG debug exception vector handler code to it's final
         * destination.
         */
-       if (cpu_has_ejtag)
-               memcpy((void *)(CAC_BASE + 0x300), &except_vec_ejtag_debug, 0x80);
+       if (cpu_has_ejtag && board_ejtag_handler_setup)
+               board_ejtag_handler_setup ();
 
        /*
         * Only some CPUs have the watch exceptions.
@@ -987,11 +1422,15 @@ void __init trap_init(void)
                set_except_vector(23, handle_watch);
 
        /*
-        * Some MIPS CPUs have a dedicated interrupt vector which reduces the
-        * interrupt processing overhead.  Use it where available.
+        * Initialise interrupt handlers
         */
-       if (cpu_has_divec)
-               memcpy((void *)(CAC_BASE + 0x200), &except_vec4, 0x8);
+       if (cpu_has_veic || cpu_has_vint) {
+               int nvec = cpu_has_veic ? 64 : 8;
+               for (i = 0; i < nvec; i++)
+                       set_vi_handler(i, NULL);
+       }
+       else if (cpu_has_divec)
+               set_handler(0x200, &except_vec4, 0x8);
 
        /*
         * Some CPUs can enable/disable for cache parity detection, but does
@@ -1007,6 +1446,7 @@ void __init trap_init(void)
        if (board_be_init)
                board_be_init();
 
+       set_except_vector(0, handle_int);
        set_except_vector(1, handle_tlbm);
        set_except_vector(2, handle_tlbl);
        set_except_vector(3, handle_tlbs);
@@ -1023,21 +1463,6 @@ void __init trap_init(void)
        set_except_vector(11, handle_cpu);
        set_except_vector(12, handle_ov);
        set_except_vector(13, handle_tr);
-       set_except_vector(22, handle_mdmx);
-
-       if (cpu_has_fpu && !cpu_has_nofpuex)
-               set_except_vector(15, handle_fpe);
-
-       if (cpu_has_mcheck)
-               set_except_vector(24, handle_mcheck);
-
-       if (cpu_has_vce)
-               /* Special exception: R4[04]00 uses also the divec space. */
-               memcpy((void *)(CAC_BASE + 0x180), &except_vec3_r4000, 0x100);
-       else if (cpu_has_4kex)
-               memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80);
-       else
-               memcpy((void *)(CAC_BASE + 0x080), &except_vec3_generic, 0x80);
 
        if (current_cpu_data.cputype == CPU_R6000 ||
            current_cpu_data.cputype == CPU_R6000A) {
@@ -1053,10 +1478,37 @@ void __init trap_init(void)
                //set_except_vector(15, handle_ndc);
        }
 
+
+       if (board_nmi_handler_setup)
+               board_nmi_handler_setup();
+
+       if (cpu_has_fpu && !cpu_has_nofpuex)
+               set_except_vector(15, handle_fpe);
+
+       set_except_vector(22, handle_mdmx);
+
+       if (cpu_has_mcheck)
+               set_except_vector(24, handle_mcheck);
+
+       if (cpu_has_mipsmt)
+               set_except_vector(25, handle_mt);
+
+       if (cpu_has_dsp)
+               set_except_vector(26, handle_dsp);
+
+       if (cpu_has_vce)
+               /* Special exception: R4[04]00 uses also the divec space. */
+               memcpy((void *)(CAC_BASE + 0x180), &except_vec3_r4000, 0x100);
+       else if (cpu_has_4kex)
+               memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80);
+       else
+               memcpy((void *)(CAC_BASE + 0x080), &except_vec3_generic, 0x80);
+
        signal_init();
 #ifdef CONFIG_MIPS32_COMPAT
        signal32_init();
 #endif
 
-       flush_icache_range(CAC_BASE, CAC_BASE + 0x400);
+       flush_icache_range(ebase, ebase + 0x400);
+       flush_tlb_handlers();
 }