perf: Remove the nmi parameter from the swevent and overflow interface
[linux-2.6.git] / arch / mips / math-emu / cp1emu.c
index d7f05b0..dbf2f93 100644 (file)
@@ -3,7 +3,6 @@
  *
  * MIPS floating point support
  * Copyright (C) 1994-2000 Algorithmics Ltd.
- * http://www.algor.co.uk
  *
  * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
  * Copyright (C) 2000  MIPS Technologies, Inc.
@@ -35,7 +34,9 @@
  * better performance by compiling with -msoft-float!
  */
 #include <linux/sched.h>
+#include <linux/module.h>
 #include <linux/debugfs.h>
+#include <linux/perf_event.h>
 
 #include <asm/inst.h>
 #include <asm/bootinfo.h>
@@ -48,7 +49,6 @@
 #include <asm/branch.h>
 
 #include "ieee754.h"
-#include "dsemul.h"
 
 /* Strap kernel emulator for full MIPS IV emulation */
 
@@ -64,18 +64,23 @@ static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
 
 #if __mips >= 4 && __mips != 32
 static int fpux_emu(struct pt_regs *,
-       struct mips_fpu_struct *, mips_instruction);
+       struct mips_fpu_struct *, mips_instruction, void *__user *);
 #endif
 
 /* Further private data for which no space exists in mips_fpu_struct */
 
-struct mips_fpu_emulator_stats fpuemustats;
+#ifdef CONFIG_DEBUG_FS
+DEFINE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats);
+#endif
 
 /* Control registers */
 
 #define FPCREG_RID     0       /* $0  = revision id */
 #define FPCREG_CSR     31      /* $31 = csr */
 
+/* Determine rounding mode from the RM bits of the FCSR */
+#define modeindex(v) ((v) & FPU_CSR_RM)
+
 /* Convert Mips rounding mode (0..3) to IEEE library modes. */
 static const unsigned char ieee_rm[4] = {
        [FPU_CSR_RN] = IEEE754_RN,
@@ -164,54 +169,62 @@ static int isBranchInstr(mips_instruction * i)
 
 /*
  * In the Linux kernel, we support selection of FPR format on the
- * basis of the Status.FR bit.  This does imply that, if a full 32
- * FPRs are desired, there needs to be a flip-flop that can be written
- * to one at that bit position.  In any case, O32 MIPS ABI uses
- * only the even FPRs (Status.FR = 0).
+ * basis of the Status.FR bit.  If an FPU is not present, the FR bit
+ * is hardwired to zero, which would imply a 32-bit FPU even for
+ * 64-bit CPUs.  For 64-bit kernels with no FPU we use TIF_32BIT_REGS
+ * as a proxy for the FR bit so that a 64-bit FPU is emulated.  In any
+ * case, for a 32-bit kernel which uses the O32 MIPS ABI, only the
+ * even FPRs are used (Status.FR = 0).
  */
-
-#define CP0_STATUS_FR_SUPPORT
-
-#ifdef CP0_STATUS_FR_SUPPORT
-#define FR_BIT ST0_FR
+static inline int cop1_64bit(struct pt_regs *xcp)
+{
+       if (cpu_has_fpu)
+               return xcp->cp0_status & ST0_FR;
+#ifdef CONFIG_64BIT
+       return !test_thread_flag(TIF_32BIT_REGS);
 #else
-#define FR_BIT 0
+       return 0;
 #endif
+}
+
+#define SIFROMREG(si, x) ((si) = cop1_64bit(xcp) || !(x & 1) ? \
+                       (int)ctx->fpr[x] : (int)(ctx->fpr[x & ~1] >> 32))
 
-#define SIFROMREG(si,x)        ((si) = \
-                       (xcp->cp0_status & FR_BIT) || !(x & 1) ? \
-                       (int)ctx->fpr[x] : \
-                       (int)(ctx->fpr[x & ~1] >> 32 ))
-#define SITOREG(si,x)  (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] = \
-                       (xcp->cp0_status & FR_BIT) || !(x & 1) ? \
+#define SITOREG(si, x) (ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = \
+                       cop1_64bit(xcp) || !(x & 1) ? \
                        ctx->fpr[x & ~1] >> 32 << 32 | (u32)(si) : \
                        ctx->fpr[x & ~1] << 32 >> 32 | (u64)(si) << 32)
 
