diff options
Diffstat (limited to 'arch/mips/math-emu/cp1emu.c')
-rw-r--r-- | arch/mips/math-emu/cp1emu.c | 117 |
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 | ||
71 | struct mips_fpu_emulator_stats fpuemustats; | 72 | #ifdef CONFIG_DEBUG_FS |
73 | DEFINE_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. */ |
79 | static const unsigned char ieee_rm[4] = { | 85 | static 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 | |||
1290 | static 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 | } | ||
1304 | DEFINE_SIMPLE_ATTRIBUTE(fops_fpuemu_stat, fpuemu_stat_get, NULL, "%llu\n"); | ||
1305 | |||
1279 | extern struct dentry *mips_debugfs_dir; | 1306 | extern struct dentry *mips_debugfs_dir; |
1280 | static int __init debugfs_fpuemu(void) | 1307 | static 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); |