blob: 096780ce939de880e778806147a599a1fe1bb2c5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
9 * Copyright (C) 2000 MIPS Technologies, Inc.
10 *
11 * This program is free software; you can distribute it and/or modify it
12 * under the terms of the GNU General Public License (Version 2) as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 * for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
23 *
24 * A complete emulator for MIPS coprocessor 1 instructions. This is
25 * required for #float(switch) or #float(trap), where it catches all
26 * COP1 instructions via the "CoProcessor Unusable" exception.
27 *
28 * More surprisingly it is also required for #float(ieee), to help out
29 * the hardware fpu at the boundaries of the IEEE-754 representation
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
39#include <asm/inst.h>
40#include <asm/bootinfo.h>
41#include <asm/cpu.h>
42#include <asm/cpu-features.h>
43#include <asm/processor.h>
44#include <asm/ptrace.h>
45#include <asm/signal.h>
46#include <asm/mipsregs.h>
47#include <asm/fpu_emulator.h>
48#include <asm/uaccess.h>
49#include <asm/branch.h>
50
51#include "ieee754.h"
52#include "dsemul.h"
53
54/* Strap kernel emulator for full MIPS IV emulation */
55
56#ifdef __mips
57#undef __mips
58#endif
59#define __mips 4
60
61/* Function which emulates a floating point instruction. */
62
63static int fpu_emu(struct pt_regs *, struct mips_fpu_soft_struct *,
64 mips_instruction);
65
66#if __mips >= 4 && __mips != 32
67static int fpux_emu(struct pt_regs *,
68 struct mips_fpu_soft_struct *, mips_instruction);
69#endif
70
71/* Further private data for which no space exists in mips_fpu_soft_struct */
72
Ralf Baechle4a99d1e2005-05-11 12:02:48 +000073struct mips_fpu_emulator_stats fpuemustats;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75/* Control registers */
76
77#define FPCREG_RID 0 /* $0 = revision id */
78#define FPCREG_CSR 31 /* $31 = csr */
79
80/* Convert Mips rounding mode (0..3) to IEEE library modes. */
81static const unsigned char ieee_rm[4] = {
Ralf Baechlecd21dfc2005-04-28 13:39:10 +000082 [FPU_CSR_RN] = IEEE754_RN,
83 [FPU_CSR_RZ] = IEEE754_RZ,
84 [FPU_CSR_RU] = IEEE754_RU,
85 [FPU_CSR_RD] = IEEE754_RD,
86};
87/* Convert IEEE library modes to Mips rounding mode (0..3). */
88static const unsigned char mips_rm[4] = {
89 [IEEE754_RN] = FPU_CSR_RN,
90 [IEEE754_RZ] = FPU_CSR_RZ,
91 [IEEE754_RD] = FPU_CSR_RD,
92 [IEEE754_RU] = FPU_CSR_RU,
Linus Torvalds1da177e2005-04-16 15:20:36 -070093};
94
95#if __mips >= 4
96/* convert condition code register number to csr bit */
97static const unsigned int fpucondbit[8] = {
98 FPU_CSR_COND0,
99 FPU_CSR_COND1,
100 FPU_CSR_COND2,
101 FPU_CSR_COND3,
102 FPU_CSR_COND4,
103 FPU_CSR_COND5,
104 FPU_CSR_COND6,
105 FPU_CSR_COND7
106};
107#endif
108
109
110/*
111 * Redundant with logic already in kernel/branch.c,
112 * embedded in compute_return_epc. At some point,
113 * a single subroutine should be used across both
114 * modules.
115 */
116static int isBranchInstr(mips_instruction * i)
117{
118 switch (MIPSInst_OPCODE(*i)) {
119 case spec_op:
120 switch (MIPSInst_FUNC(*i)) {
121 case jalr_op:
122 case jr_op:
123 return 1;
124 }
125 break;
126
127 case bcond_op:
128 switch (MIPSInst_RT(*i)) {
129 case bltz_op:
130 case bgez_op:
131 case bltzl_op:
132 case bgezl_op:
133 case bltzal_op:
134 case bgezal_op:
135 case bltzall_op:
136 case bgezall_op:
137 return 1;
138 }
139 break;
140
141 case j_op:
142 case jal_op:
143 case jalx_op:
144 case beq_op:
145 case bne_op:
146 case blez_op:
147 case bgtz_op:
148 case beql_op:
149 case bnel_op:
150 case blezl_op:
151 case bgtzl_op:
152 return 1;
153
154 case cop0_op:
155 case cop1_op:
156 case cop2_op:
157 case cop1x_op:
158 if (MIPSInst_RS(*i) == bc_op)
159 return 1;
160 break;
161 }
162
163 return 0;
164}
165
166/*
167 * In the Linux kernel, we support selection of FPR format on the
168 * basis of the Status.FR bit. This does imply that, if a full 32
169 * FPRs are desired, there needs to be a flip-flop that can be written
170 * to one at that bit position. In any case, O32 MIPS ABI uses
171 * only the even FPRs (Status.FR = 0).
172 */
173
174#define CP0_STATUS_FR_SUPPORT
175
176#ifdef CP0_STATUS_FR_SUPPORT
177#define FR_BIT ST0_FR
178#else
179#define FR_BIT 0
180#endif
181
182#define SIFROMREG(si,x) ((si) = \
183 (xcp->cp0_status & FR_BIT) || !(x & 1) ? \
184 (int)ctx->fpr[x] : \
185 (int)(ctx->fpr[x & ~1] >> 32 ))
186#define SITOREG(si,x) (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] = \
187 (xcp->cp0_status & FR_BIT) || !(x & 1) ? \
188 ctx->fpr[x & ~1] >> 32 << 32 | (u32)(si) : \
189 ctx->fpr[x & ~1] << 32 >> 32 | (u64)(si) << 32)
190
191#define DIFROMREG(di,x) ((di) = \
192 ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)])
193#define DITOREG(di,x) (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] \
194 = (di))
195
196#define SPFROMREG(sp,x) SIFROMREG((sp).bits,x)
197#define SPTOREG(sp,x) SITOREG((sp).bits,x)
198#define DPFROMREG(dp,x) DIFROMREG((dp).bits,x)
199#define DPTOREG(dp,x) DITOREG((dp).bits,x)
200
201/*
202 * Emulate the single floating point instruction pointed at by EPC.
203 * Two instructions if the instruction is in a branch delay slot.
204 */
205
206static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx)
207{
208 mips_instruction ir;
Ralf Baechle333d1f62005-02-28 17:55:57 +0000209 void * emulpc, *contpc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 unsigned int cond;
211
212 if (get_user(ir, (mips_instruction *) xcp->cp0_epc)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000213 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 return SIGBUS;
215 }
216
217 /* XXX NEC Vr54xx bug workaround */
218 if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))
219 xcp->cp0_cause &= ~CAUSEF_BD;
220
221 if (xcp->cp0_cause & CAUSEF_BD) {
222 /*
223 * The instruction to be emulated is in a branch delay slot
224 * which means that we have to emulate the branch instruction
225 * BEFORE we do the cop1 instruction.
226 *
227 * This branch could be a COP1 branch, but in that case we
228 * would have had a trap for that instruction, and would not
229 * come through this route.
230 *
231 * Linux MIPS branch emulator operates on context, updating the
232 * cp0_epc.
233 */
Ralf Baechle333d1f62005-02-28 17:55:57 +0000234 emulpc = (void *) (xcp->cp0_epc + 4); /* Snapshot emulation target */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236 if (__compute_return_epc(xcp)) {
237#ifdef CP1DBG
238 printk("failed to emulate branch at %p\n",
Ralf Baechle333d1f62005-02-28 17:55:57 +0000239 (void *) (xcp->cp0_epc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240#endif
241 return SIGILL;
242 }
243 if (get_user(ir, (mips_instruction *) emulpc)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000244 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 return SIGBUS;
246 }
247 /* __compute_return_epc() will have updated cp0_epc */
Ralf Baechle333d1f62005-02-28 17:55:57 +0000248 contpc = (void *) xcp->cp0_epc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 /* In order not to confuse ptrace() et al, tweak context */
Ralf Baechle333d1f62005-02-28 17:55:57 +0000250 xcp->cp0_epc = (unsigned long) emulpc - 4;
251 } else {
252 emulpc = (void *) xcp->cp0_epc;
253 contpc = (void *) (xcp->cp0_epc + 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 }
255
256 emul:
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000257 fpuemustats.emulated++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 switch (MIPSInst_OPCODE(ir)) {
259#ifndef SINGLE_ONLY_FPU
260 case ldc1_op:{
Ralf Baechle333d1f62005-02-28 17:55:57 +0000261 u64 *va = (void *) (xcp->regs[MIPSInst_RS(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 MIPSInst_SIMM(ir));
263 u64 val;
264
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000265 fpuemustats.loads++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 if (get_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000267 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 return SIGBUS;
269 }
270 DITOREG(val, MIPSInst_RT(ir));
271 break;
272 }
273
274 case sdc1_op:{
Ralf Baechle333d1f62005-02-28 17:55:57 +0000275 u64 *va = (void *) (xcp->regs[MIPSInst_RS(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 MIPSInst_SIMM(ir));
277 u64 val;
278
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000279 fpuemustats.stores++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 DIFROMREG(val, MIPSInst_RT(ir));
281 if (put_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000282 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 return SIGBUS;
284 }
285 break;
286 }
287#endif
288
289 case lwc1_op:{
Ralf Baechle333d1f62005-02-28 17:55:57 +0000290 u32 *va = (void *) (xcp->regs[MIPSInst_RS(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 MIPSInst_SIMM(ir));
292 u32 val;
293
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000294 fpuemustats.loads++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 if (get_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000296 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 return SIGBUS;
298 }
299#ifdef SINGLE_ONLY_FPU
300 if (MIPSInst_RT(ir) & 1) {
301 /* illegal register in single-float mode */
302 return SIGILL;
303 }
304#endif
305 SITOREG(val, MIPSInst_RT(ir));
306 break;
307 }
308
309 case swc1_op:{
Ralf Baechle333d1f62005-02-28 17:55:57 +0000310 u32 *va = (void *) (xcp->regs[MIPSInst_RS(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 MIPSInst_SIMM(ir));
312 u32 val;
313
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000314 fpuemustats.stores++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315#ifdef SINGLE_ONLY_FPU
316 if (MIPSInst_RT(ir) & 1) {
317 /* illegal register in single-float mode */
318 return SIGILL;
319 }
320#endif
321 SIFROMREG(val, MIPSInst_RT(ir));
322 if (put_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000323 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 return SIGBUS;
325 }
326 break;
327 }
328
329 case cop1_op:
330 switch (MIPSInst_RS(ir)) {
331
Yoichi Yuasa766160c2005-09-03 15:56:22 -0700332#if defined(__mips64) && !defined(SINGLE_ONLY_FPU)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 case dmfc_op:
334 /* copregister fs -> gpr[rt] */
335 if (MIPSInst_RT(ir) != 0) {
336 DIFROMREG(xcp->regs[MIPSInst_RT(ir)],
337 MIPSInst_RD(ir));
338 }
339 break;
340
341 case dmtc_op:
342 /* copregister fs <- rt */
343 DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));
344 break;
345#endif
346
347 case mfc_op:
348 /* copregister rd -> gpr[rt] */
349#ifdef SINGLE_ONLY_FPU
350 if (MIPSInst_RD(ir) & 1) {
351 /* illegal register in single-float mode */
352 return SIGILL;
353 }
354#endif
355 if (MIPSInst_RT(ir) != 0) {
356 SIFROMREG(xcp->regs[MIPSInst_RT(ir)],
357 MIPSInst_RD(ir));
358 }
359 break;
360
361 case mtc_op:
362 /* copregister rd <- rt */
363#ifdef SINGLE_ONLY_FPU
364 if (MIPSInst_RD(ir) & 1) {
365 /* illegal register in single-float mode */
366 return SIGILL;
367 }
368#endif
369 SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir));
370 break;
371
372 case cfc_op:{
373 /* cop control register rd -> gpr[rt] */
374 u32 value;
375
376 if (ir == CP1UNDEF) {
377 return do_dsemulret(xcp);
378 }
379 if (MIPSInst_RD(ir) == FPCREG_CSR) {
380 value = ctx->fcr31;
Ralf Baechlecd21dfc2005-04-28 13:39:10 +0000381 value = (value & ~0x3) | mips_rm[value & 0x3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382#ifdef CSRTRACE
383 printk("%p gpr[%d]<-csr=%08x\n",
Ralf Baechle333d1f62005-02-28 17:55:57 +0000384 (void *) (xcp->cp0_epc),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 MIPSInst_RT(ir), value);
386#endif
387 }
388 else if (MIPSInst_RD(ir) == FPCREG_RID)
389 value = 0;
390 else
391 value = 0;
392 if (MIPSInst_RT(ir))
393 xcp->regs[MIPSInst_RT(ir)] = value;
394 break;
395 }
396
397 case ctc_op:{
398 /* copregister rd <- rt */
399 u32 value;
400
401 if (MIPSInst_RT(ir) == 0)
402 value = 0;
403 else
404 value = xcp->regs[MIPSInst_RT(ir)];
405
406 /* we only have one writable control reg
407 */
408 if (MIPSInst_RD(ir) == FPCREG_CSR) {
409#ifdef CSRTRACE
410 printk("%p gpr[%d]->csr=%08x\n",
Ralf Baechle333d1f62005-02-28 17:55:57 +0000411 (void *) (xcp->cp0_epc),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 MIPSInst_RT(ir), value);
413#endif
Ralf Baechlecd21dfc2005-04-28 13:39:10 +0000414 value &= (FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);
415 ctx->fcr31 &= ~(FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);
416 /* convert to ieee library modes */
417 ctx->fcr31 |= (value & ~0x3) | ieee_rm[value & 0x3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
419 if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
420 return SIGFPE;
421 }
422 break;
423 }
424
425 case bc_op:{
426 int likely = 0;
427
428 if (xcp->cp0_cause & CAUSEF_BD)
429 return SIGILL;
430
431#if __mips >= 4
432 cond = ctx->fcr31 & fpucondbit[MIPSInst_RT(ir) >> 2];
433#else
434 cond = ctx->fcr31 & FPU_CSR_COND;
435#endif
436 switch (MIPSInst_RT(ir) & 3) {
437 case bcfl_op:
438 likely = 1;
439 case bcf_op:
440 cond = !cond;
441 break;
442 case bctl_op:
443 likely = 1;
444 case bct_op:
445 break;
446 default:
447 /* thats an illegal instruction */
448 return SIGILL;
449 }
450
451 xcp->cp0_cause |= CAUSEF_BD;
452 if (cond) {
453 /* branch taken: emulate dslot
454 * instruction
455 */
456 xcp->cp0_epc += 4;
Ralf Baechle333d1f62005-02-28 17:55:57 +0000457 contpc = (void *)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 (xcp->cp0_epc +
459 (MIPSInst_SIMM(ir) << 2));
460
461 if (get_user(ir, (mips_instruction *)
Ralf Baechle333d1f62005-02-28 17:55:57 +0000462 (void *) xcp->cp0_epc)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000463 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 return SIGBUS;
465 }
466
467 switch (MIPSInst_OPCODE(ir)) {
468 case lwc1_op:
469 case swc1_op:
Ralf Baechle587cb982005-09-15 08:52:34 +0000470#if (__mips >= 2 || defined(__mips64)) && !defined(SINGLE_ONLY_FPU)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 case ldc1_op:
472 case sdc1_op:
473#endif
474 case cop1_op:
475#if __mips >= 4 && __mips != 32
476 case cop1x_op:
477#endif
478 /* its one of ours */
479 goto emul;
480#if __mips >= 4
481 case spec_op:
482 if (MIPSInst_FUNC(ir) == movc_op)
483 goto emul;
484 break;
485#endif
486 }
487
488 /*
489 * Single step the non-cp1
490 * instruction in the dslot
491 */
Ralf Baechle333d1f62005-02-28 17:55:57 +0000492 return mips_dsemul(xcp, ir, (unsigned long) contpc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 }
494 else {
495 /* branch not taken */
496 if (likely) {
497 /*
498 * branch likely nullifies
499 * dslot if not taken
500 */
501 xcp->cp0_epc += 4;
502 contpc += 4;
503 /*
504 * else continue & execute
505 * dslot as normal insn
506 */
507 }
508 }
509 break;
510 }
511
512 default:
513 if (!(MIPSInst_RS(ir) & 0x10))
514 return SIGILL;
515 {
516 int sig;
517
518 /* a real fpu computation instruction */
519 if ((sig = fpu_emu(xcp, ctx, ir)))
520 return sig;
521 }
522 }
523 break;
524
525#if __mips >= 4 && __mips != 32
526 case cop1x_op:{
527 int sig;
528
529 if ((sig = fpux_emu(xcp, ctx, ir)))
530 return sig;
531 break;
532 }
533#endif
534
535#if __mips >= 4
536 case spec_op:
537 if (MIPSInst_FUNC(ir) != movc_op)
538 return SIGILL;
539 cond = fpucondbit[MIPSInst_RT(ir) >> 2];
540 if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0))
541 xcp->regs[MIPSInst_RD(ir)] =
542 xcp->regs[MIPSInst_RS(ir)];
543 break;
544#endif
545
546 default:
547 return SIGILL;
548 }
549
550 /* we did it !! */
Ralf Baechle333d1f62005-02-28 17:55:57 +0000551 xcp->cp0_epc = (unsigned long) contpc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 xcp->cp0_cause &= ~CAUSEF_BD;
Ralf Baechle333d1f62005-02-28 17:55:57 +0000553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 return 0;
555}
556
557/*
558 * Conversion table from MIPS compare ops 48-63
559 * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig);
560 */
561static const unsigned char cmptab[8] = {
562 0, /* cmp_0 (sig) cmp_sf */
563 IEEE754_CUN, /* cmp_un (sig) cmp_ngle */
564 IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */
565 IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */
566 IEEE754_CLT, /* cmp_olt (sig) cmp_lt */
567 IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */
568 IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */
569 IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */
570};
571
572
573#if __mips >= 4 && __mips != 32
574
575/*
576 * Additional MIPS4 instructions
577 */
578
579#define DEF3OP(name, p, f1, f2, f3) \
580static ieee754##p fpemu_##p##_##name (ieee754##p r, ieee754##p s, \
581 ieee754##p t) \
582{ \
Ralf Baechlecd21dfc2005-04-28 13:39:10 +0000583 struct _ieee754_csr ieee754_csr_save; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 s = f1 (s, t); \
585 ieee754_csr_save = ieee754_csr; \
586 s = f2 (s, r); \
587 ieee754_csr_save.cx |= ieee754_csr.cx; \
588 ieee754_csr_save.sx |= ieee754_csr.sx; \
589 s = f3 (s); \
590 ieee754_csr.cx |= ieee754_csr_save.cx; \
591 ieee754_csr.sx |= ieee754_csr_save.sx; \
592 return s; \
593}
594
595static ieee754dp fpemu_dp_recip(ieee754dp d)
596{
597 return ieee754dp_div(ieee754dp_one(0), d);
598}
599
600static ieee754dp fpemu_dp_rsqrt(ieee754dp d)
601{
602 return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d));
603}
604
605static ieee754sp fpemu_sp_recip(ieee754sp s)
606{
607 return ieee754sp_div(ieee754sp_one(0), s);
608}
609
610static ieee754sp fpemu_sp_rsqrt(ieee754sp s)
611{
612 return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s));
613}
614
615DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add,);
616DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub,);
617DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg);
618DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg);
619DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add,);
620DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub,);
621DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
622DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
623
624static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx,
625 mips_instruction ir)
626{
627 unsigned rcsr = 0; /* resulting csr */
628
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000629 fpuemustats.cp1xops++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
631 switch (MIPSInst_FMA_FFMT(ir)) {
632 case s_fmt:{ /* 0 */
633
634 ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);
635 ieee754sp fd, fr, fs, ft;
636 u32 *va;
637 u32 val;
638
639 switch (MIPSInst_FUNC(ir)) {
640 case lwxc1_op:
Ralf Baechle333d1f62005-02-28 17:55:57 +0000641 va = (void *) (xcp->regs[MIPSInst_FR(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 xcp->regs[MIPSInst_FT(ir)]);
643
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000644 fpuemustats.loads++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 if (get_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000646 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 return SIGBUS;
648 }
649#ifdef SINGLE_ONLY_FPU
650 if (MIPSInst_FD(ir) & 1) {
651 /* illegal register in single-float
652 * mode
653 */
654 return SIGILL;
655 }
656#endif
657 SITOREG(val, MIPSInst_FD(ir));
658 break;
659
660 case swxc1_op:
Ralf Baechle333d1f62005-02-28 17:55:57 +0000661 va = (void *) (xcp->regs[MIPSInst_FR(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 xcp->regs[MIPSInst_FT(ir)]);
663
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000664 fpuemustats.stores++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665#ifdef SINGLE_ONLY_FPU
666 if (MIPSInst_FS(ir) & 1) {
667 /* illegal register in single-float
668 * mode
669 */
670 return SIGILL;
671 }
672#endif
673
674 SIFROMREG(val, MIPSInst_FS(ir));
675 if (put_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000676 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return SIGBUS;
678 }
679 break;
680
681 case madd_s_op:
682 handler = fpemu_sp_madd;
683 goto scoptop;
684 case msub_s_op:
685 handler = fpemu_sp_msub;
686 goto scoptop;
687 case nmadd_s_op:
688 handler = fpemu_sp_nmadd;
689 goto scoptop;
690 case nmsub_s_op:
691 handler = fpemu_sp_nmsub;
692 goto scoptop;
693
694 scoptop:
695 SPFROMREG(fr, MIPSInst_FR(ir));
696 SPFROMREG(fs, MIPSInst_FS(ir));
697 SPFROMREG(ft, MIPSInst_FT(ir));
698 fd = (*handler) (fr, fs, ft);
699 SPTOREG(fd, MIPSInst_FD(ir));
700
701 copcsr:
702 if (ieee754_cxtest(IEEE754_INEXACT))
703 rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S;
704 if (ieee754_cxtest(IEEE754_UNDERFLOW))
705 rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S;
706 if (ieee754_cxtest(IEEE754_OVERFLOW))
707 rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S;
708 if (ieee754_cxtest(IEEE754_INVALID_OPERATION))
709 rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;
710
711 ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
713 /*printk ("SIGFPE: fpu csr = %08x\n",
714 ctx->fcr31); */
715 return SIGFPE;
716 }
717
718 break;
719
720 default:
721 return SIGILL;
722 }
723 break;
724 }
725
726#ifndef SINGLE_ONLY_FPU
727 case d_fmt:{ /* 1 */
728 ieee754dp(*handler) (ieee754dp, ieee754dp, ieee754dp);
729 ieee754dp fd, fr, fs, ft;
730 u64 *va;
731 u64 val;
732
733 switch (MIPSInst_FUNC(ir)) {
734 case ldxc1_op:
Ralf Baechle333d1f62005-02-28 17:55:57 +0000735 va = (void *) (xcp->regs[MIPSInst_FR(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 xcp->regs[MIPSInst_FT(ir)]);
737
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000738 fpuemustats.loads++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 if (get_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000740 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 return SIGBUS;
742 }
743 DITOREG(val, MIPSInst_FD(ir));
744 break;
745
746 case sdxc1_op:
Ralf Baechle333d1f62005-02-28 17:55:57 +0000747 va = (void *) (xcp->regs[MIPSInst_FR(ir)] +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 xcp->regs[MIPSInst_FT(ir)]);
749
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000750 fpuemustats.stores++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 DIFROMREG(val, MIPSInst_FS(ir));
752 if (put_user(val, va)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000753 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 return SIGBUS;
755 }
756 break;
757
758 case madd_d_op:
759 handler = fpemu_dp_madd;
760 goto dcoptop;
761 case msub_d_op:
762 handler = fpemu_dp_msub;
763 goto dcoptop;
764 case nmadd_d_op:
765 handler = fpemu_dp_nmadd;
766 goto dcoptop;
767 case nmsub_d_op:
768 handler = fpemu_dp_nmsub;
769 goto dcoptop;
770
771 dcoptop:
772 DPFROMREG(fr, MIPSInst_FR(ir));
773 DPFROMREG(fs, MIPSInst_FS(ir));
774 DPFROMREG(ft, MIPSInst_FT(ir));
775 fd = (*handler) (fr, fs, ft);
776 DPTOREG(fd, MIPSInst_FD(ir));
777 goto copcsr;
778
779 default:
780 return SIGILL;
781 }
782 break;
783 }
784#endif
785
786 case 0x7: /* 7 */
787 if (MIPSInst_FUNC(ir) != pfetch_op) {
788 return SIGILL;
789 }
790 /* ignore prefx operation */
791 break;
792
793 default:
794 return SIGILL;
795 }
796
797 return 0;
798}
799#endif
800
801
802
803/*
804 * Emulate a single COP1 arithmetic instruction.
805 */
806static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx,
807 mips_instruction ir)
808{
809 int rfmt; /* resulting format */
810 unsigned rcsr = 0; /* resulting csr */
811 unsigned cond;
812 union {
813 ieee754dp d;
814 ieee754sp s;
815 int w;
Yoichi Yuasa766160c2005-09-03 15:56:22 -0700816#ifdef __mips64
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 s64 l;
818#endif
819 } rv; /* resulting value */
820
Ralf Baechle4a99d1e2005-05-11 12:02:48 +0000821 fpuemustats.cp1ops++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
823 case s_fmt:{ /* 0 */
824 union {
825 ieee754sp(*b) (ieee754sp, ieee754sp);
826 ieee754sp(*u) (ieee754sp);
827 } handler;
828
829 switch (MIPSInst_FUNC(ir)) {
830 /* binary ops */
831 case fadd_op:
832 handler.b = ieee754sp_add;
833 goto scopbop;
834 case fsub_op:
835 handler.b = ieee754sp_sub;
836 goto scopbop;
837 case fmul_op:
838 handler.b = ieee754sp_mul;
839 goto scopbop;
840 case fdiv_op:
841 handler.b = ieee754sp_div;
842 goto scopbop;
843
844 /* unary ops */
Ralf Baechle587cb982005-09-15 08:52:34 +0000845#if __mips >= 2 || defined(__mips64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 case fsqrt_op:
847 handler.u = ieee754sp_sqrt;
848 goto scopuop;
849#endif
850#if __mips >= 4 && __mips != 32
851 case frsqrt_op:
852 handler.u = fpemu_sp_rsqrt;
853 goto scopuop;
854 case frecip_op:
855 handler.u = fpemu_sp_recip;
856 goto scopuop;
857#endif
858#if __mips >= 4
859 case fmovc_op:
860 cond = fpucondbit[MIPSInst_FT(ir) >> 2];
861 if (((ctx->fcr31 & cond) != 0) !=
862 ((MIPSInst_FT(ir) & 1) != 0))
863 return 0;
864 SPFROMREG(rv.s, MIPSInst_FS(ir));
865 break;
866 case fmovz_op:
867 if (xcp->regs[MIPSInst_FT(ir)] != 0)
868 return 0;
869 SPFROMREG(rv.s, MIPSInst_FS(ir));
870 break;
871 case fmovn_op:
872 if (xcp->regs[MIPSInst_FT(ir)] == 0)
873 return 0;
874 SPFROMREG(rv.s, MIPSInst_FS(ir));
875 break;
876#endif
877 case fabs_op:
878 handler.u = ieee754sp_abs;
879 goto scopuop;
880 case fneg_op:
881 handler.u = ieee754sp_neg;
882 goto scopuop;
883 case fmov_op:
884 /* an easy one */
885 SPFROMREG(rv.s, MIPSInst_FS(ir));
886 goto copcsr;
887
888 /* binary op on handler */
889 scopbop:
890 {
891 ieee754sp fs, ft;
892
893 SPFROMREG(fs, MIPSInst_FS(ir));
894 SPFROMREG(ft, MIPSInst_FT(ir));
895
896 rv.s = (*handler.b) (fs, ft);
897 goto copcsr;
898 }
899 scopuop:
900 {
901 ieee754sp fs;
902
903 SPFROMREG(fs, MIPSInst_FS(ir));
904 rv.s = (*handler.u) (fs);
905 goto copcsr;
906 }
907 copcsr:
908 if (ieee754_cxtest(IEEE754_INEXACT))
909 rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S;
910 if (ieee754_cxtest(IEEE754_UNDERFLOW))
911 rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S;
912 if (ieee754_cxtest(IEEE754_OVERFLOW))
913 rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S;
914 if (ieee754_cxtest(IEEE754_ZERO_DIVIDE))
915 rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S;
916 if (ieee754_cxtest(IEEE754_INVALID_OPERATION))
917 rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;
918 break;
919
920 /* unary conv ops */
921 case fcvts_op:
922 return SIGILL; /* not defined */
923 case fcvtd_op:{
924#ifdef SINGLE_ONLY_FPU
925 return SIGILL; /* not defined */
926#else
927 ieee754sp fs;
928
929 SPFROMREG(fs, MIPSInst_FS(ir));
930 rv.d = ieee754dp_fsp(fs);
931 rfmt = d_fmt;
932 goto copcsr;
933 }
934#endif
935 case fcvtw_op:{
936 ieee754sp fs;
937
938 SPFROMREG(fs, MIPSInst_FS(ir));
939 rv.w = ieee754sp_tint(fs);
940 rfmt = w_fmt;
941 goto copcsr;
942 }
943
Ralf Baechle587cb982005-09-15 08:52:34 +0000944#if __mips >= 2 || defined(__mips64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 case fround_op:
946 case ftrunc_op:
947 case fceil_op:
948 case ffloor_op:{
949 unsigned int oldrm = ieee754_csr.rm;
950 ieee754sp fs;
951
952 SPFROMREG(fs, MIPSInst_FS(ir));
953 ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
954 rv.w = ieee754sp_tint(fs);
955 ieee754_csr.rm = oldrm;
956 rfmt = w_fmt;
957 goto copcsr;
958 }
959#endif /* __mips >= 2 */
960
Yoichi Yuasa766160c2005-09-03 15:56:22 -0700961#if defined(__mips64) && !defined(SINGLE_ONLY_FPU)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 case fcvtl_op:{
963 ieee754sp fs;
964
965 SPFROMREG(fs, MIPSInst_FS(ir));
966 rv.l = ieee754sp_tlong(fs);
967 rfmt = l_fmt;
968 goto copcsr;
969 }
970
971 case froundl_op:
972 case ftruncl_op:
973 case fceill_op:
974 case ffloorl_op:{
975 unsigned int oldrm = ieee754_csr.rm;
976 ieee754sp fs;
977
978 SPFROMREG(fs, MIPSInst_FS(ir));
979 ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
980 rv.l = ieee754sp_tlong(fs);
981 ieee754_csr.rm = oldrm;
982 rfmt = l_fmt;
983 goto copcsr;
984 }
Ralf Baechle587cb982005-09-15 08:52:34 +0000985#endif /* defined(__mips64) && !fpu(single) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
987 default:
988 if (MIPSInst_FUNC(ir) >= fcmp_op) {
989 unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
990 ieee754sp fs, ft;
991
992 SPFROMREG(fs, MIPSInst_FS(ir));
993 SPFROMREG(ft, MIPSInst_FT(ir));
994 rv.w = ieee754sp_cmp(fs, ft,
995 cmptab[cmpop & 0x7], cmpop & 0x8);
996 rfmt = -1;
997 if ((cmpop & 0x8) && ieee754_cxtest
998 (IEEE754_INVALID_OPERATION))
999 rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
1000 else
1001 goto copcsr;
1002
1003 }
1004 else {
1005 return SIGILL;
1006 }
1007 break;
1008 }
1009 break;
1010 }
1011
1012#ifndef SINGLE_ONLY_FPU
1013 case d_fmt:{
1014 union {
1015 ieee754dp(*b) (ieee754dp, ieee754dp);
1016 ieee754dp(*u) (ieee754dp);
1017 } handler;
1018
1019 switch (MIPSInst_FUNC(ir)) {
1020 /* binary ops */
1021 case fadd_op:
1022 handler.b = ieee754dp_add;
1023 goto dcopbop;
1024 case fsub_op:
1025 handler.b = ieee754dp_sub;
1026 goto dcopbop;
1027 case fmul_op:
1028 handler.b = ieee754dp_mul;
1029 goto dcopbop;
1030 case fdiv_op:
1031 handler.b = ieee754dp_div;
1032 goto dcopbop;
1033
1034 /* unary ops */
Ralf Baechle587cb982005-09-15 08:52:34 +00001035#if __mips >= 2 || defined(__mips64)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 case fsqrt_op:
1037 handler.u = ieee754dp_sqrt;
1038 goto dcopuop;
1039#endif
1040#if __mips >= 4 && __mips != 32
1041 case frsqrt_op:
1042 handler.u = fpemu_dp_rsqrt;
1043 goto dcopuop;
1044 case frecip_op:
1045 handler.u = fpemu_dp_recip;
1046 goto dcopuop;
1047#endif
1048#if __mips >= 4
1049 case fmovc_op:
1050 cond = fpucondbit[MIPSInst_FT(ir) >> 2];
1051 if (((ctx->fcr31 & cond) != 0) !=
1052 ((MIPSInst_FT(ir) & 1) != 0))
1053 return 0;
1054 DPFROMREG(rv.d, MIPSInst_FS(ir));
1055 break;
1056 case fmovz_op:
1057 if (xcp->regs[MIPSInst_FT(ir)] != 0)
1058 return 0;
1059 DPFROMREG(rv.d, MIPSInst_FS(ir));
1060 break;
1061 case fmovn_op:
1062 if (xcp->regs[MIPSInst_FT(ir)] == 0)
1063 return 0;
1064 DPFROMREG(rv.d, MIPSInst_FS(ir));
1065 break;
1066#endif
1067 case fabs_op:
1068 handler.u = ieee754dp_abs;
1069 goto dcopuop;
1070
1071 case fneg_op:
1072 handler.u = ieee754dp_neg;
1073 goto dcopuop;
1074
1075 case fmov_op:
1076 /* an easy one */
1077 DPFROMREG(rv.d, MIPSInst_FS(ir));
1078 goto copcsr;
1079
1080 /* binary op on handler */
1081 dcopbop:{
1082 ieee754dp fs, ft;
1083
1084 DPFROMREG(fs, MIPSInst_FS(ir));
1085 DPFROMREG(ft, MIPSInst_FT(ir));
1086
1087 rv.d = (*handler.b) (fs, ft);
1088 goto copcsr;
1089 }
1090 dcopuop:{
1091 ieee754dp fs;
1092
1093 DPFROMREG(fs, MIPSInst_FS(ir));
1094 rv.d = (*handler.u) (fs);
1095 goto copcsr;
1096 }
1097
1098 /* unary conv ops */
1099 case fcvts_op:{
1100 ieee754dp fs;
1101
1102 DPFROMREG(fs, MIPSInst_FS(ir));
1103 rv.s = ieee754sp_fdp(fs);
1104 rfmt = s_fmt;
1105 goto copcsr;
1106 }
1107 case fcvtd_op:
1108 return SIGILL; /* not defined */
1109
1110 case fcvtw_op:{
1111 ieee754dp fs;
1112
1113 DPFROMREG(fs, MIPSInst_FS(ir));
1114 rv.w = ieee754dp_tint(fs); /* wrong */
1115 rfmt = w_fmt;
1116 goto copcsr;
1117 }
1118
Ralf Baechle587cb982005-09-15 08:52:34 +00001119#if __mips >= 2 || defined(__mips64)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 case fround_op:
1121 case ftrunc_op:
1122 case fceil_op:
1123 case ffloor_op:{
1124 unsigned int oldrm = ieee754_csr.rm;
1125 ieee754dp fs;
1126
1127 DPFROMREG(fs, MIPSInst_FS(ir));
1128 ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
1129 rv.w = ieee754dp_tint(fs);
1130 ieee754_csr.rm = oldrm;
1131 rfmt = w_fmt;
1132 goto copcsr;
1133 }
1134#endif
1135
Yoichi Yuasa766160c2005-09-03 15:56:22 -07001136#if defined(__mips64) && !defined(SINGLE_ONLY_FPU)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 case fcvtl_op:{
1138 ieee754dp fs;
1139
1140 DPFROMREG(fs, MIPSInst_FS(ir));
1141 rv.l = ieee754dp_tlong(fs);
1142 rfmt = l_fmt;
1143 goto copcsr;
1144 }
1145
1146 case froundl_op:
1147 case ftruncl_op:
1148 case fceill_op:
1149 case ffloorl_op:{
1150 unsigned int oldrm = ieee754_csr.rm;
1151 ieee754dp fs;
1152
1153 DPFROMREG(fs, MIPSInst_FS(ir));
1154 ieee754_csr.rm = ieee_rm[MIPSInst_FUNC(ir) & 0x3];
1155 rv.l = ieee754dp_tlong(fs);
1156 ieee754_csr.rm = oldrm;
1157 rfmt = l_fmt;
1158 goto copcsr;
1159 }
1160#endif /* __mips >= 3 && !fpu(single) */
1161
1162 default:
1163 if (MIPSInst_FUNC(ir) >= fcmp_op) {
1164 unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
1165 ieee754dp fs, ft;
1166
1167 DPFROMREG(fs, MIPSInst_FS(ir));
1168 DPFROMREG(ft, MIPSInst_FT(ir));
1169 rv.w = ieee754dp_cmp(fs, ft,
1170 cmptab[cmpop & 0x7], cmpop & 0x8);
1171 rfmt = -1;
1172 if ((cmpop & 0x8)
1173 &&
1174 ieee754_cxtest
1175 (IEEE754_INVALID_OPERATION))
1176 rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
1177 else
1178 goto copcsr;
1179
1180 }
1181 else {
1182 return SIGILL;
1183 }
1184 break;
1185 }
1186 break;
1187 }
1188#endif /* ifndef SINGLE_ONLY_FPU */
1189
1190 case w_fmt:{
1191 ieee754sp fs;
1192
1193 switch (MIPSInst_FUNC(ir)) {
1194 case fcvts_op:
1195 /* convert word to single precision real */
1196 SPFROMREG(fs, MIPSInst_FS(ir));
1197 rv.s = ieee754sp_fint(fs.bits);
1198 rfmt = s_fmt;
1199 goto copcsr;
1200#ifndef SINGLE_ONLY_FPU
1201 case fcvtd_op:
1202 /* convert word to double precision real */
1203 SPFROMREG(fs, MIPSInst_FS(ir));
1204 rv.d = ieee754dp_fint(fs.bits);
1205 rfmt = d_fmt;
1206 goto copcsr;
1207#endif
1208 default:
1209 return SIGILL;
1210 }
1211 break;
1212 }
1213
Yoichi Yuasa766160c2005-09-03 15:56:22 -07001214#if defined(__mips64) && !defined(SINGLE_ONLY_FPU)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 case l_fmt:{
1216 switch (MIPSInst_FUNC(ir)) {
1217 case fcvts_op:
1218 /* convert long to single precision real */
1219 rv.s = ieee754sp_flong(ctx->fpr[MIPSInst_FS(ir)]);
1220 rfmt = s_fmt;
1221 goto copcsr;
1222 case fcvtd_op:
1223 /* convert long to double precision real */
1224 rv.d = ieee754dp_flong(ctx->fpr[MIPSInst_FS(ir)]);
1225 rfmt = d_fmt;
1226 goto copcsr;
1227 default:
1228 return SIGILL;
1229 }
1230 break;
1231 }
1232#endif
1233
1234 default:
1235 return SIGILL;
1236 }
1237
1238 /*
1239 * Update the fpu CSR register for this operation.
1240 * If an exception is required, generate a tidy SIGFPE exception,
1241 * without updating the result register.
1242 * Note: cause exception bits do not accumulate, they are rewritten
1243 * for each op; only the flag/sticky bits accumulate.
1244 */
1245 ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
1246 if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
1247 /*printk ("SIGFPE: fpu csr = %08x\n",ctx->fcr31); */
1248 return SIGFPE;
1249 }
1250
1251 /*
1252 * Now we can safely write the result back to the register file.
1253 */
1254 switch (rfmt) {
1255 case -1:{
1256#if __mips >= 4
1257 cond = fpucondbit[MIPSInst_FD(ir) >> 2];
1258#else
1259 cond = FPU_CSR_COND;
1260#endif
1261 if (rv.w)
1262 ctx->fcr31 |= cond;
1263 else
1264 ctx->fcr31 &= ~cond;
1265 break;
1266 }
1267#ifndef SINGLE_ONLY_FPU
1268 case d_fmt:
1269 DPTOREG(rv.d, MIPSInst_FD(ir));
1270 break;
1271#endif
1272 case s_fmt:
1273 SPTOREG(rv.s, MIPSInst_FD(ir));
1274 break;
1275 case w_fmt:
1276 SITOREG(rv.w, MIPSInst_FD(ir));
1277 break;
Yoichi Yuasa766160c2005-09-03 15:56:22 -07001278#if defined(__mips64) && !defined(SINGLE_ONLY_FPU)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 case l_fmt:
1280 DITOREG(rv.l, MIPSInst_FD(ir));
1281 break;
1282#endif
1283 default:
1284 return SIGILL;
1285 }
1286
1287 return 0;
1288}
1289
1290int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp,
1291 struct mips_fpu_soft_struct *ctx)
1292{
Ralf Baechle333d1f62005-02-28 17:55:57 +00001293 unsigned long oldepc, prevepc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 mips_instruction insn;
1295 int sig = 0;
1296
1297 oldepc = xcp->cp0_epc;
1298 do {
1299 prevepc = xcp->cp0_epc;
1300
1301 if (get_user(insn, (mips_instruction *) xcp->cp0_epc)) {
Ralf Baechle4a99d1e2005-05-11 12:02:48 +00001302 fpuemustats.errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 return SIGBUS;
1304 }
1305 if (insn == 0)
1306 xcp->cp0_epc += 4; /* skip nops */
1307 else {
Ralf Baechlecd21dfc2005-04-28 13:39:10 +00001308 /*
1309 * The 'ieee754_csr' is an alias of
1310 * ctx->fcr31. No need to copy ctx->fcr31 to
1311 * ieee754_csr. But ieee754_csr.rm is ieee
1312 * library modes. (not mips rounding mode)
1313 */
1314 /* convert to ieee library modes */
1315 ieee754_csr.rm = ieee_rm[ieee754_csr.rm];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 sig = cop1Emulate(xcp, ctx);
Ralf Baechlecd21dfc2005-04-28 13:39:10 +00001317 /* revert to mips rounding mode */
1318 ieee754_csr.rm = mips_rm[ieee754_csr.rm];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 }
1320
1321 if (cpu_has_fpu)
1322 break;
1323 if (sig)
1324 break;
1325
1326 cond_resched();
1327 } while (xcp->cp0_epc > prevepc);
1328
1329 /* SIGILL indicates a non-fpu instruction */
1330 if (sig == SIGILL && xcp->cp0_epc != oldepc)
1331 /* but if epc has advanced, then ignore it */
1332 sig = 0;
1333
1334 return sig;
1335}