-#define DIFROMREG(di,x)        ((di) = \
-                       ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)])
-#define DITOREG(di,x)  (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] \
-                       = (di))
+#define DIFROMREG(di, x) ((di) = ctx->fpr[x & ~(cop1_64bit(xcp) == 0)])
+#define DITOREG(di, x) (ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = (di))
 
-#define SPFROMREG(sp,x)        SIFROMREG((sp).bits,x)
-#define SPTOREG(sp,x)  SITOREG((sp).bits,x)
-#define DPFROMREG(dp,x)        DIFROMREG((dp).bits,x)
-#define DPTOREG(dp,x)  DITOREG((dp).bits,x)
+#define SPFROMREG(sp, x) SIFROMREG((sp).bits, x)
+#define SPTOREG(sp, x) SITOREG((sp).bits, x)
+#define DPFROMREG(dp, x)       DIFROMREG((dp).bits, x)
+#define DPTOREG(dp, x) DITOREG((dp).bits, x)
 
 /*
  * Emulate the single floating point instruction pointed at by EPC.
  * Two instructions if the instruction is in a branch delay slot.
  */
 
-static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
+static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+                      void *__user *fault_addr)
 {
        mips_instruction ir;
-       void * emulpc, *contpc;
+       unsigned long emulpc, contpc;
        unsigned int cond;
 
-       if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
-               fpuemustats.errors++;
+       if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) {
+               MIPS_FPU_EMU_INC_STATS(errors);
+               *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
                return SIGBUS;
        }
+       if (__get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
+               MIPS_FPU_EMU_INC_STATS(errors);
+               *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
+               return SIGSEGV;
+       }
 
        /* XXX NEC Vr54xx bug workaround */
        if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))
@@ -230,7 +243,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                 * Linux MIPS branch emulator operates on context, updating the
                 * cp0_epc.
                 */
-               emulpc = (void *) (xcp->cp0_epc + 4);   /* Snapshot emulation target */
+               emulpc = xcp->cp0_epc + 4;      /* Snapshot emulation target */
 
                if (__compute_return_epc(xcp)) {
 #ifdef CP1DBG
@@ -239,32 +252,46 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
 #endif
                        return SIGILL;
                }
-               if (get_user(ir, (mips_instruction __user *) emulpc)) {
-                       fpuemustats.errors++;
+               if (!access_ok(VERIFY_READ, emulpc, sizeof(mips_instruction))) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = (mips_instruction __user *)emulpc;
                        return SIGBUS;
                }
+               if (__get_user(ir, (mips_instruction __user *) emulpc)) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = (mips_instruction __user *)emulpc;
+                       return SIGSEGV;
+               }
                /* __compute_return_epc() will have updated cp0_epc */
-               contpc = (void *)  xcp->cp0_epc;
+               contpc = xcp->cp0_epc;
                /* In order not to confuse ptrace() et al, tweak context */
-               xcp->cp0_epc = (unsigned long) emulpc - 4;
+               xcp->cp0_epc = emulpc - 4;
        } else {
-               emulpc = (void *)  xcp->cp0_epc;
-               contpc = (void *) (xcp->cp0_epc + 4);
+               emulpc = xcp->cp0_epc;
+               contpc = xcp->cp0_epc + 4;
        }
 
       emul:
-       fpuemustats.emulated++;
+       perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0);
+       MIPS_FPU_EMU_INC_STATS(emulated);
        switch (MIPSInst_OPCODE(ir)) {
        case ldc1_op:{
                u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
                        MIPSInst_SIMM(ir));
                u64 val;
 
-               fpuemustats.loads++;
-               if (get_user(val, va)) {
-                       fpuemustats.errors++;
+               MIPS_FPU_EMU_INC_STATS(loads);
+
+               if (!access_ok(VERIFY_READ, va, sizeof(u64))) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
                        return SIGBUS;
                }
+               if (__get_user(val, va)) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
+                       return SIGSEGV;
+               }
                DITOREG(val, MIPSInst_RT(ir));
                break;
        }
@@ -274,12 +301,18 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                        MIPSInst_SIMM(ir));
                u64 val;
 
-               fpuemustats.stores++;
+               MIPS_FPU_EMU_INC_STATS(stores);
                DIFROMREG(val, MIPSInst_RT(ir));
-               if (put_user(val, va)) {
-                       fpuemustats.errors++;
+               if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
                        return SIGBUS;
                }
+               if (__put_user(val, va)) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
+                       return SIGSEGV;
+               }
                break;
        }
 
