aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/math-emu/cp1emu.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/math-emu/cp1emu.c')
-rw-r--r--arch/mips/math-emu/cp1emu.c117
1 files changed, 72 insertions, 45 deletions
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index 454b53924490..f2338d1c0b48 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -35,6 +35,7 @@
35 * better performance by compiling with -msoft-float! 35 * better performance by compiling with -msoft-float!
36 */ 36 */
37#include <linux/sched.h> 37#include <linux/sched.h>
38#include <linux/module.h>
38#include <linux/debugfs.h> 39#include <linux/debugfs.h>
39 40
40#include <asm/inst.h> 41#include <asm/inst.h>
@@ -68,13 +69,18 @@ static int fpux_emu(struct pt_regs *,
68 69
69/* Further private data for which no space exists in mips_fpu_struct */ 70/* Further private data for which no space exists in mips_fpu_struct */
70 71
71struct mips_fpu_emulator_stats fpuemustats; 72#ifdef CONFIG_DEBUG_FS
73DEFINE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats);
74#endif
72 75
73/* Control registers */ 76/* Control registers */
74 77
75#define FPCREG_RID 0 /* $0 = revision id */ 78#define FPCREG_RID 0 /* $0 = revision id */
76#define FPCREG_CSR 31 /* $31 = csr */ 79#define FPCREG_CSR 31 /* $31 = csr */
77 80
81/* Determine rounding mode from the RM bits of the FCSR */
82#define modeindex(v) ((v) & FPU_CSR_RM)
83
78/* Convert Mips rounding mode (0..3) to IEEE library modes. */ 84/* Convert Mips rounding mode (0..3) to IEEE library modes. */
79static const unsigned char ieee_rm[4] = { 85static const unsigned char ieee_rm[4] = {
80 [FPU_CSR_RN] = IEEE754_RN, 86 [FPU_CSR_RN] = IEEE754_RN,
@@ -209,7 +215,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
209 unsigned int cond; 215 unsigned int cond;
210 216
211 if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) { 217 if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
212 fpuemustats.errors++; 218 MIPS_FPU_EMU_INC_STATS(errors);
213 return SIGBUS; 219 return SIGBUS;
214 } 220 }
215 221
@@ -240,7 +246,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
240 return SIGILL; 246 return SIGILL;
241 } 247 }
242 if (get_user(ir, (mips_instruction __user *) emulpc)) { 248 if (get_user(ir, (mips_instruction __user *) emulpc)) {
243 fpuemustats.errors++; 249 MIPS_FPU_EMU_INC_STATS(errors);
244 return SIGBUS; 250 return SIGBUS;
245 } 251 }
246 /* __compute_return_epc() will have updated cp0_epc */ 252 /* __compute_return_epc() will have updated cp0_epc */
@@ -253,16 +259,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
253 } 259 }
254 260
255 emul: 261 emul:
256 fpuemustats.emulated++; 262 MIPS_FPU_EMU_INC_STATS(emulated);
257 switch (MIPSInst_OPCODE(ir)) { 263 switch (MIPSInst_OPCODE(ir)) {
258 case ldc1_op:{ 264 case ldc1_op:{
259 u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 265 u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
260 MIPSInst_SIMM(ir)); 266 MIPSInst_SIMM(ir));
261 u64 val; 267 u64 val;
262 268
263 fpuemustats.loads++; 269 MIPS_FPU_EMU_INC_STATS(loads);
264 if (get_user(val, va)) { 270 if (get_user(val, va)) {
265 fpuemustats.errors++; 271 MIPS_FPU_EMU_INC_STATS(errors);
266 return SIGBUS; 272 return SIGBUS;
267 } 273 }
268 DITOREG(val, MIPSInst_RT(ir)); 274 DITOREG(val, MIPSInst_RT(ir));
@@ -274,10 +280,10 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
274 MIPSInst_SIMM(ir)); 280 MIPSInst_SIMM(ir));
275 u64 val; 281 u64 val;
276 282
277 fpuemustats.stores++; 283 MIPS_FPU_EMU_INC_STATS(stores);
278 DIFROMREG(val, MIPSInst_RT(ir)); 284 DIFROMREG(val, MIPSInst_RT(ir));
279 if (put_user(val, va)) { 285 if (put_user(val, va)) {
280 fpuemustats.errors++; 286 MIPS_FPU_EMU_INC_STATS(errors);
281 return SIGBUS; 287 return SIGBUS;
282 } 288 }
283 break; 289 break;
@@ -288,9 +294,9 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
288 MIPSInst_SIMM(ir)); 294 MIPSInst_SIMM(ir));
289 u32 val; 295 u32 val;
290 296
291 fpuemustats.loads++; 297 MIPS_FPU_EMU_INC_STATS(loads);
292 if (get_user(val, va)) { 298 if (get_user(val, va)) {
293 fpuemustats.errors++; 299 MIPS_FPU_EMU_INC_STATS(errors);
294 return SIGBUS; 300 return SIGBUS;
295 } 301 }
296 SITOREG(val, MIPSInst_RT(ir)); 302 SITOREG(val, MIPSInst_RT(ir));
@@ -302,10 +308,10 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
302 MIPSInst_SIMM(ir)); 308 MIPSInst_SIMM(ir));
303 u32 val; 309 u32 val;
304 310
305 fpuemustats.stores++; 311 MIPS_FPU_EMU_INC_STATS(stores);
306 SIFROMREG(val, MIPSInst_RT(ir)); 312 SIFROMREG(val, MIPSInst_RT(ir));
307 if (put_user(val, va)) { 313 if (put_user(val, va)) {
308 fpuemustats.errors++; 314 MIPS_FPU_EMU_INC_STATS(errors);
309 return SIGBUS; 315 return SIGBUS;
310 } 316 }
311 break; 317 break;
@@ -381,10 +387,14 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
381 (void *) (xcp->cp0_epc), 387 (void *) (xcp->cp0_epc),
382 MIPSInst_RT(ir), value); 388 MIPSInst_RT(ir), value);
383#endif 389#endif
384 value &= (FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03); 390
385 ctx->fcr31 &= ~(FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03); 391 /*
386 /* convert to ieee library modes */ 392 * Don't write reserved bits,
387 ctx->fcr31 |= (value & ~0x3) | ieee_rm[value & 0x3]; 393 * and convert to ieee library modes
394 */
395 ctx->fcr31 = (value &
396 ~(FPU_CSR_RSVD | FPU_CSR_RM)) |
397 ieee_rm[modeindex(value)];
388 } 398 }
389 if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 399 if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
390 return SIGFPE; 400 return SIGFPE;
@@ -429,7 +439,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
429 439
430 if (get_user(ir, 440 if (get_user(ir,
431 (mips_instruction __user *) xcp->cp0_epc)) { 441 (mips_instruction __user *) xcp->cp0_epc)) {
432 fpuemustats.errors++; 442 MIPS_FPU_EMU_INC_STATS(errors);
433 return SIGBUS; 443 return SIGBUS;
434 } 444 }
435 445
@@ -595,7 +605,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
595{ 605{
596 unsigned rcsr = 0; /* resulting csr */ 606 unsigned rcsr = 0; /* resulting csr */
597 607
598 fpuemustats.cp1xops++; 608 MIPS_FPU_EMU_INC_STATS(cp1xops);
599 609
600 switch (MIPSInst_FMA_FFMT(ir)) { 610 switch (MIPSInst_FMA_FFMT(ir)) {
601 case s_fmt:{ /* 0 */ 611 case s_fmt:{ /* 0 */
@@ -610,9 +620,9 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
610 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 620 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
611 xcp->regs[MIPSInst_FT(ir)]); 621 xcp->regs[MIPSInst_FT(ir)]);
612 622
613 fpuemustats.loads++; 623 MIPS_FPU_EMU_INC_STATS(loads);
614 if (get_user(val, va)) { 624 if (get_user(val, va)) {
615 fpuemustats.errors++; 625 MIPS_FPU_EMU_INC_STATS(errors);
616 return SIGBUS; 626 return SIGBUS;
617 } 627 }
618 SITOREG(val, MIPSInst_FD(ir)); 628 SITOREG(val, MIPSInst_FD(ir));
@@ -622,11 +632,11 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
622 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 632 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
623 xcp->regs[MIPSInst_FT(ir)]); 633 xcp->regs[MIPSInst_FT(ir)]);
624 634
625 fpuemustats.stores++; 635 MIPS_FPU_EMU_INC_STATS(stores);
626 636
627 SIFROMREG(val, MIPSInst_FS(ir)); 637 SIFROMREG(val, MIPSInst_FS(ir));
628 if (put_user(val, va)) { 638 if (put_user(val, va)) {
629 fpuemustats.errors++; 639 MIPS_FPU_EMU_INC_STATS(errors);
630 return SIGBUS; 640 return SIGBUS;
631 } 641 }
632 break; 642 break;
@@ -687,9 +697,9 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
687 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 697 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
688 xcp->regs[MIPSInst_FT(ir)]); 698 xcp->regs[MIPSInst_FT(ir)]);
689 699
690 fpuemustats.loads++; 700 MIPS_FPU_EMU_INC_STATS(loads);
691 if (get_user(val, va)) { 701 if (get_user(val, va)) {
692 fpuemustats.errors++; 702 MIPS_FPU_EMU_INC_STATS(errors);
693 return SIGBUS; 703 return SIGBUS;
694 } 704 }
695 DITOREG(val, MIPSInst_FD(ir)); 705 DITOREG(val, MIPSInst_FD(ir));
@@ -699,10 +709,10 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
699 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 709 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
700 xcp->regs[MIPSInst_FT(ir)]); 710 xcp->regs[MIPSInst_FT(ir)]);
701 711
702 fpuemustats.stores++; 712 MIPS_FPU_EMU_INC_STATS(stores);
703 DIFROMREG(val, MIPSInst_FS(ir)); 713 DIFROMREG(val, MIPSInst_FS(ir));
704 if (put_user(val, va)) { 714 if (put_user(val, va)) {
705 fpuemustats.errors++; 715 MIPS_FPU_EMU_INC_STATS(errors);
706 return SIGBUS; 716 return SIGBUS;
707 } 717 }
708 break; 718 break;
@@ -769,7 +779,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
769#endif 779#endif
770 } rv; /* resulting value */ 780 } rv; /* resulting value */
771 781
772 fpuemustats.cp1ops++; 782 MIPS_FPU_EMU_INC_STATS(cp1ops);
773 switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { 783 switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
774 case s_fmt:{ /* 0 */ 784 case s_fmt:{ /* 0 */
775 union { 785 union {
@@ -1240,7 +1250,7 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
1240 prevepc = xcp->cp0_epc; 1250 prevepc = xcp->cp0_epc;
1241 1251
1242 if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) { 1252 if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
1243 fpuemustats.errors++; 1253 MIPS_FPU_EMU_INC_STATS(errors);
1244 return SIGBUS; 1254 return SIGBUS;
1245 } 1255 }
1246 if (insn == 0) 1256 if (insn == 0)
@@ -1276,33 +1286,50 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
1276} 1286}
1277 1287
1278#ifdef CONFIG_DEBUG_FS 1288#ifdef CONFIG_DEBUG_FS
1289
1290static int fpuemu_stat_get(void *data, u64 *val)
1291{
1292 int cpu;
1293 unsigned long sum = 0;
1294 for_each_online_cpu(cpu) {
1295 struct mips_fpu_emulator_stats *ps;
1296 local_t *pv;
1297 ps = &per_cpu(fpuemustats, cpu);
1298 pv = (void *)ps + (unsigned long)data;
1299 sum += local_read(pv);
1300 }
1301 *val = sum;
1302 return 0;
1303}
1304DEFINE_SIMPLE_ATTRIBUTE(fops_fpuemu_stat, fpuemu_stat_get, NULL, "%llu\n");
1305
1279extern struct dentry *mips_debugfs_dir; 1306extern struct dentry *mips_debugfs_dir;
1280static int __init debugfs_fpuemu(void) 1307static int __init debugfs_fpuemu(void)
1281{ 1308{
1282 struct dentry *d, *dir; 1309 struct dentry *d, *dir;
1283 int i;
1284 static struct {
1285 const char *name;
1286 unsigned int *v;
1287 } vars[] __initdata = {
1288 { "emulated", &fpuemustats.emulated },
1289 { "loads", &fpuemustats.loads },
1290 { "stores", &fpuemustats.stores },
1291 { "cp1ops", &fpuemustats.cp1ops },
1292 { "cp1xops", &fpuemustats.cp1xops },
1293 { "errors", &fpuemustats.errors },
1294 };
1295 1310
1296 if (!mips_debugfs_dir) 1311 if (!mips_debugfs_dir)
1297 return -ENODEV; 1312 return -ENODEV;
1298 dir = debugfs_create_dir("fpuemustats", mips_debugfs_dir); 1313 dir = debugfs_create_dir("fpuemustats", mips_debugfs_dir);
1299 if (!dir) 1314 if (!dir)
1300 return -ENOMEM; 1315 return -ENOMEM;
1301 for (i = 0; i < ARRAY_SIZE(vars); i++) { 1316
1302 d = debugfs_create_u32(vars[i].name, S_IRUGO, dir, vars[i].v); 1317#define FPU_STAT_CREATE(M) \
1303 if (!d) 1318 do { \
1304 return -ENOMEM; 1319 d = debugfs_create_file(#M , S_IRUGO, dir, \
1305 } 1320 (void *)offsetof(struct mips_fpu_emulator_stats, M), \
1321 &fops_fpuemu_stat); \
1322 if (!d) \
1323 return -ENOMEM; \
1324 } while (0)
1325
1326 FPU_STAT_CREATE(emulated);
1327 FPU_STAT_CREATE(loads);
1328 FPU_STAT_CREATE(stores);
1329 FPU_STAT_CREATE(cp1ops);
1330 FPU_STAT_CREATE(cp1xops);
1331 FPU_STAT_CREATE(errors);
1332
1306 return 0; 1333 return 0;
1307} 1334}
1308__initcall(debugfs_fpuemu); 1335__initcall(debugfs_fpuemu);