cp1emu.c (559300bc0ef7ccd541656f1189d38e7088389559) cp1emu.c (b6ee75ed4fa201873d3a2b32dfce2dbd701a2de4)
1/*
2 * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator
3 *
4 * MIPS floating point support
5 * Copyright (C) 1994-2000 Algorithmics Ltd.
6 * http://www.algor.co.uk
7 *
8 * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com

--- 21 unchanged lines hidden (view full) ---

30 * (denormalised values, infinities, underflow, etc). It is made
31 * quite nasty because emulation of some non-COP1 instructions is
32 * required, e.g. in branch delay slots.
33 *
34 * Note if you know that you won't have an fpu, then you'll get much
35 * better performance by compiling with -msoft-float!
36 */
37#include <linux/sched.h>
1/*
2 * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator
3 *
4 * MIPS floating point support
5 * Copyright (C) 1994-2000 Algorithmics Ltd.
6 * http://www.algor.co.uk
7 *
8 * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com

--- 21 unchanged lines hidden (view full) ---

30 * (denormalised values, infinities, underflow, etc). It is made
31 * quite nasty because emulation of some non-COP1 instructions is
32 * required, e.g. in branch delay slots.
33 *
34 * Note if you know that you won't have an fpu, then you'll get much
35 * better performance by compiling with -msoft-float!
36 */
37#include <linux/sched.h>
38#include <linux/module.h>
38#include <linux/debugfs.h>
39
40#include <asm/inst.h>
41#include <asm/bootinfo.h>
42#include <asm/processor.h>
43#include <asm/ptrace.h>
44#include <asm/signal.h>
45#include <asm/mipsregs.h>

--- 17 unchanged lines hidden (view full) ---

63
64#if __mips >= 4 && __mips != 32
65static int fpux_emu(struct pt_regs *,
66 struct mips_fpu_struct *, mips_instruction);
67#endif
68
69/* Further private data for which no space exists in mips_fpu_struct */
70
39#include <linux/debugfs.h>
40
41#include <asm/inst.h>
42#include <asm/bootinfo.h>
43#include <asm/processor.h>
44#include <asm/ptrace.h>
45#include <asm/signal.h>
46#include <asm/mipsregs.h>

--- 17 unchanged lines hidden (view full) ---

