MIPS: R6: emulation of MIPS R2 removed instructions.
authorLeonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Thu, 20 Nov 2014 01:44:31 +0000 (17:44 -0800)
committerRaghu Gandham <raghu.gandham@imgtec.com>
Tue, 2 Dec 2014 00:58:31 +0000 (16:58 -0800)
MIPS R6 architecture has deleted some MIPS R2 instructions.
This patch does emulation of that instructions in kernel.

Squashed:
5465a192ac70 R6 emulation of MIPS R2: restore MIPS32 R6 build
1cdef69e7305 MIPS: R6: R2 emulation optimisation
ece19295b50e MIPS: R6: FRE mode support for R2 emulation
666439e519f1 MIPS: Build: bind R2 and incompatible FPU emulation together
3be8d0f9a1d8 MIPS: R6: R2 emulation bugfixes in negative signal return
aefc61dfb88a MIPS: R6: R2 emulation bugfix of address in process_fpemu_return
8b7d02f5263b MIPS: R6: R2 emulation bugfix of EVA by LLE/SCE opcodes
7f403f401c6d MIPS: R6 emulation of R2 bugfix of LWR instruction
07c2979e66b3 MIPS: bugfix of FR0/1 setup failure processing

Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
16 files changed:
arch/mips/Kconfig
arch/mips/include/asm/asmmacro-32.h
arch/mips/include/asm/asmmacro-64.h
arch/mips/include/asm/cpu-features.h
arch/mips/include/asm/cpu.h
arch/mips/include/asm/fpu.h
arch/mips/include/asm/fpu_emulator.h
arch/mips/include/asm/mipsregs.h
arch/mips/include/uapi/asm/inst.h
arch/mips/kernel/Makefile
arch/mips/kernel/branch.c
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/mips-r2-emul.c [new file with mode: 0644]
arch/mips/kernel/traps.c
arch/mips/math-emu/cp1emu.c
arch/mips/math-emu/dsemul.c

index 2e66da2437a41995d2700fd90781419ee7591f1e..256289a1948a732371355a1364d6a71f17237d77 100644 (file)
@@ -2037,14 +2037,15 @@ config MIPS_MT_FPAFF
        default y
        depends on MIPS_MT_SMP || MIPS_MT_SMTC
 
-config MIPS_INCOMPATIBLE_FPU_EMULATION
-       bool "Emulation of incompatible FPU"
+config MIPS_INCOMPATIBLE_ARCH_EMULATION
+       bool "Emulation of incompatible architecture"
        default n
-       depends on !CPU_MIPS32_R2 && !CPU_MIPS64_R1 && !CPU_MIPS64_R2
+       depends on CPU_MIPSR6
        help
          Emulation of 32x32bit or 32x64bit FPU ELFs on incompatible FPU.
          CP0_Status.FR bit controls switch between both models but
          some CPU may not have this capability.
+         It also can emulate MIPS32/64 R2 architecture on MIPS R6.
          If unsure, leave N here.
 
 config MIPS_VPE_LOADER
index 9060c15fef501b9e2dcf185e2a64ff5b12e53722..b2e5c37721755978a35da0aafe1096b587ab3686 100644 (file)
@@ -17,7 +17,6 @@
        /* copy stuff from MIPS64 */
 
        .macro  fpu_save_16even thread tmp=t0
-       cfc1    \tmp, fcr31
        sdc1    $f0,  THREAD_FPR0(\thread)
        sdc1    $f2,  THREAD_FPR2(\thread)
        sdc1    $f4,  THREAD_FPR4(\thread)
@@ -34,7 +33,6 @@
        sdc1    $f26, THREAD_FPR26(\thread)
        sdc1    $f28, THREAD_FPR28(\thread)
        sdc1    $f30, THREAD_FPR30(\thread)
-       sw  \tmp, THREAD_FCR31(\thread)
        .endm
 
        .macro  fpu_save_16odd thread
        fpu_save_16odd \thread
 2:
        fpu_save_16even \thread \tmp
+#ifdef CONFIG_CPU_MIPSR6
+       lw      \status, THREAD_FCR31(\thread)
+       lui     \tmp, (FPU_CSR_COND0|FPU_CSR_COND1|FPU_CSR_COND2|FPU_CSR_COND3| \
+                   FPU_CSR_COND4|FPU_CSR_COND5|FPU_CSR_COND6|FPU_CSR_COND7)>>16
+       and     \tmp, \status, \tmp
+       cfc1    \status, fcr31
+       or      \tmp, \status, \tmp
+       sw      \tmp, THREAD_FCR31(\thread)
+#else
+       cfc1    \tmp, fcr31
+       sw      \tmp, THREAD_FCR31(\thread)
+#endif
        .set    pop
        .endm
 
index 922209d6347026de49be8871a7879f83f6938dc7..f2ca6a3ef1e8929bf0af62ab068b135611539c48 100644 (file)
@@ -14,7 +14,6 @@
 #include <asm/mipsregs.h>
 
        .macro  fpu_save_16even thread tmp=t0
-       cfc1    \tmp, fcr31
        sdc1    $f0,  THREAD_FPR0(\thread)
        sdc1    $f2,  THREAD_FPR2(\thread)
        sdc1    $f4,  THREAD_FPR4(\thread)
@@ -31,7 +30,6 @@
        sdc1    $f26, THREAD_FPR26(\thread)
        sdc1    $f28, THREAD_FPR28(\thread)
        sdc1    $f30, THREAD_FPR30(\thread)
-       sw      \tmp, THREAD_FCR31(\thread)
        .endm
 
        .macro  fpu_save_16odd thread
        fpu_save_16odd \thread
 2:
        fpu_save_16even \thread \tmp
+#ifdef CONFIG_CPU_MIPSR6
+       lw      \status, THREAD_FCR31(\thread)
+       lui     \tmp, (FPU_CSR_COND0|FPU_CSR_COND1|FPU_CSR_COND2|FPU_CSR_COND3| \
+                   FPU_CSR_COND4|FPU_CSR_COND5|FPU_CSR_COND6|FPU_CSR_COND7)>>16
+       and     \tmp, \status, \tmp
+       cfc1    \status, fcr31
+       or      \tmp, \status, \tmp
+       sw      \tmp, THREAD_FCR31(\thread)
+#else
+       cfc1    \tmp, fcr31
+       sw      \tmp, THREAD_FCR31(\thread)
+#endif
        .endm
 
        .macro  fpu_restore_16even thread tmp=t0
index 95feb244a4a104b93d5ebe0ee21c43ff9fec01ed..6eb93f1465e6bd109733e24c3be913355a9213e5 100644 (file)
 #ifndef cpu_has_maar
 #define cpu_has_maar            (cpu_data[0].options2 & MIPS_CPU_MAAR)
 #endif
+#ifndef cpu_has_fre
+#ifdef CONFIG_MIPS_INCOMPATIBLE_ARCH_EMULATION
+#define cpu_has_fre             (cpu_data[0].options2 & MIPS_CPU_FRE)
+#else
+#define cpu_has_fre             0
+#endif
+#endif /* cpu_has_fre */
 
 /*
  * I-Cache snoops remote store.         This only matters on SMP.  Some multiprocessors
index be8c7e3fd9770ac38b750598f6455781944fc6e9..91361acd9c9929eed6412b7e3df2f0a59080b1ed 100644 (file)
@@ -351,6 +351,7 @@ enum cpu_type_enum {
  */
 #define MIPS_CPU_MAAR           0x00000001      /* MAAR exists */
 #define MIPS_CPU_HIMEM          0x00000002 /* MIPS32: PA bits exceed PTE space */
+#define MIPS_CPU_FRE            0x00000004 /* CPU has FRE support */
 
 /*
  * CPU ASE encodings
index 134ee8ee293a45d6332e86276f21c7111cf91018..d8cf068e6dce14ea4083c1c23e2629abc8536918 100644 (file)
@@ -60,19 +60,37 @@ static inline int __own_fpu(void)
        int ret = 0;
 
 #if defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_CPU_MIPS32_R6) || defined(CONFIG_CPU_MIPS64)
+       u32 status;
+
        if (test_thread_flag(TIF_32BIT_REGS)) {
-               change_c0_status(ST0_CU1|ST0_FR,ST0_CU1);
-               KSTK_STATUS(current) |= ST0_CU1;
-               KSTK_STATUS(current) &= ~ST0_FR;
+               status = change_c0_status(ST0_CU1|ST0_FR,ST0_CU1);
                enable_fpu_hazard();
-               if (read_c0_status() & ST0_FR)
-                   ret = SIGFPE;
+               if (read_c0_status() & ST0_FR) {
+                       if (cpu_has_fre) {
+                               write_c0_config5(read_c0_config5() | MIPS_CONF5_FRE);
+                               back_to_back_c0_hazard();
+                               KSTK_STATUS(current) |= ST0_CU1|ST0_FR;
+                       } else {
+                               write_c0_status(status);
+                               disable_fpu_hazard();
+                               return(SIGFPE);
+                       }
+               } else {
+                       KSTK_STATUS(current) = (KSTK_STATUS(current) & ~ST0_FR) | ST0_CU1;
+               }
        } else {
-               set_c0_status(ST0_CU1|ST0_FR);
-               KSTK_STATUS(current) |= ST0_CU1|ST0_FR;
+               status = set_c0_status(ST0_CU1|ST0_FR);
                enable_fpu_hazard();
-               if (!(read_c0_status() & ST0_FR))
-                   ret = SIGFPE;
+               if (!(read_c0_status() & ST0_FR)) {
+                       write_c0_status(status);
+                       disable_fpu_hazard();
+                       return(SIGFPE);
+               }
+               if (cpu_has_fre) {
+                       write_c0_config5(read_c0_config5() | ~MIPS_CONF5_FRE);
+                       back_to_back_c0_hazard();
+               }
+               KSTK_STATUS(current) |= ST0_CU1|ST0_FR;
        }
 #else
        if (!test_thread_flag(TIF_32BIT_REGS))
index 2abb587d5ab40fd41aa950d1987d5f854f60e531..56f4d0c51831935fb026f8ec1466b1bfc0afe941 100644 (file)
@@ -61,6 +61,13 @@ int process_fpemu_return(int sig, void __user *fault_addr);
 int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                     unsigned long *contpc);
 
+#ifdef CONFIG_MIPS_INCOMPATIBLE_ARCH_EMULATION
+extern int mipsr2_emulation;
+extern const unsigned int fpucondbit[];
+int mipsr2_decoder(struct pt_regs *regs, u32 instruction);
+#else
+#define mipsr2_emulation    0
+#endif
 /*
  * Instruction inserted following the badinst to further tag the sequence
  */
index 69f42b5911651311f8978785edd24af105c6df42..ea5612ab5a60e71d0cb8fd3a2cea724bc6bcfdf5 100644 (file)
 #define MIPS_CONF4_TLBINV_FULL  (_ULCAST_(1) << 29)
 
 #define MIPS_CONF5_MRP          (_ULCAST_(1) << 3)
+#define MIPS_CONF5_FRE          (_ULCAST_(1) << 8)
+#define MIPS_CONF5_UFE          (_ULCAST_(1) << 9)
 #define MIPS_CONF5_EVA         (_ULCAST_(1) << 28)
 #define MIPS_CONF5_CV          (_ULCAST_(1) << 29)
 #define MIPS_CONF5_K           (_ULCAST_(1) << 30)
 /* additional bits in MIPS32/64 coprocessor 2 (FPU) */
 #define MIPS_FPIR_HAS2008       (_ULCAST_(1) << 23)
 #define MIPS_FPIR_FC            (_ULCAST_(1) << 24)