@@ -288,11 +321,17 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                        MIPSInst_SIMM(ir));
                u32 val;
 
-               fpuemustats.loads++;
-               if (get_user(val, va)) {
-                       fpuemustats.errors++;
+               MIPS_FPU_EMU_INC_STATS(loads);
+               if (!access_ok(VERIFY_READ, va, sizeof(u32))) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
                        return SIGBUS;
                }
+               if (__get_user(val, va)) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
+                       return SIGSEGV;
+               }
                SITOREG(val, MIPSInst_RT(ir));
                break;
        }
@@ -302,12 +341,18 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                        MIPSInst_SIMM(ir));
                u32 val;
 
-               fpuemustats.stores++;
+               MIPS_FPU_EMU_INC_STATS(stores);
                SIFROMREG(val, MIPSInst_RT(ir));
-               if (put_user(val, va)) {
-                       fpuemustats.errors++;
+               if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
                        return SIGBUS;
                }
+               if (__put_user(val, va)) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = va;
+                       return SIGSEGV;
+               }
                break;
        }
 
@@ -346,12 +391,10 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                        /* cop control register rd -> gpr[rt] */
                        u32 value;
 
-                       if (ir == CP1UNDEF) {
-                               return do_dsemulret(xcp);
-                       }
                        if (MIPSInst_RD(ir) == FPCREG_CSR) {
                                value = ctx->fcr31;
-                               value = (value & ~0x3) | mips_rm[value & 0x3];
+                               value = (value & ~FPU_CSR_RM) |
+                                       mips_rm[modeindex(value)];
 #ifdef CSRTRACE
                                printk("%p gpr[%d]<-csr=%08x\n",
                                        (void *) (xcp->cp0_epc),
@@ -384,10 +427,14 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                                        (void *) (xcp->cp0_epc),
                                        MIPSInst_RT(ir), value);
 #endif
-                               value &= (FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);
-                               ctx->fcr31 &= ~(FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);
-                               /* convert to ieee library modes */
-                               ctx->fcr31 |= (value & ~0x3) | ieee_rm[value & 0x3];
+
+                               /*
+                                * Don't write reserved bits,
+                                * and convert to ieee library modes
+                                */
+                               ctx->fcr31 = (value &
+                                               ~(FPU_CSR_RSVD | FPU_CSR_RM)) |
+                                               ieee_rm[modeindex(value)];
                        }
                        if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
                                return SIGFPE;
@@ -427,15 +474,21 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                                 * instruction
                                 */
                                xcp->cp0_epc += 4;
-                               contpc = (void *)
-                                       (xcp->cp0_epc +
+                               contpc = (xcp->cp0_epc +
                                        (MIPSInst_SIMM(ir) << 2));
 
-                               if (get_user(ir,
-                                   (mips_instruction __user *) xcp->cp0_epc)) {
-                                       fpuemustats.errors++;
+                               if (!access_ok(VERIFY_READ, xcp->cp0_epc,
+                                              sizeof(mips_instruction))) {
+                                       MIPS_FPU_EMU_INC_STATS(errors);
+                                       *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
                                        return SIGBUS;
                                }
+                               if (__get_user(ir,
+                                   (mips_instruction __user *) xcp->cp0_epc)) {
+                                       MIPS_FPU_EMU_INC_STATS(errors);
+                                       *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
+                                       return SIGSEGV;
+                               }
 
                                switch (MIPSInst_OPCODE(ir)) {
                                case lwc1_op:
@@ -462,7 +515,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
                                 * Single step the non-cp1
                                 * instruction in the dslot
                                 */
-                               return mips_dsemul(xcp, ir, (unsigned long) contpc);
+                               return mips_dsemul(xcp, ir, contpc);
                        }
                        else {
                                /* branch not taken */
@@ -497,9 +550,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
 
 #if __mips >= 4 && __mips != 32
        case cop1x_op:{
-               int sig;
-
-               if ((sig = fpux_emu(xcp, ctx, ir)))
+               int sig = fpux_emu(xcp, ctx, ir, fault_addr);
+               if (sig)
                        return sig;
                break;
        }
@@ -521,7 +573,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
        }
 
        /* we did it !! */
-       xcp->cp0_epc = (unsigned long) contpc;
+       xcp->cp0_epc = contpc;
        xcp->cp0_cause &= ~CAUSEF_BD;
 
        return 0;
@@ -550,16 +602,16 @@ static const unsigned char cmptab[8] = {
  */
 
 #define DEF3OP(name, p, f1, f2, f3) \
-static ieee754##p fpemu_##p##_##name (ieee754##p r, ieee754##p s, \
+static ieee754##p fpemu_##p##_##name(ieee754##p r, ieee754##p s, \
     ieee754##p t) \
 { \
        struct _ieee754_csr ieee754_csr_save; \
-       s = f1 (s, t); \
+       s = f1(s, t); \
        ieee754_csr_save = ieee754_csr; \
-       s = f2 (s, r); \
+       s = f2(s, r); \
        ieee754_csr_save.cx |= ieee754_csr.cx; \
        ieee754_csr_save.sx |= ieee754_csr.sx; \
-       s = f3 (s); \
+       s = f3(s); \
        ieee754_csr.cx |= ieee754_csr_save.cx; \
        ieee754_csr.sx |= ieee754_csr_save.sx; \
        return s; \
@@ -585,21 +637,21 @@ static ieee754sp fpemu_sp_rsqrt(ieee754sp s)
        return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s));
 }
 
-DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add,);
-DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub,);
+DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, );
+DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, );
 DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg);
 DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg);
-DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add,);
-DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub,);
+DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, );
+DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, );
 DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
 DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
 
 static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
-       mips_instruction ir)
+       mips_instruction ir, void *__user *fault_addr)
 {
        unsigned rcsr = 0;      /* resulting csr */
 
-       fpuemustats.cp1xops++;
+       MIPS_FPU_EMU_INC_STATS(cp1xops);
 
        switch (MIPSInst_FMA_FFMT(ir)) {
        case s_fmt:{            /* 0 */
@@ -614,11 +666,17 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
                                xcp->regs[MIPSInst_FT(ir)]);
 
-                       fpuemustats.loads++;
-                       if (get_user(val, va)) {
-                               fpuemustats.errors++;
+                       MIPS_FPU_EMU_INC_STATS(loads);
+                       if (!access_ok(VERIFY_READ, va, sizeof(u32))) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
                                return SIGBUS;
                        }
+                       if (__get_user(val, va)) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
+                               return SIGSEGV;
+                       }
                        SITOREG(val, MIPSInst_FD(ir));
                        break;
 
@@ -626,13 +684,19 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
                                xcp->regs[MIPSInst_FT(ir)]);
 
-                       fpuemustats.stores++;
+                       MIPS_FPU_EMU_INC_STATS(stores);
 
                        SIFROMREG(val, MIPSInst_FS(ir));
-                       if (put_user(val, va)) {
-                               fpuemustats.errors++;
+                       if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
                                return SIGBUS;
                        }
+                       if (put_user(val, va)) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
+                               return SIGSEGV;
+                       }
                        break;
 
                case madd_s_op:
@@ -691,11 +755,17 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
                                xcp->regs[MIPSInst_FT(ir)]);
 
-                       fpuemustats.loads++;
-                       if (get_user(val, va)) {
-                               fpuemustats.errors++;
+                       MIPS_FPU_EMU_INC_STATS(loads);
+                       if (!access_ok(VERIFY_READ, va, sizeof(u64))) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
                                return SIGBUS;
                        }
+                       if (__get_user(val, va)) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
+                               return SIGSEGV;
+                       }
                        DITOREG(val, MIPSInst_FD(ir));
                        break;
 
@@ -703,12 +773,18 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
                                xcp->regs[MIPSInst_FT(ir)]);
 
-                       fpuemustats.stores++;
+                       MIPS_FPU_EMU_INC_STATS(stores);
                        DIFROMREG(val, MIPSInst_FS(ir));
-                       if (put_user(val, va)) {
-                               fpuemustats.errors++;
+                       if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
                                return SIGBUS;
                        }
+                       if (__put_user(val, va)) {
+                               MIPS_FPU_EMU_INC_STATS(errors);
+                               *fault_addr = va;
+                               return SIGSEGV;
+                       }
                        break;
 
                case madd_d_op:
@@ -773,7 +849,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
 #endif
        } rv;                   /* resulting value */
 