64
65#if __mips >= 4 && __mips != 32
66static int fpux_emu(struct pt_regs *,
67 struct mips_fpu_struct *, mips_instruction);
68#endif
69
70/* Further private data for which no space exists in mips_fpu_struct */
71
71struct mips_fpu_emulator_stats fpuemustats;
72#ifdef CONFIG_DEBUG_FS
73DEFINE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats);
74#endif
72
73/* Control registers */
74
75#define FPCREG_RID 0 /* $0 = revision id */
76#define FPCREG_CSR 31 /* $31 = csr */
77
78/* Convert Mips rounding mode (0..3) to IEEE library modes. */
79static const unsigned char ieee_rm[4] = {

--- 124 unchanged lines hidden (view full) ---

204
205static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
206{
207 mips_instruction ir;
208 unsigned long emulpc, contpc;
209 unsigned int cond;
210
211 if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
75
76/* Control registers */
77
78#define FPCREG_RID 0 /* $0 = revision id */
79#define FPCREG_CSR 31 /* $31 = csr */
80
81/* Convert Mips rounding mode (0..3) to IEEE library modes. */
82static const unsigned char ieee_rm[4] = {

--- 124 unchanged lines hidden (view full) ---

207
208static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
209{
210 mips_instruction ir;
211 unsigned long emulpc, contpc;
212 unsigned int cond;
213
214 if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
212 fpuemustats.errors++;
215 MIPS_FPU_EMU_INC_STATS(errors);
213 return SIGBUS;
214 }
215
216 /* XXX NEC Vr54xx bug workaround */
217 if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))
218 xcp->cp0_cause &= ~CAUSEF_BD;
219
220 if (xcp->cp0_cause & CAUSEF_BD) {

--- 14 unchanged lines hidden (view full) ---

235 if (__compute_return_epc(xcp)) {
236#ifdef CP1DBG
237 printk("failed to emulate branch at %p\n",
238 (void *) (xcp->cp0_epc));
239#endif
240 return SIGILL;
241 }
242 if (get_user(ir, (mips_instruction __user *) emulpc)) {
216 return SIGBUS;
217 }
218
219 /* XXX NEC Vr54xx bug workaround */
220 if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))
221 xcp->cp0_cause &= ~CAUSEF_BD;
222
223 if (xcp->cp0_cause & CAUSEF_BD) {

--- 14 unchanged lines hidden (view full) ---

238 if (__compute_return_epc(xcp)) {
239#ifdef CP1DBG
240 printk("failed to emulate branch at %p\n",
241 (void *) (xcp->cp0_epc));
242#endif
243 return SIGILL;
244 }
245 if (get_user(ir, (mips_instruction __user *) emulpc)) {
243 fpuemustats.errors++;
246 MIPS_FPU_EMU_INC_STATS(errors);
244 return SIGBUS;
245 }
246 /* __compute_return_epc() will have updated cp0_epc */
247 contpc = xcp->cp0_epc;
248 /* In order not to confuse ptrace() et al, tweak context */
249 xcp->cp0_epc = emulpc - 4;
250 } else {
251 emulpc = xcp->cp0_epc;
252 contpc = xcp->cp0_epc + 4;
253 }
254
255 emul:
247 return SIGBUS;
248 }
249 /* __compute_return_epc() will have updated cp0_epc */
250 contpc = xcp->cp0_epc;
251 /* In order not to confuse ptrace() et al, tweak context */
252 xcp->cp0_epc = emulpc - 4;
253 } else {
254 emulpc = xcp->cp0_epc;
255 contpc = xcp->cp0_epc + 4;
256 }
257
258 emul:
256 fpuemustats.emulated++;
259 MIPS_FPU_EMU_INC_STATS(emulated);
257 switch (MIPSInst_OPCODE(ir)) {
258 case ldc1_op:{
259 u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
260 MIPSInst_SIMM(ir));
261 u64 val;
262
260 switch (MIPSInst_OPCODE(ir)) {
261 case ldc1_op:{
262 u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
263 MIPSInst_SIMM(ir));
264 u64 val;
265
263 fpuemustats.loads++;
266 MIPS_FPU_EMU_INC_STATS(loads);
264 if (get_user(val, va)) {
267 if (get_user(val, va)) {
265 fpuemustats.errors++;
268 MIPS_FPU_EMU_INC_STATS(errors);
266 return SIGBUS;
267 }
268 DITOREG(val, MIPSInst_RT(ir));
269 break;
270 }
271
272 case sdc1_op:{
273 u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
274 MIPSInst_SIMM(ir));
275 u64 val;
276
269 return SIGBUS;
270 }
271 DITOREG(val, MIPSInst_RT(ir));
272 break;
273 }
274
275 case sdc1_op:{
276 u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] +
277 MIPSInst_SIMM(ir));
278 u64 val;
279
277 fpuemustats.stores++;
280 MIPS_FPU_EMU_INC_STATS(stores);
278 DIFROMREG(val, MIPSInst_RT(ir));
279 if (put_user(val, va)) {
281 DIFROMREG(val, MIPSInst_RT(ir));
282 if (put_user(val, va)) {
280 fpuemustats.errors++;
283 MIPS_FPU_EMU_INC_STATS(errors);
281 return SIGBUS;
282 }
283 break;
284 }
285
286 case lwc1_op:{
287 u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
288 MIPSInst_SIMM(ir));
289 u32 val;
290
284 return SIGBUS;
285 }
286 break;
287 }
288
289 case lwc1_op:{
290 u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
291 MIPSInst_SIMM(ir));
292 u32 val;
293
291 fpuemustats.loads++;
294 MIPS_FPU_EMU_INC_STATS(loads);
292 if (get_user(val, va)) {
295 if (get_user(val, va)) {
293 fpuemustats.errors++;
296 MIPS_FPU_EMU_INC_STATS(errors);
294 return SIGBUS;
295 }
296 SITOREG(val, MIPSInst_RT(ir));
297 break;
298 }
299
300 case swc1_op:{
301 u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
302 MIPSInst_SIMM(ir));
303 u32 val;
304
297 return SIGBUS;
298 }
299 SITOREG(val, MIPSInst_RT(ir));
300 break;
301 }
302
303 case swc1_op:{
304 u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] +
305 MIPSInst_SIMM(ir));
306 u32 val;
307
305 fpuemustats.stores++;
308 MIPS_FPU_EMU_INC_STATS(stores);
306 SIFROMREG(val, MIPSInst_RT(ir));
307 if (put_user(val, va)) {
309 SIFROMREG(val, MIPSInst_RT(ir));
310 if (put_user(val, va)) {
308 fpuemustats.errors++;
311 MIPS_FPU_EMU_INC_STATS(errors);
309 return SIGBUS;
310 }
311 break;
312 }
313
314 case cop1_op:
315 switch (MIPSInst_RS(ir)) {
316

--- 107 unchanged lines hidden (view full) ---

424 * instruction
425 */
426 xcp->cp0_epc += 4;
427 contpc = (xcp->cp0_epc +
428 (MIPSInst_SIMM(ir) << 2));
429
430 if (get_user(ir,
431 (mips_instruction __user *) xcp->cp0_epc)) {
312 return SIGBUS;
313 }
314 break;
315 }
316
317 case cop1_op:
318 switch (MIPSInst_RS(ir)) {
319

--- 107 unchanged lines hidden (view full) ---

427 * instruction
428 */
429 xcp->cp0_epc += 4;
430 contpc = (xcp->cp0_epc +
431 (MIPSInst_SIMM(ir) << 2));
432
433 if (get_user(ir,
434 (mips_instruction __user *) xcp->cp0_epc)) {
432 fpuemustats.errors++;
435 MIPS_FPU_EMU_INC_STATS(errors);
433 return SIGBUS;
434 }
435
436 switch (MIPSInst_OPCODE(ir)) {
437 case lwc1_op:
438 case swc1_op:
439#if (__mips >= 2 || defined(__mips64))
440 case ldc1_op:

--- 149 unchanged lines hidden (view full) ---

590DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
591DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
592
593static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
594 mips_instruction ir)
595{
596 unsigned rcsr = 0; /* resulting csr */
597
436 return SIGBUS;
437 }
438
439 switch (MIPSInst_OPCODE(ir)) {
440 case lwc1_op:
441 case swc1_op:
442#if (__mips >= 2 || defined(__mips64))
443 case ldc1_op:

--- 149 unchanged lines hidden (view full) ---

593DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
594DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
595
596static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
597 mips_instruction ir)
598{
599 unsigned rcsr = 0; /* resulting csr */
600
598 fpuemustats.cp1xops++;
601 MIPS_FPU_EMU_INC_STATS(cp1xops);
599
600 switch (MIPSInst_FMA_FFMT(ir)) {
601 case s_fmt:{ /* 0 */
602
603 ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);
604 ieee754sp fd, fr, fs, ft;
605 u32 __user *va;
606 u32 val;
607
608 switch (MIPSInst_FUNC(ir)) {
609 case lwxc1_op:
610 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
611 xcp->regs[MIPSInst_FT(ir)]);
612
602
603 switch (MIPSInst_FMA_FFMT(ir)) {
604 case s_fmt:{ /* 0 */
605
606 ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);
607 ieee754sp fd, fr, fs, ft;
608 u32 __user *va;
609 u32 val;
610
611 switch (MIPSInst_FUNC(ir)) {
612 case lwxc1_op:
613 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
614 xcp->regs[MIPSInst_FT(ir)]);
615
613 fpuemustats.loads++;
616 MIPS_FPU_EMU_INC_STATS(loads);
614 if (get_user(val, va)) {
617 if (get_user(val, va)) {
615 fpuemustats.errors++;
618 MIPS_FPU_EMU_INC_STATS(errors);
616 return SIGBUS;
617 }
618 SITOREG(val, MIPSInst_FD(ir));
619 break;
620
621 case swxc1_op:
622 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
623 xcp->regs[MIPSInst_FT(ir)]);
624
619 return SIGBUS;
620 }
621 SITOREG(val, MIPSInst_FD(ir));
622 break;
623
624 case swxc1_op:
625 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
626 xcp->regs[MIPSInst_FT(ir)]);
627
625 fpuemustats.stores++;
628 MIPS_FPU_EMU_INC_STATS(stores);
626
627 SIFROMREG(val, MIPSInst_FS(ir));
628 if (put_user(val, va)) {
629
630 SIFROMREG(val, MIPSInst_FS(ir));
631 if (put_user(val, va)) {
629 fpuemustats.errors++;
632 MIPS_FPU_EMU_INC_STATS(errors);
630 return SIGBUS;
631 }
632 break;
633
634 case madd_s_op:
635 handler = fpemu_sp_madd;
636 goto scoptop;
637 case msub_s_op:

--- 44 unchanged lines hidden (view full) ---

682 u64 __user *va;
683 u64 val;
684
685 switch (MIPSInst_FUNC(ir)) {
686 case ldxc1_op:
687 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
688 xcp->regs[MIPSInst_FT(ir)]);
689
633 return SIGBUS;
634 }
635 break;
636
637 case madd_s_op:
638 handler = fpemu_sp_madd;
639 goto scoptop;
640 case msub_s_op:

--- 44 unchanged lines hidden (view full) ---

685 u64 __user *va;
686 u64 val;
687
688 switch (MIPSInst_FUNC(ir)) {
689 case ldxc1_op:
690 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
691 xcp->regs[MIPSInst_FT(ir)]);
692
690 fpuemustats.loads++;
693 MIPS_FPU_EMU_INC_STATS(loads);
691 if (get_user(val, va)) {
694 if (get_user(val, va)) {
692 fpuemustats.errors++;
695 MIPS_FPU_EMU_INC_STATS(errors);
693 return SIGBUS;
694 }
695 DITOREG(val, MIPSInst_FD(ir));
696 break;
697
698 case sdxc1_op:
699 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
700 xcp->regs[MIPSInst_FT(ir)]);
701
696 return SIGBUS;
697 }
698 DITOREG(val, MIPSInst_FD(ir));
699 break;
700
701 case sdxc1_op:
702 va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] +
703 xcp->regs[MIPSInst_FT(ir)]);
704
702 fpuemustats.stores++;
705 MIPS_FPU_EMU_INC_STATS(stores);
703 DIFROMREG(val, MIPSInst_FS(ir));
704 if (put_user(val, va)) {
706 DIFROMREG(val, MIPSInst_FS(ir));
707 if (put_user(val, va)) {
705 fpuemustats.errors++;
708 MIPS_FPU_EMU_INC_STATS(errors);
706 return SIGBUS;
707 }
708 break;
709
710 case madd_d_op:
711 handler = fpemu_dp_madd;
712 goto dcoptop;
713 case msub_d_op:

--- 50 unchanged lines hidden (view full) ---

764 ieee754dp d;
765 ieee754sp s;
766 int w;
767#ifdef __mips64
768 s64 l;
769#endif
770 } rv; /* resulting value */
771
709 return SIGBUS;
710 }
711 break;
712
713 case madd_d_op:
714 handler = fpemu_dp_madd;
715 goto dcoptop;
716 case msub_d_op:

