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 |