-       fpuemustats.cp1ops++;
+       MIPS_FPU_EMU_INC_STATS(cp1ops);
        switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
        case s_fmt:{            /* 0 */
                union {
@@ -901,7 +977,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        ieee754sp fs;
 
                        SPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
+                       ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))];
                        rv.w = ieee754sp_tint(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = w_fmt;
@@ -927,7 +1003,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        ieee754sp fs;
 
                        SPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
+                       ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))];
                        rv.l = ieee754sp_tlong(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = l_fmt;
@@ -1075,7 +1151,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        ieee754dp fs;
 
                        DPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
+                       ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))];
                        rv.w = ieee754dp_tint(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = w_fmt;
@@ -1101,7 +1177,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        ieee754dp fs;
 
                        DPFROMREG(fs, MIPSInst_FS(ir));
-                       ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
+                       ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))];
                        rv.l = ieee754dp_tlong(fs);
                        ieee754_csr.rm = oldrm;
                        rfmt = l_fmt;
@@ -1233,7 +1309,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
 }
 
 int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
-       int has_fpu)
+       int has_fpu, void *__user *fault_addr)
 {
        unsigned long oldepc, prevepc;
        mips_instruction insn;
@@ -1243,10 +1319,16 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
        do {
                prevepc = xcp->cp0_epc;
 
-               if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
-                       fpuemustats.errors++;
+               if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
                        return SIGBUS;
                }
+               if (__get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
+                       MIPS_FPU_EMU_INC_STATS(errors);
+                       *fault_addr = (mips_instruction __user *)xcp->cp0_epc;
+                       return SIGSEGV;
+               }
                if (insn == 0)
                        xcp->cp0_epc += 4;      /* skip nops */
                else {
@@ -1258,7 +1340,7 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                         */
                        /* convert to ieee library modes */
                        ieee754_csr.rm = ieee_rm[ieee754_csr.rm];
-                       sig = cop1Emulate(xcp, ctx);
+                       sig = cop1Emulate(xcp, ctx, fault_addr);
                        /* revert to mips rounding mode */
                        ieee754_csr.rm = mips_rm[ieee754_csr.rm];
                }
@@ -1280,33 +1362,50 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
 }
 
 #ifdef CONFIG_DEBUG_FS
+
+static int fpuemu_stat_get(void *data, u64 *val)
+{
+       int cpu;
+       unsigned long sum = 0;
+       for_each_online_cpu(cpu) {
+               struct mips_fpu_emulator_stats *ps;
+               local_t *pv;
+               ps = &per_cpu(fpuemustats, cpu);
+               pv = (void *)ps + (unsigned long)data;
+               sum += local_read(pv);
+       }
+       *val = sum;
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_fpuemu_stat, fpuemu_stat_get, NULL, "%llu\n");
+
 extern struct dentry *mips_debugfs_dir;
 static int __init debugfs_fpuemu(void)
 {
        struct dentry *d, *dir;
-       int i;
-       static struct {
-               const char *name;
-               unsigned int *v;
-       } vars[] __initdata = {
-               { "emulated", &fpuemustats.emulated },
-               { "loads",    &fpuemustats.loads },
-               { "stores",   &fpuemustats.stores },
-               { "cp1ops",   &fpuemustats.cp1ops },
-               { "cp1xops",  &fpuemustats.cp1xops },
-               { "errors",   &fpuemustats.errors },
-       };
 
        if (!mips_debugfs_dir)
                return -ENODEV;
        dir = debugfs_create_dir("fpuemustats", mips_debugfs_dir);
-       if (IS_ERR(dir))
-               return PTR_ERR(dir);
-       for (i = 0; i < ARRAY_SIZE(vars); i++) {
-               d = debugfs_create_u32(vars[i].name, S_IRUGO, dir, vars[i].v);
-               if (IS_ERR(d))
-                       return PTR_ERR(d);
-       }
+       if (!dir)
+               return -ENOMEM;
+
+#define FPU_STAT_CREATE(M)                                             \
+       do {                                                            \
+               d = debugfs_create_file(#M , S_IRUGO, dir,              \
+                       (void *)offsetof(struct mips_fpu_emulator_stats, M), \
+                       &fops_fpuemu_stat);                             \
+               if (!d)                                                 \
+                       return -ENOMEM;                                 \
+       } while (0)
+
+       FPU_STAT_CREATE(emulated);
+       FPU_STAT_CREATE(loads);
+       FPU_STAT_CREATE(stores);
+       FPU_STAT_CREATE(cp1ops);
+       FPU_STAT_CREATE(cp1xops);
+       FPU_STAT_CREATE(errors);
+
        return 0;
 }
 __initcall(debugfs_fpuemu);