+#define MIPS_FPIR_FREP          (_ULCAST_(1) << 29)
 
 /*
  * Bits in the MIPS32 Memory Segmentation registers.
index 871344419e3a6e9e2e2a2ddba07aacda08531fb0..b37f8d3d60515d12174a30c794d6f108f66560b2 100644 (file)
@@ -113,11 +113,7 @@ enum rt_op {
        spimi_op, unused_rt_op_0x05, unused_rt_op_0x06, unused_rt_op_0x07,
        tgei_op, tgeiu_op, tlti_op, tltiu_op,
        teqi_op, unused_0x0d_rt_op, tnei_op, unused_0x0f_rt_op,
-#ifndef CONFIG_CPU_MIPSR6
        bltzal_op, bgezal_op, bltzall_op, bgezall_op,
-#else
-       nal_op, bal_op, rt_op_0x12_op, rt_op_0x13_op,
-#endif
        rt_op_0x14, rt_op_0x15, rt_op_0x16, rt_op_0x17,
        rt_op_0x18, rt_op_0x19, rt_op_0x1a, rt_op_0x1b,
        bposge32_op, rt_op_0x1d, rt_op_0x1e, rt_op_0x1f
index d93277cb7ba306634e956caa5f8fdfd81170c8c2..8ec25462c8bc4e8e129be2e4cf4b64983cb9c11d 100644 (file)
@@ -86,6 +86,7 @@ obj-$(CONFIG_CRASH_DUMP)      += crash_dump.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
 obj-$(CONFIG_SPINLOCK_TEST)    += spinlock_test.o
 obj-$(CONFIG_MIPS_MACHINE)     += mips_machine.o
+obj-$(CONFIG_MIPS_INCOMPATIBLE_ARCH_EMULATION)      += mips-r2-emul.o
 
 CFLAGS_cpu-bugs64.o    = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -x c /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
 
index ea5764106683c5624c57b9c68c4e05db22ba0ad6..5aee775157f01d46887b996e1f6d5f17e2e7888a 100644 (file)
@@ -220,10 +220,10 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
        unsigned int bit;
        long epc = regs->cp0_epc;
        int ret = 0;
+       unsigned int fcr31;
 #ifdef CONFIG_CPU_MIPSR6
        int reg;
 #else
-       unsigned int fcr31;
        unsigned dspcontrol;
 #endif
 
@@ -235,9 +235,14 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
                switch (insn.r_format.func) {
                case jalr_op:
                        regs->regs[insn.r_format.rd] = epc + 8;
-                       /* Fall through */
-#ifndef CONFIG_CPU_MIPSR6
+                       regs->cp0_epc = regs->regs[insn.r_format.rs];
+                       break;
                case jr_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       if (!mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
 #endif
                        regs->cp0_epc = regs->regs[insn.r_format.rs];
                        break;
@@ -251,55 +256,55 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
         */
        case bcond_op:
                switch (insn.i_format.rt) {
-               case bltz_op:
-#ifndef CONFIG_CPU_MIPSR6
                case bltzl_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       if (!mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
 #endif
+               case bltz_op:
                        if ((long)regs->regs[insn.i_format.rs] < 0) {
                                epc = epc + 4 + (insn.i_format.simmediate << 2);
-#ifndef CONFIG_CPU_MIPSR6
                                if (insn.i_format.rt == bltzl_op)
                                        ret = BRANCH_LIKELY_TAKEN;
-#endif
                        } else
                                epc += 8;
                        regs->cp0_epc = epc;
                        break;
 
-               case bgez_op:
-#ifndef CONFIG_CPU_MIPSR6
                case bgezl_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       if (!mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
 #endif
+               case bgez_op:
                        if ((long)regs->regs[insn.i_format.rs] >= 0) {
                                epc = epc + 4 + (insn.i_format.simmediate << 2);
-#ifndef CONFIG_CPU_MIPSR6
                                if (insn.i_format.rt == bgezl_op)
                                        ret = BRANCH_LIKELY_TAKEN;
-#endif
                        } else
                                epc += 8;
                        regs->cp0_epc = epc;
                        break;
 
+               case bltzall_op:
 #ifdef CONFIG_CPU_MIPSR6
-               case nal_op:    /* MIPSR6: nal == bltzal $0 */
-                       if (insn.i_format.rs)
+                       if (!mipsr2_emulation) {
+                               ret = -SIGILL;
                                break;
-                       regs->regs[31] = epc + 8;
-                       epc += 4;
-                       regs->cp0_epc = epc;
-                       break;
-
-               case bal_op:    /* MIPSR6: bal == bgezal $0 */
-                       if (insn.i_format.rs)
-                               break;
-                       regs->regs[31] = epc + 8;
-                       epc = epc + 4 + (insn.i_format.simmediate << 2);
-                       regs->cp0_epc = epc;
-                       break;
-#else
+                       }
+#endif
                case bltzal_op:
-               case bltzall_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       /* MIPSR6: nal == bltzal $0 */
+                       if (insn.i_format.rs && !mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
+#endif
                        regs->regs[31] = epc + 8;
                        if ((long)regs->regs[insn.i_format.rs] < 0) {
                                epc = epc + 4 + (insn.i_format.simmediate << 2);
@@ -310,8 +315,21 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
                        regs->cp0_epc = epc;
                        break;
 
-               case bgezal_op:
                case bgezall_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       if (!mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
+#endif
+               case bgezal_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       /* MIPSR6: bal == bgezal $0 */
+                       if (insn.i_format.rs && !mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
+#endif
                        regs->regs[31] = epc + 8;
                        if ((long)regs->regs[insn.i_format.rs] >= 0) {
                                epc = epc + 4 + (insn.i_format.simmediate << 2);
@@ -322,6 +340,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
                        regs->cp0_epc = epc;
                        break;
 
+#ifndef CONFIG_CPU_MIPSR6
                case bposge32_op:
                        if (!cpu_has_dsp)
                                goto sigill;
@@ -356,69 +375,64 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
        /*
         * These are conditional and in i_format.
         */
-       case beq_op:
-#ifndef CONFIG_CPU_MIPSR6
        case beql_op:
+#ifdef CONFIG_CPU_MIPSR6
+               if (!mipsr2_emulation) {
+                       ret = -SIGILL;
+                       break;
+               }
 #endif
+       case beq_op:
                if (regs->regs[insn.i_format.rs] ==
                    regs->regs[insn.i_format.rt]) {
                        epc = epc + 4 + (insn.i_format.simmediate << 2);
-#ifndef CONFIG_CPU_MIPSR6
                        if (insn.i_format.opcode == beql_op)
                                ret = BRANCH_LIKELY_TAKEN;
-#endif
                } else
                        epc += 8;
                regs->cp0_epc = epc;
                break;
 
-       case bne_op:
-#ifndef CONFIG_CPU_MIPSR6
        case bnel_op:
+#ifdef CONFIG_CPU_MIPSR6
+               if (!mipsr2_emulation) {
+                       ret = -SIGILL;
+                       break;
+               }
 #endif
+       case bne_op:
                if (regs->regs[insn.i_format.rs] !=
                    regs->regs[insn.i_format.rt]) {
                        epc = epc + 4 + (insn.i_format.simmediate << 2);
-#ifndef CONFIG_CPU_MIPSR6
                        if (insn.i_format.opcode == bnel_op)
                                ret = BRANCH_LIKELY_TAKEN;
-#endif
                } else
                        epc += 8;
                regs->cp0_epc = epc;
                break;
 
        case blez_op: /* not really i_format */
+       case blezl_op:
 #ifdef CONFIG_CPU_MIPSR6
                /*
-                *  Compact branches: blezalc, bgezalc, bgeuc
+                *  Compact branches: (blez:)  blezalc, bgezalc, bgeuc
+                *  Compact branches: (blezl:) blezc, bgezc, bgec
                 */
                if (insn.i_format.rt) {
-                       if ((insn.i_format.rs == insn.i_format.rt) ||
-                           !insn.i_format.rs)   /* blezalc, bgezalc */
-                               regs->regs[31] = epc + 4;
+                       if (insn.i_format.opcode == blez_op)
+                               if ((insn.i_format.rs == insn.i_format.rt) ||
+                                   !insn.i_format.rs)  /* blezalc, bgezalc */
+                                       regs->regs[31] = epc + 4;
                        epc += 8;
                        regs->cp0_epc = epc;
                        break;
                }
 
-               if ((long)regs->regs[insn.i_format.rs] <= 0) {
-                       epc = epc + 4 + (insn.i_format.simmediate << 2);
-               } else
-                       epc += 8;
-               regs->cp0_epc = epc;
-               break;
+               if ((insn.i_format.opcode != blez_op) && !mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
 #endif
-       case blezl_op:
-#ifdef CONFIG_CPU_MIPSR6
-               /*
-                *  Compact branches: blezc, bgezc, bgec
-                */
-               epc += 8;
-               regs->cp0_epc = epc;
-
-               break;
-#else
                /* rt field assumed to be zero */
                if ((long)regs->regs[insn.i_format.rs] <= 0) {
                        epc = epc + 4 + (insn.i_format.simmediate << 2);
@@ -428,39 +442,29 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
                        epc += 8;
                regs->cp0_epc = epc;
                break;
-#endif
 
        case bgtz_op:
+       case bgtzl_op:
 #ifdef CONFIG_CPU_MIPSR6
                /*
-                *  Compact branches: bltzalc, bgtzalc, bltuc
+                *  Compact branches: (bgtz:)  bltzalc, bgtzalc, bltuc
+                *  Compact branches: (bgtzl:) bltc, bltzc, bgtzc
                 */
                if (insn.i_format.rt) {
-                       if ((insn.i_format.rs == insn.i_format.rt) ||
-                           !insn.i_format.rs)   /* bltzalc, bgtzalc */
-                               regs->regs[31] = epc + 4;
+                       if (insn.i_format.opcode == bgtz_op)
+                               if ((insn.i_format.rs == insn.i_format.rt) ||
+                                   !insn.i_format.rs)   /* bltzalc, bgtzalc */
+                                       regs->regs[31] = epc + 4;
                        epc += 8;
                        regs->cp0_epc = epc;
                        break;
                }
 
-               if ((long)regs->regs[insn.i_format.rs] > 0) {
-                       epc = epc + 4 + (insn.i_format.simmediate << 2);
-               } else
-                       epc += 8;
-               regs->cp0_epc = epc;
-               break;
+               if ((insn.i_format.opcode != bgtz_op) && !mipsr2_emulation) {
+                               ret = -SIGILL;
+                               break;
+                       }
 #endif
-       case bgtzl_op:
-#ifdef CONFIG_CPU_MIPSR6
-               /*
-                *  Compact branches: bltc, bltzc, bgtzc
-                */
-               epc += 8;
-               regs->cp0_epc = epc;
-
-               break;
-#else
                /* rt field assumed to be zero */
                if ((long)regs->regs[insn.i_format.rs] > 0) {
                        epc = epc + 4 + (insn.i_format.simmediate << 2);
@@ -471,8 +475,6 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
                regs->cp0_epc = epc;
                break;
 
-#endif
-
 #ifdef CONFIG_CPU_MIPSR6
        case cbcond0_op:
                /*
@@ -497,32 +499,46 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
         */
        case cop1_op:
 #ifdef CONFIG_CPU_MIPSR6
-               if ((insn.i_format.rs != bc1eqz_op) &&
-                   (insn.i_format.rs != bc1nez_op))
-                       break;
+               if ((insn.i_format.rs == bc1eqz_op) ||
+                   (insn.i_format.rs == bc1nez_op)) {
+
+                       if (!used_math()) {     /* First time FPU user.  */
+                               ret = init_fpu();
+                               if (ret && !mipsr2_emulation) {
+                                       ret = -ret;
+                                       break;
+                               }
+                               ret = 0;
+                               set_used_math();
+                       }
+                       lose_fpu(1);    /* Save FPU state for the emulator. */
+                       reg = insn.i_format.rt;
+                       bit = 0;
+                       switch (insn.i_format.rs) {
+                       case bc1eqz_op:
+                               if (current->thread.fpu.fpr[reg] == (__u64)0)
+                                       bit = 1;
+                               break;
+                       case bc1nez_op:
+                               if (current->thread.fpu.fpr[reg] != (__u64)0)
+                                       bit = 1;
+                               break;
+                       }
+                       own_fpu(1);     /* Restore FPU state. */
+                       if (bit)
+                               epc = epc + 4 + (insn.i_format.simmediate << 2);
+                       else
+                               epc += 8;
+                       regs->cp0_epc = epc;
 
-               lose_fpu(1);    /* Save FPU state for the emulator. */
-               reg = insn.i_format.rt;
-               bit = 0;
-               switch (insn.i_format.rs) {
-               case bc1eqz_op:
-                       if (current->thread.fpu.fpr[reg] == (__u64)0)
-                               bit = 1;
-                       break;
-               case bc1nez_op:
-                       if (current->thread.fpu.fpr[reg] != (__u64)0)
-                               bit = 1;
                        break;
                }
-               own_fpu(1);     /* Restore FPU state. */
-               if (bit)
-                       epc = epc + 4 + (insn.i_format.simmediate << 2);
-               else
-                       epc += 8;
-               regs->cp0_epc = epc;
 
-               break;
-#else
+               if (!mipsr2_emulation) {
+                       ret = -SIGILL;
+                       break;
+               }
+#endif
                preempt_disable();
                if (is_fpu_owner())
                        asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
@@ -557,7 +573,6 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
                        break;
                }
                break;
-#endif
 
 #ifdef CONFIG_CPU_MIPSR6
        case bc_op:
@@ -625,6 +640,10 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
 #endif
        }
 
+       if (ret < 0) {
+               force_sig(-ret,current);
+               return -EFAULT;
+       }
        return ret;
 
 #ifndef CONFIG_CPU_MIPSR6
index 81b3673b36077c6cbed2436f44ebd0884fc2f686..1b1a6c2d1e531cca945f3f8d2c1b52a9e6fd883a 100644 (file)
@@ -1266,6 +1266,8 @@ __cpuinit void cpu_probe(void)
                                c->ases |= MIPS_ASE_MIPS3D;
                        if (c->fpu_id & MIPS_FPIR_HAS2008)
                                fpu_fcr31 = cpu_test_fpu_csr31(FPU_CSR_DEFAULT|FPU_CSR_MAC2008|FPU_CSR_ABS2008|FPU_CSR_NAN2008);
+                       if (c->fpu_id & MIPS_FPIR_FREP)
+                               c->options2 |= MIPS_CPU_FRE;
                }
        }
 
diff --git a/arch/mips/kernel/mips-r2-emul.c b/arch/mips/kernel/mips-r2-emul.c
new file mode 100644 (file)
index 0000000..4f21d51
--- /dev/null
@@ -0,0 +1,2004 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2014 Imagination Technologies, LLC.  All rights reserved.
+ *
+ *      MIPS R2 user space instruction emulator
+ *
+ *      Author: Leonid Yegoshin
+ */
+#include <linux/bug.h>
+#include <linux/compiler.h>
+#include <linux/ptrace.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/branch.h>
+#include <asm/fpu.h>
+#include <asm/fpu_emulator.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/asm.h>
+
+#include <asm/break.h>
+#include <asm/inst.h>
+#include <asm/local.h>
+
+/* number of loop cycles before we gave up */
+#define TOTAL_PASS      10
+
+#ifdef CONFIG_CPU_MIPS64
+#define ADDIU           "daddiu "
+#define INS             "dins "
+#define EXT             "dext "
+#define SB              "sb "
+#define LB              "lb "
+#define LL              "ll "
+#define SC              "sc "
+#else /* !CONFIG_CPU_MIPS64 */
+#define ADDIU           "addiu "
+#define INS             "ins "
+#define EXT             "ext "
+#ifdef CONFIG_EVA
+#define SB              "sbe "
+#define LB              "lbe "
+#define LL              "lle "
+#define SC              "sce "
+#else
+#define SB              "sb "
+#define LB              "lb "
+#define LL              "ll "
+#define SC              "sc "
+#endif
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+struct mips_r2_emulator_stats {
+       local_t movs;
+       local_t hilo;
+       local_t muls;
+       local_t divs;
+       local_t dsps;
+       local_t bops;
+       local_t traps;
+       local_t fpus;
+       local_t loads;
+       local_t stores;
+       local_t llsc;
+       local_t dsemul;
+};
+
+DEFINE_PER_CPU(struct mips_r2_emulator_stats, mipsr2emustats);
+DEFINE_PER_CPU(struct mips_r2_emulator_stats, mipsr2bdemustats);
+
+#define MIPS_R2_STATS(M)                                       \
+do {                                                                   \
+       u32 nir;                                                        \
+       int err;                                                        \
+                                                                       \
+       preempt_disable();                                              \
+       __local_inc(&__get_cpu_var(mipsr2emustats).M);                     \
+       err = __get_user(nir, (u32 __user *)regs->cp0_epc);                \
+       if (!err) {                                                        \
+               if (nir == BREAK_MATH)                                     \
+                       __local_inc(&__get_cpu_var(mipsr2bdemustats).M);   \
+       }                                                                  \
+       preempt_enable();                                               \
+} while (0)
+
+struct mips_r2br_emulator_stats {
+       local_t jrs;
+       local_t bltzl;
+       local_t bgezl;
+       local_t bltzll;
+       local_t bgezll;
+       local_t bltzall;
+       local_t bgezall;
+       local_t bltzal;
+       local_t bgezal;
+       local_t beql;
+       local_t bnel;
+       local_t blezl;
+       local_t bgtzl;
+};
+
+DEFINE_PER_CPU(struct mips_r2br_emulator_stats, mipsr2bremustats);
+
+#define MIPS_R2BR_STATS(M)                                       \
+do {                                                                   \
+       preempt_disable();                                              \
+       __local_inc(&__get_cpu_var(mipsr2bremustats).M);                     \
+       preempt_enable();                                               \
+} while (0)
+
+#else
+
+#define MIPS_R2_STATS(M)          do { } while (0)
+#define MIPS_R2BR_STATS(M)        do { } while (0)
+
+#endif /* CONFIG_DEBUG_FS */
+
+struct r2_decoder_table {
+       u32     mask;
+       u32     code;
+       int     (*func)(struct pt_regs *regs, u32 inst);
+};
+
+
+int mipsr2_emulation = 1;
+
+void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
+       const char *str);
+
+static int __init nomipsr2_func(char *s)
+{
+       mipsr2_emulation = 0;
+       return 1;
+}
+__setup("nomipsr2", nomipsr2_func);
+
+/* Emulate some frequent R2/R5/R6 instructions in JR BD slot for performance.
+ * Otherwise it will be emulated via stack trampoline... very slow.
+ */
+static inline int mipsr6_emul(struct pt_regs *regs, u32 ir)
+{
+       switch (MIPSInst_OPCODE(ir)) {
+       case addiu_op:
+               if (MIPSInst_RT(ir))
+                       regs->regs[MIPSInst_RT(ir)] =
+                               (s32)regs->regs[MIPSInst_RS(ir)] +
+                               (s32)MIPSInst_SIMM(ir);
+               return(0);
+#ifdef CONFIG_64BIT
+       case daddiu_op:
+               if (MIPSInst_RT(ir))
+                       regs->regs[MIPSInst_RT(ir)] =
+                               (s64)regs->regs[MIPSInst_RS(ir)] +
+                               (s64)MIPSInst_SIMM(ir);
+               return(0);
+#endif
+       case lwc1_op:
+       case swc1_op:
+       case cop1_op:
+       case cop1x_op:
+               /* indicate FPU instructions */
+               return(-SIGFPE);
+       case spec_op:
+               switch (MIPSInst_FUNC(ir)) {
+               case or_op:
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       regs->regs[MIPSInst_RS(ir)] |
+                                       regs->regs[MIPSInst_RT(ir)];
+                       return(0);
+               case sll_op:
+                       if (MIPSInst_RS(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (s32)(((u32)regs->regs[MIPSInst_RT(ir)]) <<
+                                               MIPSInst_FD(ir));
+                       return(0);
+               case srl_op:
+                       if (MIPSInst_RS(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (s32)(((u32)regs->regs[MIPSInst_RT(ir)]) >>
+                                               MIPSInst_FD(ir));
+                       return(0);
+               case addu_op:
+                       if (MIPSInst_FD(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (s32)((u32)regs->regs[MIPSInst_RS(ir)] +
+                                             (u32)regs->regs[MIPSInst_RT(ir)]);
+                       return(0);
+               case subu_op:
+                       if (MIPSInst_FD(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (s32)((u32)regs->regs[MIPSInst_RS(ir)] -
+                                             (u32)regs->regs[MIPSInst_RT(ir)]);
+                       return(0);
+#ifdef CONFIG_64BIT
+               case dsll_op:
+                       if (MIPSInst_RS(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (s64)(((u64)regs->regs[MIPSInst_RT(ir)]) <<
+                                               MIPSInst_FD(ir));
+                       return(0);
+               case dsrl_op:
+                       if (MIPSInst_RS(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (s64)(((u64)regs->regs[MIPSInst_RT(ir)]) >>
+                                               MIPSInst_FD(ir));
+                       return(0);
+               case daddu_op:
+                       if (MIPSInst_FD(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (u64)regs->regs[MIPSInst_RS(ir)] +
+                                       (u64)regs->regs[MIPSInst_RT(ir)];
+                       return(0);
+               case dsubu_op:
+                       if (MIPSInst_FD(ir))
+                               break;
+                       if (MIPSInst_RD(ir))
+                               regs->regs[MIPSInst_RD(ir)] =
+                                       (s64)((u64)regs->regs[MIPSInst_RS(ir)] -
+                                             (u64)regs->regs[MIPSInst_RT(ir)]);
+                       return(0);
+#endif
+               }
+               break;
+       }
+       return SIGILL;
+}
+
+static int movf_func(struct pt_regs *regs, u32 ir)
+{
+       u32 csr;
+       u32 cond;
+
+       csr = current->thread.fpu.fcr31;
+       cond = fpucondbit[MIPSInst_RT(ir) >> 2];
+       if (((csr & cond) == 0) && MIPSInst_RD(ir))
+               regs->regs[MIPSInst_RD(ir)] = regs->regs[MIPSInst_RS(ir)];
+       MIPS_R2_STATS(movs);
+       return(0);
+}
+
+static int movt_func(struct pt_regs *regs, u32 ir)
+{
+       u32 csr;
+       u32 cond;
+
+       csr = current->thread.fpu.fcr31;
+       cond = fpucondbit[MIPSInst_RT(ir) >> 2];
+       if (((csr & cond) != 0) && MIPSInst_RD(ir))
+               regs->regs[MIPSInst_RD(ir)] = regs->regs[MIPSInst_RS(ir)];
+       MIPS_R2_STATS(movs);
+       return(0);
+}
+
+static int jr_func(struct pt_regs *regs, u32 ir)
+{
+       int err;
+       unsigned long epc;
+       unsigned long cpc;
+       unsigned long nepc;
+       u32 nir;
+
+       if (delay_slot(regs))
+               return(SIGILL);
+       nepc = regs->cp0_epc;
+       regs->cp0_epc -= 4;
+       epc = regs->cp0_epc;
+       err = __compute_return_epc(regs);
+       if (err < 0)
+               return(SIGEMT);
+       cpc = regs->cp0_epc;
+       err = __get_user(nir, (u32 __user *)nepc);
+       if (err)
+               return(SIGSEGV);
+       MIPS_R2BR_STATS(jrs);
+       if (nir) {  /* NOP is easy */
+               /* Negative err means FPU instruction in BD-slot,
+                  Zero err means 'BD-slot emulation done' */
+               if ((err = mipsr6_emul(regs,nir)) > 0) {
+                       err = mips_dsemul(regs, nir, cpc);
+                       if (err == SIGILL)
+                               err = SIGEMT;
+                       MIPS_R2_STATS(dsemul);
+               }
+       }
+       return (err);
+}
+
+static int movz_func(struct pt_regs *regs, u32 ir)
+{
+       if (((regs->regs[MIPSInst_RT(ir)]) == 0) && MIPSInst_RD(ir))
+               regs->regs[MIPSInst_RD(ir)] = regs->regs[MIPSInst_RS(ir)];
+       MIPS_R2_STATS(movs);
+       return(0);
+}
+
+static int movn_func(struct pt_regs *regs, u32 ir)
+{
+       if (((regs->regs[MIPSInst_RT(ir)]) != 0) && MIPSInst_RD(ir))
+               regs->regs[MIPSInst_RD(ir)] = regs->regs[MIPSInst_RS(ir)];
+       MIPS_R2_STATS(movs);
+       return(0);
+}
+
+static int mfhi_func(struct pt_regs *regs, u32 ir)
+{
+       if (MIPSInst_RD(ir))
+               regs->regs[MIPSInst_RD(ir)] = regs->hi;
+       MIPS_R2_STATS(hilo);
+       return(0);
+}
+
+static int mthi_func(struct pt_regs *regs, u32 ir)
+{
+       regs->hi = regs->regs[MIPSInst_RS(ir)];
+       MIPS_R2_STATS(hilo);
+       return(0);
+}
+
+static int mflo_func(struct pt_regs *regs, u32 ir)
+{
+       if (MIPSInst_RD(ir))
+               regs->regs[MIPSInst_RD(ir)] = regs->lo;
+       MIPS_R2_STATS(hilo);
+       return(0);
+}
+
+static int mtlo_func(struct pt_regs *regs, u32 ir)
+{
+       regs->lo = regs->regs[MIPSInst_RS(ir)];
+       MIPS_R2_STATS(hilo);
+       return(0);
+}
+
+static int mult_func(struct pt_regs *regs, u32 ir)
+{
+       s64 res;
+       s32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = (s64)rt * (s64)rs;
+
+       rs = res;
+       regs->lo = (s64)rs;
+       rt = res >> 32;
+       res = (s64)rt;
+       regs->hi = res;
+       MIPS_R2_STATS(muls);
+       return(0);
+}
+
+static int multu_func(struct pt_regs *regs, u32 ir)
+{
+       u64 res;
+       u32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = (u64)rt * (u64)rs;
+       rt = res;
+       regs->lo = (s64)rt;
+       regs->hi = (s64)(res >> 32);
+       MIPS_R2_STATS(muls);
+       return(0);
+}
+
+static int div_func(struct pt_regs *regs, u32 ir)
+{
+       s32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+
+       regs->lo = (s64)(rs / rt);
+       regs->hi = (s64)(rs % rt);
+       MIPS_R2_STATS(divs);
+       return(0);
+}
+
+static int divu_func(struct pt_regs *regs, u32 ir)
+{
+       u32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+
+       regs->lo = (s64)(rs / rt);
+       regs->hi = (s64)(rs % rt);
+       MIPS_R2_STATS(divs);
+       return(0);
+}
+
+#ifdef CONFIG_64BIT
+static int dmult_func(struct pt_regs *regs, u32 ir)
+{
+       s64 res;
+       s64 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = rt * rs;
+
+       regs->lo = res;
+       __asm__ __volatile__("dmuh %0, %1, %2" : "=r"(res) : "r"(rt), "r"(rs));
+       regs->hi = res;
+       MIPS_R2_STATS(muls);
+       return(0);
+}
+
+static int dmultu_func(struct pt_regs *regs, u32 ir)
+{
+       u64 res;
+       u64 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = rt * rs;
+
+       regs->lo = res;
+       __asm__ __volatile__("dmuhu %0, %1, %2" : "=r"(res) : "r"(rt), "r"(rs));
+       regs->hi = res;
+       MIPS_R2_STATS(muls);
+       return(0);
+}
+
+static int ddiv_func(struct pt_regs *regs, u32 ir)
+{
+       s64 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+
+       regs->lo = rs / rt;
+       regs->hi = rs % rt;
+       MIPS_R2_STATS(divs);
+       return(0);
+}
+
+static int ddivu_func(struct pt_regs *regs, u32 ir)
+{
+       u64 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+
+       regs->lo = rs / rt;
+       regs->hi = rs % rt;
+       MIPS_R2_STATS(divs);
+       return(0);
+}
+#endif
+
+static struct r2_decoder_table spec_op_table[] = {
+       { 0xfc1ff83f, 0x00000008, jr_func },
+       { 0xfc00ffff, 0x00000018, mult_func },  /* case AC=0 only */
+       { 0xfc00ffff, 0x00000019, multu_func }, /* case AC=0 only */
+#ifdef CONFIG_64BIT
+       { 0xfc00ffff, 0x0000001c, dmult_func },  /* case AC=0 only */
+       { 0xfc00ffff, 0x0000001d, dmultu_func }, /* case AC=0 only */
+#endif
+       { 0xffff07ff, 0x00000010, mfhi_func },
+       { 0xfc1fffff, 0x00000011, mthi_func },
+       { 0xffff07ff, 0x00000012, mflo_func },
+       { 0xfc1fffff, 0x00000013, mtlo_func },
+       { 0xfc0307ff, 0x00000001, movf_func },
+       { 0xfc0307ff, 0x00010001, movt_func },
+       { 0xfc0007ff, 0x0000000a, movz_func },
+       { 0xfc0007ff, 0x0000000b, movn_func },
+       { 0xfc00ffff, 0x0000001a, div_func },
+       { 0xfc00ffff, 0x0000001b, divu_func },
+#ifdef CONFIG_64BIT
+       { 0xfc00ffff, 0x0000001e, ddiv_func },
+       { 0xfc00ffff, 0x0000001f, ddivu_func },
+#endif
+       { }
+};
+
+static int madd_func(struct pt_regs *regs, u32 ir)
+{
+       s64 res;
+       s32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = (s64)rt * (s64)rs;
+       rt = regs->hi;
+       rs = regs->lo;
+       res += ((((s64)rt) << 32) | (u32)rs);
+
+       rt = res;
+       regs->lo = (s64)rt;
+       rs = res >> 32;
+       regs->hi = (s64)rs;
+       MIPS_R2_STATS(dsps);
+       return(0);
+}
+
+static int maddu_func(struct pt_regs *regs, u32 ir)
+{
+       u64 res;
+       u32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = (u64)rt * (u64)rs;
+       rt = regs->hi;
+       rs = regs->lo;
+       res += ((((s64)rt) << 32) | (u32)rs);
+
+       rt = res;
+       regs->lo = (s64)rt;
+       rs = res >> 32;
+       regs->hi = (s64)rs;
+       MIPS_R2_STATS(dsps);
+       return(0);
+}
+
+static int msub_func(struct pt_regs *regs, u32 ir)
+{
+       s64 res;
+       s32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = (s64)rt * (s64)rs;
+       rt = regs->hi;
+       rs = regs->lo;
+       res = ((((s64)rt) << 32) | (u32)rs) - res;
+
+       rt = res;
+       regs->lo = (s64)rt;
+       rs = res >> 32;
+       regs->hi = (s64)rs;
+       MIPS_R2_STATS(dsps);
+       return(0);
+}
+
+static int msubu_func(struct pt_regs *regs, u32 ir)
+{
+       u64 res;
+       u32 rt, rs;
+
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = (u64)rt * (u64)rs;
+       rt = regs->hi;
+       rs = regs->lo;
+       res = ((((s64)rt) << 32) | (u32)rs) - res;
+
+       rt = res;
+       regs->lo = (s64)rt;
+       rs = res >> 32;
+       regs->hi = (s64)rs;
+       MIPS_R2_STATS(dsps);
+       return(0);
+}
+
+static int mul_func(struct pt_regs *regs, u32 ir)
+{
+       s64 res;
+       s32 rt, rs;
+
+       if (!MIPSInst_RD(ir))
+               return(0);
+       rt = regs->regs[MIPSInst_RT(ir)];
+       rs = regs->regs[MIPSInst_RS(ir)];
+       res = (s64)rt * (s64)rs;
+
+       rs = res;
+       regs->regs[MIPSInst_RD(ir)] = (s64)rs;
+       MIPS_R2_STATS(muls);
+       return(0);
+}
+
+static int clz_func(struct pt_regs *regs, u32 ir)
+{
+       u32 res;
+       u32 rs;
+
+       if (!MIPSInst_RD(ir))
+               return(0);
+       rs = regs->regs[MIPSInst_RS(ir)];
+       __asm__ __volatile__("clz %0, %1" : "=r"(res) : "r"(rs));
+       regs->regs[MIPSInst_RD(ir)] = res;
+       MIPS_R2_STATS(bops);
+       return(0);
+}
+
+static int clo_func(struct pt_regs *regs, u32 ir)
+{
+       u32 res;
+       u32 rs;
+
+       if (!MIPSInst_RD(ir))
+               return(0);
+       rs = regs->regs[MIPSInst_RS(ir)];
+       __asm__ __volatile__("clo %0, %1" : "=r"(res) : "r"(rs));
+       regs->regs[MIPSInst_RD(ir)] = res;
+       MIPS_R2_STATS(bops);
+       return(0);
+}
+
+#ifdef CONFIG_64BIT
+static int dclz_func(struct pt_regs *regs, u32 ir)
+{
+       u64 res;
+       u64 rs;
+
+       if (!MIPSInst_RD(ir))
+               return(0);
+       rs = regs->regs[MIPSInst_RS(ir)];
+       __asm__ __volatile__("dclz %0, %1" : "=r"(res) : "r"(rs));
+       regs->regs[MIPSInst_RD(ir)] = res;
+       MIPS_R2_STATS(bops);
+       return(0);
+}
+
+static int dclo_func(struct pt_regs *regs, u32 ir)
+{
+       u64 res;
+       u64 rs;
+
+       if (!MIPSInst_RD(ir))
+               return(0);
+       rs = regs->regs[MIPSInst_RS(ir)];
+       __asm__ __volatile__("dclo %0, %1" : "=r"(res) : "r"(rs));
+       regs->regs[MIPSInst_RD(ir)] = res;
+       MIPS_R2_STATS(bops);
+       return(0);
+}
+#endif
+
+static struct r2_decoder_table spec2_op_table[] = {
+       { 0xfc00ffff, 0x70000000, madd_func },
+       { 0xfc00ffff, 0x70000001, maddu_func },
+       { 0xfc0007ff, 0x70000002, mul_func },
+       { 0xfc00ffff, 0x70000004, msub_func },  /* case AC=0 only */
+       { 0xfc00ffff, 0x70000005, msubu_func }, /* case AC=0 only */
+       { 0xfc0007ff, 0x70000020, clz_func },
+       { 0xfc0007ff, 0x70000021, clo_func },
+#ifdef CONFIG_64BIT
+       { 0xfc0007ff, 0x70000024, dclz_func },
+       { 0xfc0007ff, 0x70000025, dclo_func },
+#endif
+/*         { 0xfc00003f, 0x7000003f, sdbbp_func }, - no SDBBP support in R2 emulation */
+       { }
+};
+
+static inline int mipsr2_find_ex(struct pt_regs *regs, u32 inst, struct r2_decoder_table *table)
+{
+       struct r2_decoder_table *p;
+       int err;
+
+       for (p=table; p->func; p++) {
+               if ((inst & p->mask) == p->code) {
+                       err = (p->func)(regs, inst);
+                       return(err);
+               }
+       }
+       return(SIGILL);
+}
+
+int mipsr2_decoder(struct pt_regs *regs, u32 instruction)
+{
+       int err = 0;
+       unsigned long vaddr;
+       u32 inst = instruction;
+       u32 nir;
+       unsigned long r31;
+       unsigned long epc;
+       unsigned long nepc;
+       unsigned long cpc;
+       unsigned long rt;
+       unsigned long rs;
+       unsigned long res;
+       void __user *fault_addr = NULL;
+       int pass = 0;
+
+repeat:
+       r31 = regs->regs[31];
+       epc = regs->cp0_epc;
+       err = compute_return_epc(regs);
+       if (err < 0)
+               return(SIGEMT);
+
+       switch (MIPSInst_OPCODE(inst)) {
+
+       case spec_op:
+               err = mipsr2_find_ex(regs, inst, spec_op_table);
+               if (err < 0) {
+                       /* FPU instruction under JR */
+                       regs->cp0_cause |= CAUSEF_BD;
+                       goto fpu_emul;
+               }
+               break;
+
+       case spec2_op:
+               err = mipsr2_find_ex(regs, inst, spec2_op_table);
+               break;
+
+       case bcond_op:
+               rt = MIPSInst_RT(inst);
+               rs = MIPSInst_RS(inst);
+               switch (rt) {
+               case tgei_op:
+                       if ((long)regs->regs[rs] >= MIPSInst_SIMM(inst))
+                               do_trap_or_bp(regs, 0, "TGEI");
+                       MIPS_R2_STATS(traps);
+                       break;
+               case tgeiu_op:
+                       if (regs->regs[rs] >= MIPSInst_UIMM(inst))
+                               do_trap_or_bp(regs, 0, "TGEIU");
+                       MIPS_R2_STATS(traps);
+                       break;
+               case tlti_op:
+                       if ((long)regs->regs[rs] < MIPSInst_SIMM(inst))
+                               do_trap_or_bp(regs, 0, "TLTI");
+                       MIPS_R2_STATS(traps);
+                       break;
+               case tltiu_op:
+                       if (regs->regs[rs] < MIPSInst_UIMM(inst))
+                               do_trap_or_bp(regs, 0, "TLTIU");
+                       MIPS_R2_STATS(traps);
+                       break;
+               case teqi_op:
+                       if (regs->regs[rs] == MIPSInst_SIMM(inst))
+                               do_trap_or_bp(regs, 0, "TEQI");
+                       MIPS_R2_STATS(traps);
+                       break;
+               case tnei_op:
+                       if (regs->regs[rs] != MIPSInst_SIMM(inst))
+                               do_trap_or_bp(regs, 0, "TNEI");
+                       MIPS_R2_STATS(traps);
+                       break;
+               case bltzl_op:
+               case bgezl_op:
+               case bltzall_op:
+               case bgezall_op:
+                       if (delay_slot(regs)) {
+                               err = SIGILL;
+                               break;
+                       }
+                       regs->regs[31] = r31;
+                       regs->cp0_epc = epc;
+                       err = __compute_return_epc(regs);
+                       if (err < 0)
+                               return(SIGEMT);
+                       if (err != BRANCH_LIKELY_TAKEN)
+                               break;
+                       cpc = regs->cp0_epc;
+                       nepc = epc + 4;
+                       err = __get_user(nir, (u32 __user *)nepc);
+                       if (err) {
+                               err = SIGSEGV;
+                               break;
+                       }
+#ifdef CONFIG_DEBUG_FS
+                       switch (rt) {
+                       case bltzl_op:
+                               MIPS_R2BR_STATS(bltzl);
+                               break;
+                       case bgezl_op:
+                               MIPS_R2BR_STATS(bgezl);
+                               break;
+                       case bltzall_op:
+                               MIPS_R2BR_STATS(bltzall);
+                               break;
+                       case bgezall_op:
+                               MIPS_R2BR_STATS(bgezall);
+                               break;
+                       }
+#endif
+                       switch (MIPSInst_OPCODE(nir)) {
+                       case cop1_op:
+                       case cop1x_op:
+                       case lwc1_op:
+                       case swc1_op:
+                               regs->cp0_cause |= CAUSEF_BD;
+                               goto fpu_emul;
+                       }
+                       if (nir) {  /* NOP is easy */
+                               if ((err = mipsr6_emul(regs,nir)) > 0) {
+                                       err = mips_dsemul(regs, nir, cpc);
+                                       if (err == SIGILL)
+                                               err = SIGEMT;
+                                       MIPS_R2_STATS(dsemul);
+                               }
+                       }
+                       break;
+               case bltzal_op:
+               case bgezal_op:
+                       if (delay_slot(regs)) {
+                               err = SIGILL;
+                               break;
+                       }
+                       regs->regs[31] = r31;
+                       regs->cp0_epc = epc;
+                       err = __compute_return_epc(regs);
+                       if (err < 0)
+                               return(SIGEMT);
+                       cpc = regs->cp0_epc;
+                       nepc = epc + 4;
+                       err = __get_user(nir, (u32 __user *)nepc);
+                       if (err) {
+                               err = SIGSEGV;
+                               break;
+                       }
+#ifdef CONFIG_DEBUG_FS
+                       switch (rt) {
+                       case bltzal_op:
+                               MIPS_R2BR_STATS(bltzal);
+                               break;
+                       case bgezal_op:
+                               MIPS_R2BR_STATS(bgezal);
+                               break;
+                       }
+#endif
+                       switch (MIPSInst_OPCODE(nir)) {
+                       case cop1_op:
+                       case cop1x_op:
+                       case lwc1_op:
+                       case swc1_op:
+                               regs->cp0_cause |= CAUSEF_BD;
+                               goto fpu_emul;
+                       }
+                       if (nir) {  /* NOP is easy */
+                               if ((err = mipsr6_emul(regs,nir)) > 0) {
+                                       err = mips_dsemul(regs, nir, cpc);
+                                       if (err == SIGILL)
+                                               err = SIGEMT;
+                                       MIPS_R2_STATS(dsemul);
+                               }
+                       }
+                       break;
+               default:
+                       regs->regs[31] = r31;
+                       regs->cp0_epc = epc;
+                       err = SIGILL;
+                       break;
+               }
+               break;
+
+       case beql_op:
+       case bnel_op:
+       case blezl_op:
+       case bgtzl_op:
+               if (delay_slot(regs)) {
+                       err = SIGILL;
+                       break;
+               }
+               regs->regs[31] = r31;
+               regs->cp0_epc = epc;
+               err = __compute_return_epc(regs);
+               if (err < 0)
+                       return(SIGEMT);
+               if (err != BRANCH_LIKELY_TAKEN)
+                       break;
+               cpc = regs->cp0_epc;
+               nepc = epc + 4;
+               err = __get_user(nir, (u32 __user *)nepc);
+               if (err) {
+                       err = SIGSEGV;
+                       break;
+               }
+#ifdef CONFIG_DEBUG_FS
+               switch (MIPSInst_OPCODE(inst)) {
+               case beql_op:
+                       MIPS_R2BR_STATS(beql);
+                       break;
+               case bnel_op:
+                       MIPS_R2BR_STATS(bnel);
+                       break;
+               case blezl_op:
+                       MIPS_R2BR_STATS(blezl);
+                       break;
+               case bgtzl_op:
+                       MIPS_R2BR_STATS(bgtzl);
+                       break;
+               }
+#endif
+               switch (MIPSInst_OPCODE(nir)) {
+               case cop1_op:
+               case cop1x_op:
+               case lwc1_op:
+               case swc1_op:
+                       regs->cp0_cause |= CAUSEF_BD;
+                       goto fpu_emul;
+               }
+               if (nir) {  /* NOP is easy */
+                       if ((err = mipsr6_emul(regs,nir)) > 0) {
+                               err = mips_dsemul(regs, nir, cpc);
+                               if (err == SIGILL)
+                                       err = SIGEMT;
+                               MIPS_R2_STATS(dsemul);
+                       }
+               }
+               break;
+       case lwc1_op:
+       case swc1_op:
+       case cop1_op:
+       case cop1x_op:
+fpu_emul:
+               regs->regs[31] = r31;
+               regs->cp0_epc = epc;
+               /* all cop1/cop1x RI in R6 are a removed R5 FPU instructions */
+               if (!used_math()) {     /* First time FPU user.  */
+                       err = init_fpu();
+                       set_used_math();
+               }
+               lose_fpu(1);    /* Save FPU state for the emulator. */
+
+               err = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0,
+                                              &fault_addr);
+
+               /* this is a tricky issue - lose_fpu() uses LL/SC atomics
+                  if FPU is owned and effectively cancels user level LL/SC.
+                  So, it could be logical to don't restore FPU ownership here.
+                  But the sequence of multiple FPU instructions is much much
+                  more often than LL-FPU-SC and I prefer loop here until
+                  next scheduler cycle cancels FPU ownership. */
+               own_fpu(1);     /* Restore FPU state. */
+
+               if (err)
+                       current->thread.cp0_baduaddr = (unsigned long)fault_addr;
+
+               MIPS_R2_STATS(fpus);
+               break;;
+
+       case lwl_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_READ, vaddr, 4)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                       "1:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 24, 8          \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                       "2:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 16, 8          \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                       "3:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 8, 8           \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                       "4:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 0, 8           \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                       "1:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 24, 8          \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "2:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 16, 8          \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "3:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 8, 8           \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "4:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 0, 8           \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:     sll     %0, %0, 0               \n"
+                       "10:                                    \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      10b                      \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV));
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = rt;
+
+               MIPS_R2_STATS(loads);
+               break;
+
+       case lwr_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_READ, vaddr, 4)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                       "1:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 0, 8           \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "2:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 8, 8           \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "3:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 16, 8          \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "4:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 24, 8          \n"
+                       "       sll     %0, %0, 0               \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                       "1:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 0, 8           \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                       "2:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 8, 8           \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                       "3:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 16, 8          \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                       "4:"    LB      "%1, 0(%2)              \n"
+                               INS     "%0, %1, 24, 8          \n"
+                       "       sll     %0, %0, 0               \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:                                     \n"
+                       "10:                                    \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      10b                      \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV));
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = rt;
+
+               MIPS_R2_STATS(loads);
+               break;
+
+       case swl_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_WRITE, vaddr, 4)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                               EXT     "%1, %0, 24, 8          \n"
+                       "1:"    SB      "%1, 0(%2)              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                               EXT     "%1, %0, 16, 8          \n"
+                       "2:"    SB      "%1, 0(%2)              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                               EXT     "%1, %0, 8, 8           \n"
+                       "3:"    SB      "%1, 0(%2)              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                               EXT     "%1, %0, 0, 8           \n"
+                       "4:"    SB      "%1, 0(%2)              \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                               EXT     "%1, %0, 24, 8          \n"
+                       "1:"    SB      "%1, 0(%2)              \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               EXT     "%1, %0, 16, 8          \n"
+                       "2:"    SB      "%1, 0(%2)              \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               EXT     "%1, %0, 8, 8           \n"
+                       "3:"    SB      "%1, 0(%2)              \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               EXT     "%1, %0, 0, 8           \n"
+                       "4:"    SB      "%1, 0(%2)              \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:                                     \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      9b                       \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV)
+                       : "memory");
+
+               MIPS_R2_STATS(stores);
+               break;
+
+       case swr_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_WRITE, vaddr, 4)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                               EXT     "%1, %0, 0, 8           \n"
+                       "1:"    SB      "%1, 0(%2)              \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               EXT     "%1, %0, 8, 8           \n"
+                       "2:"    SB      "%1, 0(%2)              \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               EXT     "%1, %0, 16, 8          \n"
+                       "3:"    SB      "%1, 0(%2)              \n"
+                               ADDIU   "%2, %2, 1              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               EXT     "%1, %0, 24, 8          \n"
+                       "4:"    SB      "%1, 0(%2)              \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                               EXT     "%1, %0, 0, 8           \n"
+                       "1:"    SB      "%1, 0(%2)              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                               EXT     "%1, %0, 8, 8           \n"
+                       "2:"    SB      "%1, 0(%2)              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                               EXT     "%1, %0, 16, 8          \n"
+                       "3:"    SB      "%1, 0(%2)              \n"
+                       "       andi    %1, %2, 0x3             \n"
+                       "       beq     $0, %1, 9f              \n"
+                               ADDIU   "%2, %2, -1             \n"
+                               EXT     "%1, %0, 24, 8          \n"
+                       "4:"    SB      "%1, 0(%2)              \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:                                     \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      9b                       \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV)
+                       : "memory");
+
+               MIPS_R2_STATS(stores);
+               break;
+
+#ifdef CONFIG_64BIT
+       case ldl_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_READ, vaddr, 8)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                       "1:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 56, 8           \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "2:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 48, 8           \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "3:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 40, 8           \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "4:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 32, 8           \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "5:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 24, 8           \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "6:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 16, 8           \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "7:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 8, 8            \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "0:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 0, 8            \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                       "1:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 56, 8           \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "2:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 48, 8           \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "3:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 40, 8           \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "4:     lb      %1, 0(%2)               \n"
+                       "       dinsu   %0, %1, 32, 8           \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "5:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 24, 8           \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "6:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 16, 8           \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "7:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 8, 8            \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "0:     lb      %1, 0(%2)               \n"
+                       "       dins    %0, %1, 0, 8            \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:                                     \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      9b                       \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .word  5b,8b                    \n"
+                       "       .word  6b,8b                    \n"
+                       "       .word  7b,8b                    \n"
+                       "       .word  0b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV));
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = rt;
+
+               MIPS_R2_STATS(loads);
+               break;
+
+       case ldr_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_READ, vaddr, 8)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                       "1:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 0, 8             \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "2:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 8, 8             \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "3:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 16, 8            \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "4:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 24, 8            \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "5:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 32, 8          \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "6:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 40, 8          \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "7:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 48, 8          \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "0:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 56, 8          \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                       "1:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 0, 8             \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "2:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 8, 8             \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "3:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 16, 8            \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "4:     lb      %1, 0(%2)               \n"
+                       "       dins   %0, %1, 24, 8            \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "5:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 32, 8          \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "6:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 40, 8          \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "7:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 48, 8          \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "0:     lb      %1, 0(%2)               \n"
+                       "       dinsu    %0, %1, 56, 8          \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:                                     \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      9b                       \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .word  5b,8b                    \n"
+                       "       .word  6b,8b                    \n"
+                       "       .word  7b,8b                    \n"
+                       "       .word  0b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV));
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = rt;
+
+               MIPS_R2_STATS(loads);
+               break;
+
+       case sdl_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_WRITE, vaddr, 8)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                       "       dextu   %1, %0, 56, 8           \n"
+                       "1:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dextu   %1, %0, 48, 8           \n"
+                       "2:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dextu   %1, %0, 40, 8           \n"
+                       "3:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dextu   %1, %0, 32, 8           \n"
+                       "4:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dext    %1, %0, 24, 8           \n"
+                       "5:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dext    %1, %0, 16, 8           \n"
+                       "6:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dext    %1, %0, 8, 8            \n"
+                       "7:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dext    %1, %0, 0, 8            \n"
+                       "0:     sb      %1, 0(%2)               \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                       "       dextu   %1, %0, 56, 8           \n"
+                       "1:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dextu   %1, %0, 48, 8           \n"
+                       "2:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dextu   %1, %0, 40, 8           \n"
+                       "3:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dextu   %1, %0, 32, 8           \n"
+                       "4:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dext    %1, %0, 24, 8           \n"
+                       "5:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dext    %1, %0, 16, 8           \n"
+                       "6:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dext    %1, %0, 8, 8            \n"
+                       "7:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dext    %1, %0, 0, 8            \n"
+                       "0:     sb      %1, 0(%2)               \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:                                     \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      9b                       \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .word  5b,8b                    \n"
+                       "       .word  6b,8b                    \n"
+                       "       .word  7b,8b                    \n"
+                       "       .word  0b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV)
+                       : "memory");
+
+               MIPS_R2_STATS(stores);
+               break;
+
+       case sdr_op:
+               rt = regs->regs[MIPSInst_RT(inst)];
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (!access_ok(VERIFY_WRITE, vaddr, 8)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGSEGV;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "       .set    push                    \n"
+                       "       .set    reorder                 \n"
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+                       "       dext    %1, %0, 0, 8            \n"
+                       "1:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dext    %1, %0, 8, 8            \n"
+                       "2:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dext    %1, %0, 16, 8           \n"
+                       "3:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dext    %1, %0, 24, 8           \n"
+                       "4:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dextu   %1, %0, 32, 8           \n"
+                       "5:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dextu   %1, %0, 40, 8           \n"
+                       "6:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dextu   %1, %0, 48, 8           \n"
+                       "7:     sb      %1, 0(%2)               \n"
+                       "       daddiu  %2, %2, 1               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       dextu   %1, %0, 56, 8           \n"
+                       "0:     sb      %1, 0(%2)               \n"
+#else /* !CONFIG_CPU_LITTLE_ENDIAN */
+                       "       dext    %1, %0, 0, 8            \n"
+                       "1:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dext    %1, %0, 8, 8            \n"
+                       "2:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dext    %1, %0, 16, 8           \n"
+                       "3:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dext    %1, %0, 24, 8           \n"
+                       "4:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dextu   %1, %0, 32, 8           \n"
+                       "5:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dextu   %1, %0, 40, 8           \n"
+                       "6:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dextu   %1, %0, 48, 8           \n"
+                       "7:     sb      %1, 0(%2)               \n"
+                       "       andi    %1, %2, 0x7             \n"
+                       "       beq     $0, %1, 9f              \n"
+                       "       daddiu  %2, %2, -1              \n"
+                       "       dextu   %1, %0, 56, 8           \n"
+                       "0:     sb      %1, 0(%2)               \n"
+#endif /* CONFIG_CPU_LITTLE_ENDIAN */
+                       "9:                                     \n"
+                       "       .insn                           \n"
+                       "       .section        .fixup,\"ax\"   \n"
+                       "8:     li     %3,%4                    \n"
+                       "       j      9b                       \n"
+                       "       .previous                       \n"
+                       "       .section        __ex_table,\"a\"\n"
+                       "       .word  1b,8b                    \n"
+                       "       .word  2b,8b                    \n"
+                       "       .word  3b,8b                    \n"
+                       "       .word  4b,8b                    \n"
+                       "       .word  5b,8b                    \n"
+                       "       .word  6b,8b                    \n"
+                       "       .word  7b,8b                    \n"
+                       "       .word  0b,8b                    \n"
+                       "       .previous                       \n"
+                       "       .set    pop                     \n"
+                       : "+&r"(rt), "=&r"(rs),
+                         "+&r"(vaddr), "+&r"(err)
+                       : "i"(SIGSEGV)
+                       : "memory");
+
+               MIPS_R2_STATS(stores);
+               break;
+#endif
+       case ll_op:
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (vaddr & 0x3) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               if (!access_ok(VERIFY_READ, vaddr, 4)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "1:                                 \n"
+                       LL  "   %0, 0(%2)                   \n"
+                       "2:                                 \n"
+                       ".insn                              \n"
+                       ".section        .fixup,\"ax\"      \n"
+                       "3:                                 \n"
+                       "li     %1, %3                      \n"
+                       "j      2b                          \n"
+                       ".previous                          \n"
+                       ".section        __ex_table,\"a\"   \n"
+                       ".word  1b, 3b                      \n"
+                       ".previous                          \n"
+                       :"=&r"(res), "+&r"(err)
+                       :"r"(vaddr), "i"(SIGSEGV)
+                       :"memory");
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = res;
+               MIPS_R2_STATS(llsc);
+               break;
+
+       case sc_op:
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (vaddr & 0x3) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               if (!access_ok(VERIFY_WRITE, vaddr, 4)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               res = regs->regs[MIPSInst_RT(inst)];
+               __asm__ __volatile__(
+                       "1:                                 \n"
+                       SC  "   %0, 0(%2)                   \n"
+                       "2:                                 \n"
+                       ".insn                              \n"
+                       ".section        .fixup,\"ax\"      \n"
+                       "3:                                 \n"
+                       "li     %1, %3                      \n"
+                       "j      2b                          \n"
+                       ".previous                          \n"
+                       ".section        __ex_table,\"a\"   \n"
+                       ".word  1b, 3b                      \n"
+                       ".previous                          \n"
+                       :"+&r"(res), "+&r"(err)
+                       :"r"(vaddr), "i"(SIGSEGV)
+                       :"memory");
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = res;
+               MIPS_R2_STATS(llsc);
+               break;
+
+#ifdef CONFIG_64BIT
+       case lld_op:
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (vaddr & 0x7) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               if (!access_ok(VERIFY_READ, vaddr, 8)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               __asm__ __volatile__(
+                       "1:                                 \n"
+                       "lld    %0, 0(%2)                   \n"
+                       "2:                                 \n"
+                       ".insn                              \n"
+                       ".section        .fixup,\"ax\"      \n"
+                       "3:                                 \n"
+                       "li     %1, %3                      \n"
+                       "j      2b                          \n"
+                       ".previous                          \n"
+                       ".section        __ex_table,\"a\"   \n"
+                       ".word  1b, 3b                      \n"
+                       ".previous                          \n"
+                       :"=&r"(res), "+&r"(err)
+                       :"r"(vaddr), "i"(SIGSEGV)
+                       :"memory");
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = res;
+               MIPS_R2_STATS(llsc);
+               break;
+
+       case scd_op:
+               vaddr = regs->regs[MIPSInst_RS(inst)] + MIPSInst_SIMM(inst);
+               if (vaddr & 0x7) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               if (!access_ok(VERIFY_WRITE, vaddr, 8)) {
+                       current->thread.cp0_baduaddr = vaddr;
+                       err = SIGBUS;
+                       break;
+               }
+               res = regs->regs[MIPSInst_RT(inst)];
+               __asm__ __volatile__(
+                       "1:                                 \n"
+                       "scd    %0, 0(%2)                   \n"
+                       "2:                                 \n"
+                       ".insn                              \n"
+                       ".section        .fixup,\"ax\"      \n"
+                       "3:                                 \n"
+                       "li     %1, %3                      \n"
+                       "j      2b                          \n"
+                       ".previous                          \n"
+                       ".section        __ex_table,\"a\"   \n"
+                       ".word  1b, 3b                      \n"
+                       ".previous                          \n"
+                       :"+&r"(res), "+&r"(err)
+                       :"r"(vaddr), "i"(SIGSEGV)
+                       :"memory");
+               if (MIPSInst_RT(inst) && !err)
+                       regs->regs[MIPSInst_RT(inst)] = res;
+               MIPS_R2_STATS(llsc);
+               break;
+#endif
+       case pref_op:
+               /* skip it */
+               break;
+
+       default:
+               err = SIGILL;
+               break;
+       }
+
+       if ((!err) && (pass++ < TOTAL_PASS)) {
+               regs->cp0_cause &= ~CAUSEF_BD;
+               err = get_user(inst, (u32 __user *)regs->cp0_epc);
+               if (!err)
+                       goto repeat;
+               if (err < 0)
+                       err = SIGSEGV;
+       }
+       if (err && (err != SIGEMT)) {
+               regs->regs[31] = r31;
+               regs->cp0_epc = epc;
+       }
+       /* it can be MIPS R6 instruction */
+       if (pass && (err == SIGILL))
+               err = 0;
+       return(err);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int mipsr2_stats_show(struct seq_file *s, void *unused)
+{
+
+       seq_printf(s, "Instruction\tTotal\tBDslot\n------------------------------\n");
+       seq_printf(s, "movs\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).movs.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).movs.a.counter);
+       seq_printf(s, "hilo\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).hilo.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).hilo.a.counter);
+       seq_printf(s, "muls\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).muls.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).muls.a.counter);
+       seq_printf(s, "divs\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).divs.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).divs.a.counter);
+       seq_printf(s, "dsps\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).dsps.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).dsps.a.counter);
+       seq_printf(s, "bops\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).bops.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).bops.a.counter);
+       seq_printf(s, "traps\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).traps.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).traps.a.counter);
+       seq_printf(s, "fpus\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).fpus.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).fpus.a.counter);
+       seq_printf(s, "loads\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).loads.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).loads.a.counter);
+       seq_printf(s, "stores\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).stores.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).stores.a.counter);
+       seq_printf(s, "llsc\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).llsc.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).llsc.a.counter);
+       seq_printf(s, "dsemul\t\t%ld\t%ld\n",(unsigned long)__get_cpu_var(mipsr2emustats).dsemul.a.counter,
+                                        (unsigned long)__get_cpu_var(mipsr2bdemustats).dsemul.a.counter);
+
+       seq_printf(s, "jr\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).jrs.a.counter);
+       seq_printf(s, "bltzl\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bltzl.a.counter);
+       seq_printf(s, "bgezl\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bgezl.a.counter);
+       seq_printf(s, "bltzll\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bltzll.a.counter);
+       seq_printf(s, "bgezll\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bgezll.a.counter);
+       seq_printf(s, "bltzal\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bltzal.a.counter);
+       seq_printf(s, "bgezal\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bgezal.a.counter);
+       seq_printf(s, "beql\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).beql.a.counter);
+       seq_printf(s, "bnel\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bnel.a.counter);
+       seq_printf(s, "blezl\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).blezl.a.counter);
+       seq_printf(s, "bgtzl\t\t%ld\n",(unsigned long)__get_cpu_var(mipsr2bremustats).bgtzl.a.counter);
+
+       return 0;
+}
+
+static int mipsr2_stats_clear_show(struct seq_file *s, void *unused)
+{
+       mipsr2_stats_show(s, unused);
+
+       __get_cpu_var(mipsr2emustats).movs.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).movs.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).hilo.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).hilo.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).muls.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).muls.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).divs.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).divs.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).dsps.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).dsps.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).bops.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).bops.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).traps.a.counter   = 0;
+       __get_cpu_var(mipsr2bdemustats).traps.a.counter = 0;
+       __get_cpu_var(mipsr2emustats).fpus.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).fpus.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).loads.a.counter   = 0;
+       __get_cpu_var(mipsr2bdemustats).loads.a.counter = 0;
+       __get_cpu_var(mipsr2emustats).stores.a.counter  = 0;
+       __get_cpu_var(mipsr2bdemustats).stores.a.counter= 0;
+       __get_cpu_var(mipsr2emustats).llsc.a.counter    = 0;
+       __get_cpu_var(mipsr2bdemustats).llsc.a.counter  = 0;
+       __get_cpu_var(mipsr2emustats).dsemul.a.counter  = 0;
+       __get_cpu_var(mipsr2bdemustats).dsemul.a.counter= 0;
+
+       __get_cpu_var(mipsr2bremustats).jrs.a.counter   = 0;
+       __get_cpu_var(mipsr2bremustats).bltzl.a.counter = 0;
+       __get_cpu_var(mipsr2bremustats).bgezl.a.counter = 0;
+       __get_cpu_var(mipsr2bremustats).bltzll.a.counter= 0;
+       __get_cpu_var(mipsr2bremustats).bgezll.a.counter= 0;
+       __get_cpu_var(mipsr2bremustats).bltzal.a.counter= 0;
+       __get_cpu_var(mipsr2bremustats).bgezal.a.counter= 0;
+       __get_cpu_var(mipsr2bremustats).beql.a.counter  = 0;
+       __get_cpu_var(mipsr2bremustats).bnel.a.counter  = 0;
+       __get_cpu_var(mipsr2bremustats).blezl.a.counter = 0;
+       __get_cpu_var(mipsr2bremustats).bgtzl.a.counter = 0;
+
+       return 0;
+}
+
+static int mipsr2_stats_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mipsr2_stats_show, inode->i_private);
+}
+
+static int mipsr2_stats_clear_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mipsr2_stats_clear_show, inode->i_private);
+}
+
+static const struct file_operations mipsr2_fops = {
+       .open                   = mipsr2_stats_open,
+       .read                   = seq_read,
+       .llseek                 = seq_lseek,
+       .release                = single_release,
+};
+
+static const struct file_operations mipsr2_clear_fops = {
+       .open                   = mipsr2_stats_clear_open,
+       .read                   = seq_read,
+       .llseek                 = seq_lseek,
+       .release                = single_release,
+};
+
+
+int mipsr2_init_debugfs(void)
+{
+       struct dentry           *root;
+       struct dentry           *file;
+       int                     ret;
+
+       root = debugfs_create_dir("mipsr2-emulation", NULL);
+       if (!root) {
+               ret = -ENOMEM;
+               goto err0;
+       }
+
+       file = debugfs_create_file("stats", S_IRUGO, root, NULL,
+                       &mipsr2_fops);
+       if (!file) {
+               ret = -ENOMEM;
+               goto err1;
+       }
+
+       file = debugfs_create_file("stats-clear", S_IRUGO, root, NULL,
+                       &mipsr2_clear_fops);
+       if (!file) {
+               ret = -ENOMEM;
+               goto err1;
+       }
+
+       return 0;
+
+err1:
+       debugfs_remove_recursive(root);
+
+err0:
+       return ret;
+}
+__initcall(mipsr2_init_debugfs);
+
+#endif /* CONFIG_DEBUG_FS */
index 1db5d43f13cc28d568978201380d7b8d1c99c06b..09730ff560a8f447e5556e74652841ea5dff3f05 100644 (file)
@@ -777,7 +777,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
        force_sig_info(SIGFPE, &info, current);
 }
 
-static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
+void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
        const char *str)
 {
        siginfo_t info;
@@ -966,6 +966,24 @@ asmlinkage void do_ri(struct pt_regs *regs)
        unsigned int opcode = 0;
        int status = -1;
 
+#ifdef CONFIG_MIPS_INCOMPATIBLE_ARCH_EMULATION
+       if (mipsr2_emulation && likely(user_mode(regs))) {
+               if (likely(get_user(opcode, epc) >= 0)) {
+                       status = mipsr2_decoder(regs, opcode);
+                       switch (status) {
+                       case SIGEMT:
+                       case 0:
+                               return;
+                       case SIGILL:
+                               break;
+                       default:
+                               process_fpemu_return(status, (void __user *)current->thread.cp0_baduaddr);
+                               return;
+                       }
+               }
+       }
+#endif
+
        if (notify_die(DIE_RI, "RI Fault", regs, 0, regs_to_trapnr(regs), SIGILL)
            == NOTIFY_STOP)
                return;
@@ -1153,12 +1171,14 @@ asmlinkage void do_cpu(struct pt_regs *regs)
                        status = own_fpu(1);
                else {                  /* First time FPU user.  */
                        status = init_fpu();
-#ifndef CONFIG_MIPS_INCOMPATIBLE_FPU_EMULATION
+#ifdef CONFIG_MIPS_INCOMPATIBLE_ARCH_EMULATION
+                       if (status && !mipsr2_emulation) {
+#else
                        if (status) {
+#endif
                                force_sig(SIGFPE, current);
                                return;
                        }
-#endif
 
                        set_used_math();
                }
index 48b55c456bd9ab35fbbe5fda4535aee22a339950..6d41b79c66bb3700d6a5b33cc15948dedd434c92 100644 (file)
@@ -104,7 +104,7 @@ static const unsigned char mips_rm[4] = {
 
 #if __mips >= 4
 /* convert condition code register number to csr bit */
-static const unsigned int fpucondbit[8] = {
+const unsigned int fpucondbit[8] = {
        FPU_CSR_COND0,
        FPU_CSR_COND1,
        FPU_CSR_COND2,
@@ -691,10 +691,9 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
 {
        union mips_instruction insn = (union mips_instruction)dec_insn.insn;
        unsigned int bit = 0;
+       unsigned int fcr31;
 #ifdef CONFIG_CPU_MIPSR6
        int reg;
-#else
-       unsigned int fcr31;
 #endif
 
        switch (insn.i_format.opcode) {
@@ -704,9 +703,12 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                        regs->regs[insn.r_format.rd] =
                                regs->cp0_epc + dec_insn.pc_inc +
                                dec_insn.next_pc_inc;
-                       /* Fall through */
-#ifndef CONFIG_CPU_MIPSR6
+                       *contpc = regs->regs[insn.r_format.rs];
+                       return 1;
                case jr_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       if (!mipsr2_emulation)
+                               break;
 #endif
                        *contpc = regs->regs[insn.r_format.rs];
                        return 1;
@@ -715,22 +717,37 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                break;
        case bcond_op:
                switch (insn.i_format.rt) {
+               case bltzall_op:
 #ifdef CONFIG_CPU_MIPSR6
-               case nal_op:    /* MIPSR6: nal == bltzal $0 */
-                       if (insn.i_format.rs)
+                       if (!mipsr2_emulation)
                                break;
-#else
+#endif
                case bltzal_op:
-               case bltzall_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       /* MIPSR6: nal == bltzal $0 */
+                       if (insn.i_format.rs && !mipsr2_emulation)
+                               break;
 #endif
                        regs->regs[31] = regs->cp0_epc +
                                dec_insn.pc_inc +
                                dec_insn.next_pc_inc;
-                       /* Fall through */
-               case bltz_op:
-#ifndef CONFIG_CPU_MIPSR6
+                       if ((long)regs->regs[insn.i_format.rs] < 0)
+                               *contpc = regs->cp0_epc +
+                                       dec_insn.pc_inc +
+                                       (insn.i_format.simmediate << 2);
+                       else
+                               *contpc = regs->cp0_epc +
+                                       dec_insn.pc_inc +
+                                       dec_insn.next_pc_inc;
+                       return 1;
+                       break;
+
                case bltzl_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       if (!mipsr2_emulation)
+                               break;
 #endif
+               case bltz_op:
                        if ((long)regs->regs[insn.i_format.rs] < 0)
                                *contpc = regs->cp0_epc +
                                        dec_insn.pc_inc +
@@ -741,22 +758,38 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                                        dec_insn.next_pc_inc;
                        return 1;
                        break;
+
+               case bgezall_op:
 #ifdef CONFIG_CPU_MIPSR6
-               case bal_op:    /* MIPSR6: bal == bgezal $0 */
-                       if (insn.i_format.rs)
+                       if (!mipsr2_emulation)
                                break;
-#else
+#endif
                case bgezal_op:
-               case bgezall_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       /* MIPSR6: bal == bgezal $0 */
+                       if (insn.i_format.rs && !mipsr2_emulation)
+                               break;
 #endif
                        regs->regs[31] = regs->cp0_epc +
                                dec_insn.pc_inc +
                                dec_insn.next_pc_inc;
-                       /* Fall through */
-               case bgez_op:
-#ifndef CONFIG_CPU_MIPSR6
+                       if ((long)regs->regs[insn.i_format.rs] >= 0)
+                               *contpc = regs->cp0_epc +
+                                       dec_insn.pc_inc +
+                                       (insn.i_format.simmediate << 2);
+                       else
+                               *contpc = regs->cp0_epc +
+                                       dec_insn.pc_inc +
+                                       dec_insn.next_pc_inc;
+                       return 1;
+                       break;
+
                case bgezl_op:
+#ifdef CONFIG_CPU_MIPSR6
+                       if (!mipsr2_emulation)
+                               break;
 #endif
+               case bgez_op:
                        if ((long)regs->regs[insn.i_format.rs] >= 0)
                                *contpc = regs->cp0_epc +
                                        dec_insn.pc_inc +
@@ -785,10 +818,12 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                *contpc ^= bit;
                return 1;
                break;
-       case beq_op:
-#ifndef CONFIG_CPU_MIPSR6
        case beql_op:
+#ifdef CONFIG_CPU_MIPSR6
+               if (!mipsr2_emulation)
+                       break;
 #endif
+       case beq_op:
                if (regs->regs[insn.i_format.rs] ==
                    regs->regs[insn.i_format.rt])
                        *contpc = regs->cp0_epc +
@@ -800,10 +835,12 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                                dec_insn.next_pc_inc;
                return 1;
                break;
-       case bne_op:
-#ifndef CONFIG_CPU_MIPSR6
        case bnel_op:
+#ifdef CONFIG_CPU_MIPSR6
+               if (!mipsr2_emulation)
+                       break;
 #endif
+       case bne_op:
                if (regs->regs[insn.i_format.rs] !=
                    regs->regs[insn.i_format.rt])
                        *contpc = regs->cp0_epc +
@@ -816,15 +853,18 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                return 1;
                break;
        case blez_op:
+       case blezl_op:
 #ifdef CONFIG_CPU_MIPSR6
                /*
-                *  Compact branches: blezalc, bgezalc, bgeuc
+                *  Compact branches: (blez:)  blezalc, bgezalc, bgeuc
+                *  Compact branches: (blezl:) blezc, bgezc, bgec
                 */
                if (insn.i_format.rt) {
-                       if ((insn.i_format.rs == insn.i_format.rt) ||
-                           !insn.i_format.rs)   /* blezalc, bgezalc */
-                               regs->regs[31] = regs->cp0_epc +
-                                       dec_insn.pc_inc;
+                       if (insn.i_format.opcode == blez_op)
+                               if ((insn.i_format.rs == insn.i_format.rt) ||
+                                   !insn.i_format.rs)   /* blezalc, bgezalc */
+                                       regs->regs[31] = regs->cp0_epc +
+                                               dec_insn.pc_inc;
                        *contpc = regs->cp0_epc +
                                dec_insn.pc_inc +
                                dec_insn.next_pc_inc;
@@ -832,28 +872,9 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                        break;
                }
 
-               if ((long)regs->regs[insn.i_format.rs] <= 0)
-                       *contpc = regs->cp0_epc +
-                               dec_insn.pc_inc +
-                               (insn.i_format.simmediate << 2);
-               else
-                       *contpc = regs->cp0_epc +
-                               dec_insn.pc_inc +
-                               dec_insn.next_pc_inc;
-               return 1;
-               break;
+               if ((insn.i_format.opcode != blez_op) && !mipsr2_emulation)
+                       break;
 #endif
-       case blezl_op:
-#ifdef CONFIG_CPU_MIPSR6
-               /*
-                *  Compact branches: blezc, bgezc, bgec
-                */
-               *contpc = regs->cp0_epc +
-                       dec_insn.pc_inc +
-                       dec_insn.next_pc_inc;
-               return 1;
-               break;
-#else
                if ((long)regs->regs[insn.i_format.rs] <= 0)
                        *contpc = regs->cp0_epc +
                                dec_insn.pc_inc +
@@ -864,17 +885,20 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                                dec_insn.next_pc_inc;
                return 1;
                break;
-#endif
+
        case bgtz_op:
+       case bgtzl_op:
 #ifdef CONFIG_CPU_MIPSR6
                /*
-                *  Compact branches: bltzalc, bgtzalc, bltuc
+                *  Compact branches: (bgtz:)  bltzalc, bgtzalc, bltuc
+                *  Compact branches: (bgtzl:) bltc, bltzc, bgtzc
                 */
                if (insn.i_format.rt) {
-                       if ((insn.i_format.rs == insn.i_format.rt) ||
-                           !insn.i_format.rs)   /* bltzalc, bgtzalc */
-                               regs->regs[31] = regs->cp0_epc +
-                                       dec_insn.pc_inc;
+                       if (insn.i_format.opcode == bgtz_op)
+                               if ((insn.i_format.rs == insn.i_format.rt) ||
+                                   !insn.i_format.rs)   /* bltzalc, bgtzalc */
+                                       regs->regs[31] = regs->cp0_epc +
+                                               dec_insn.pc_inc;
                        *contpc = regs->cp0_epc +
                                dec_insn.pc_inc +
                                dec_insn.next_pc_inc;
@@ -882,28 +906,9 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                        break;
                }
 
-               if ((long)regs->regs[insn.i_format.rs] > 0)
-                       *contpc = regs->cp0_epc +
-                               dec_insn.pc_inc +
-                               (insn.i_format.simmediate << 2);
-               else
-                       *contpc = regs->cp0_epc +
-                               dec_insn.pc_inc +
-                               dec_insn.next_pc_inc;
-               return 1;
-               break;
+               if ((insn.i_format.opcode != bgtz_op) && !mipsr2_emulation)
+                       break;
 #endif
-       case bgtzl_op:
-#ifdef CONFIG_CPU_MIPSR6
-               /*
-                *  Compact branches: bltc, bltzc, bgtzc
-                */
-               *contpc = regs->cp0_epc +
-                       dec_insn.pc_inc +
-                       dec_insn.next_pc_inc;
-               return 1;
-               break;
-#else
                if ((long)regs->regs[insn.i_format.rs] > 0)
                        *contpc = regs->cp0_epc +
                                dec_insn.pc_inc +
@@ -914,7 +919,6 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                                dec_insn.next_pc_inc;
                return 1;
                break;
-#endif
 
 #ifdef CONFIG_CPU_MIPSR6
        case cbcond0_op:
@@ -936,37 +940,39 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                return 1;
 #endif
 
-#ifdef CONFIG_CPU_MIPSR6
        case cop1_op:
-               if ((insn.i_format.rs != bc1eqz_op) &&
-                   (insn.i_format.rs != bc1nez_op))
-                       break;
+#ifdef CONFIG_CPU_MIPSR6
+               if ((insn.i_format.rs == bc1eqz_op) ||
+                   (insn.i_format.rs == bc1nez_op)) {
 
-               reg = insn.i_format.rt;
-               bit = 0;
-               switch (insn.i_format.rs) {
-               case bc1eqz_op:
-                       if (current->thread.fpu.fpr[reg] == (__u64)0)
-                               bit = 1;
-                       break;
-               case bc1nez_op:
-                       if (current->thread.fpu.fpr[reg] != (__u64)0)
-                               bit = 1;
+                       reg = insn.i_format.rt;
+                       bit = 0;
+                       switch (insn.i_format.rs) {
+                       case bc1eqz_op:
+                               if (current->thread.fpu.fpr[reg] == (__u64)0)
+                                       bit = 1;
+                               break;
+                       case bc1nez_op:
+                               if (current->thread.fpu.fpr[reg] != (__u64)0)
+                                       bit = 1;
+                               break;
+                       }
+                       if (bit)
+                               *contpc = regs->cp0_epc +
+                                       dec_insn.pc_inc +
+                                       (insn.i_format.simmediate << 2);
+                       else
+                               *contpc = regs->cp0_epc +
+                                       dec_insn.pc_inc +
+                                       dec_insn.next_pc_inc;
+                       return 1;
                        break;
                }
-               if (bit)
-                       *contpc = regs->cp0_epc +
-                               dec_insn.pc_inc +
-                               (insn.i_format.simmediate << 2);
-               else
-                       *contpc = regs->cp0_epc +
-                               dec_insn.pc_inc +
-                               dec_insn.next_pc_inc;
-               return 1;
-               break;
-#else
+
+               if (!mipsr2_emulation)
+                       break;
+#endif
        case cop0_op:
-       case cop1_op:
        case cop2_op:
        case cop1x_op:
                if (insn.i_format.rs == rs_bc_op) {
@@ -1008,7 +1014,6 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                        }
                }
                break;
-#endif
        }
        return 0;
 }
@@ -1060,6 +1065,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
        unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc;
        unsigned int cond;
        int pc_inc;
+       int likely;
 
        /* XXX NEC Vr54xx bug workaround */
        if (xcp->cp0_cause & CAUSEF_BD) {
@@ -1309,6 +1315,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        if (xcp->cp0_cause & CAUSEF_BD)
                                return SIGILL;
 
+                       likely = 0;
+
                        reg = MIPSInst_FT(ir);
                        cond = 0;
                        switch (MIPSInst_RS(ir)) {
@@ -1321,9 +1329,18 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                                        cond = 1;
                                break;
                        }
-#else /* !CONFIG_CPU_MIPSR6 */
-               case rs_bc_op:{
-                       int likely = 0;
+               }
+               goto branch_cont;
+
+#endif /* CONFIG_CPU_MIPSR6 */
+               case rs_bc_op:
+#ifdef CONFIG_CPU_MIPSR6
+               if (!mipsr2_emulation)
+                       goto default_op;
+#endif
+
+               {
+                       likely = 0;
 
                        if (xcp->cp0_cause & CAUSEF_BD)
                                return SIGILL;
@@ -1347,8 +1364,11 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                                /* thats an illegal instruction */
                                return SIGILL;
                        }
-#endif /* CONFIG_CPU_MIPSR6 */
+#ifdef CONFIG_CPU_MIPSR6
+               }
 
+branch_cont:    {
+#endif
                        xcp->cp0_cause |= CAUSEF_BD;
                        if (cond) {
                                /* branch taken: emulate dslot
@@ -1397,14 +1417,15 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
 #endif
                                        /* its one of ours */
                                        goto emul;
-#ifndef CONFIG_CPU_MIPSR6
 #if __mips >= 4
                                case spec_op:
-                                       if (MIPSInst_FUNC(ir) == movc_op)
-                                               goto emul;
+#ifdef CONFIG_CPU_MIPSR6
+                                       if (mipsr2_emulation)
+#endif
+                                               if (MIPSInst_FUNC(ir) == movc_op)
+                                                          goto emul;
                                        break;
 #endif
-#endif /* CONFIG_CPU_MIPSR6 */
                                }
 
                                /*
@@ -1412,9 +1433,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                                 * instruction in the dslot
                                 */
                                return mips_dsemul(xcp, ir, contpc);
-                       }
-#ifndef CONFIG_CPU_MIPSR6
-                       else {
+                       } else {
                                /* branch not taken */
                                if (likely) {
                                        /*
@@ -1429,11 +1448,13 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                                         */
                                }
                        }
-#endif /* CONFIG_CPU_MIPSR6 */
                        break;
                }
 
                default:
+#ifdef CONFIG_CPU_MIPSR6
+default_op:
+#endif
                        if (!(MIPSInst_RS(ir) & 0x10))
                                return SIGILL;
                        {
@@ -1455,9 +1476,12 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
        }
 #endif
 
-#ifndef CONFIG_CPU_MIPSR6
 #if __mips >= 4
        case spec_op:
+#ifdef CONFIG_CPU_MIPSR6
+               if (!mipsr2_emulation)
+                       return SIGILL;
+#endif
                if (MIPSInst_FUNC(ir) != movc_op)
                        return SIGILL;
                cond = fpucondbit[MIPSInst_RT(ir) >> 2];
@@ -1465,7 +1489,6 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        xcp->regs[MIPSInst_RD(ir)] =
                                xcp->regs[MIPSInst_RS(ir)];
                break;
-#endif
 #endif
 
        default:
index 7ea622ab8dad32df6bf1fbd8d07bffdbf48c3f72..364df098892bdbd4cc1ae4a41e80bee06d87dc42 100644 (file)
@@ -179,6 +179,5 @@ int do_dsemulret(struct pt_regs *xcp)
 
        /* Set EPC to return to post-branch instruction */
        xcp->cp0_epc = epc;
-
        return 1;
 }