--- 50 unchanged lines hidden (view full) ---

767 ieee754dp d;
768 ieee754sp s;
769 int w;
770#ifdef __mips64
771 s64 l;
772#endif
773 } rv; /* resulting value */
774
772 fpuemustats.cp1ops++;
775 MIPS_FPU_EMU_INC_STATS(cp1ops);
773 switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
774 case s_fmt:{ /* 0 */
775 union {
776 ieee754sp(*b) (ieee754sp, ieee754sp);
777 ieee754sp(*u) (ieee754sp);
778 } handler;
779
780 switch (MIPSInst_FUNC(ir)) {

--- 454 unchanged lines hidden (view full) ---

1235 mips_instruction insn;
1236 int sig = 0;
1237
1238 oldepc = xcp->cp0_epc;
1239 do {
1240 prevepc = xcp->cp0_epc;
1241
1242 if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
776 switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
777 case s_fmt:{ /* 0 */
778 union {
779 ieee754sp(*b) (ieee754sp, ieee754sp);
780 ieee754sp(*u) (ieee754sp);
781 } handler;
782
783 switch (MIPSInst_FUNC(ir)) {

--- 454 unchanged lines hidden (view full) ---

1238 mips_instruction insn;
1239 int sig = 0;
1240
1241 oldepc = xcp->cp0_epc;
1242 do {
1243 prevepc = xcp->cp0_epc;
1244
1245 if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
1243 fpuemustats.errors++;
1246 MIPS_FPU_EMU_INC_STATS(errors);
1244 return SIGBUS;
1245 }
1246 if (insn == 0)
1247 xcp->cp0_epc += 4; /* skip nops */
1248 else {
1249 /*
1250 * The 'ieee754_csr' is an alias of
1251 * ctx->fcr31. No need to copy ctx->fcr31 to

--- 19 unchanged lines hidden (view full) ---

1271 if (sig == SIGILL && xcp->cp0_epc != oldepc)
1272 /* but if epc has advanced, then ignore it */
1273 sig = 0;
1274
1275 return sig;
1276}
1277
1278#ifdef CONFIG_DEBUG_FS
1247 return SIGBUS;
1248 }
1249 if (insn == 0)
1250 xcp->cp0_epc += 4; /* skip nops */
1251 else {
1252 /*
1253 * The 'ieee754_csr' is an alias of
1254 * ctx->fcr31. No need to copy ctx->fcr31 to

--- 19 unchanged lines hidden (view full) ---

1274 if (sig == SIGILL && xcp->cp0_epc != oldepc)
1275 /* but if epc has advanced, then ignore it */
1276 sig = 0;
1277
1278 return sig;
1279}
1280
1281#ifdef CONFIG_DEBUG_FS
1282
1283static int fpuemu_stat_get(void *data, u64 *val)
1284{
1285 int cpu;
1286 unsigned long sum = 0;
1287 for_each_online_cpu(cpu) {
1288 struct mips_fpu_emulator_stats *ps;
1289 local_t *pv;
1290 ps = &per_cpu(fpuemustats, cpu);
1291 pv = (void *)ps + (unsigned long)data;
1292 sum += local_read(pv);
1293 }
1294 *val = sum;
1295 return 0;
1296}
1297DEFINE_SIMPLE_ATTRIBUTE(fops_fpuemu_stat, fpuemu_stat_get, NULL, "%llu\n");
1298
1279extern struct dentry *mips_debugfs_dir;
1280static int __init debugfs_fpuemu(void)
1281{
1282 struct dentry *d, *dir;
1299extern struct dentry *mips_debugfs_dir;
1300static int __init debugfs_fpuemu(void)
1301{
1302 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
1296 if (!mips_debugfs_dir)
1297 return -ENODEV;
1298 dir = debugfs_create_dir("fpuemustats", mips_debugfs_dir);
1299 if (!dir)
1300 return -ENOMEM;
1303
1304 if (!mips_debugfs_dir)
1305 return -ENODEV;
1306 dir = debugfs_create_dir("fpuemustats", mips_debugfs_dir);
1307 if (!dir)
1308 return -ENOMEM;
1301 for (i = 0; i < ARRAY_SIZE(vars); i++) {
1302 d = debugfs_create_u32(vars[i].name, S_IRUGO, dir, vars[i].v);
1303 if (!d)
1304 return -ENOMEM;
1305 }
1309
1310#define FPU_STAT_CREATE(M) \
1311 do { \
1312 d = debugfs_create_file(#M , S_IRUGO, dir, \
1313 (void *)offsetof(struct mips_fpu_emulator_stats, M), \
1314 &fops_fpuemu_stat); \
1315 if (!d) \
1316 return -ENOMEM; \
1317 } while (0)
1318
1319 FPU_STAT_CREATE(emulated);
1320 FPU_STAT_CREATE(loads);
1321 FPU_STAT_CREATE(stores);
1322 FPU_STAT_CREATE(cp1ops);
1323 FPU_STAT_CREATE(cp1xops);
1324 FPU_STAT_CREATE(errors);
1325
1306 return 0;
1307}
1308__initcall(debugfs_fpuemu);
1309#endif
1326 return 0;
1327}
1328__initcall(debugfs_fpuemu);
1329#endif