1 /*-
2 * Copyright (C) 1996 Wolfgang Solfrank.
3 * Copyright (C) 1996 TooLs GmbH.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by TooLs GmbH.
17 * 4. The name of TooLs GmbH may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $
32 */
33
34 #include <sys/cdefs.h>
35 #include <sys/param.h>
36 #include <sys/proc.h>
37 #include <sys/systm.h>
38 #include <sys/limits.h>
39
40 #include <machine/altivec.h>
41 #include <machine/fpu.h>
42 #include <machine/ieeefp.h>
43 #include <machine/pcb.h>
44 #include <machine/psl.h>
45
46 #include <powerpc/fpu/fpu_arith.h>
47 #include <powerpc/fpu/fpu_emu.h>
48 #include <powerpc/fpu/fpu_extern.h>
49
50 void spe_handle_fpdata(struct trapframe *);
51 void spe_handle_fpround(struct trapframe *);
52 static int spe_emu_instr(uint32_t, struct fpemu *, struct fpn **, uint32_t *);
53
54 static void
save_vec_int(struct thread * td)55 save_vec_int(struct thread *td)
56 {
57 int msr;
58 struct pcb *pcb;
59
60 pcb = td->td_pcb;
61
62 /*
63 * Temporarily re-enable the vector unit during the save
64 */
65 msr = mfmsr();
66 mtmsr(msr | PSL_VEC);
67
68 /*
69 * Save the vector registers and SPEFSCR to the PCB
70 */
71 #define EVSTDW(n) __asm ("evstdw %1,0(%0)" \
72 :: "b"(pcb->pcb_vec.vr[n]), "n"(n));
73 EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3);
74 EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7);
75 EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11);
76 EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15);
77 EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19);
78 EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23);
79 EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27);
80 EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31);
81 #undef EVSTDW
82
83 __asm ( "evxor 0,0,0\n"
84 "evmwumiaa 0,0,0\n"
85 "evstdd 0,0(%0)" :: "b"(&pcb->pcb_vec.spare[0]));
86 pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
87
88 /*
89 * Disable vector unit again
90 */
91 isync();
92 mtmsr(msr);
93
94 }
95
96 void
enable_vec(struct thread * td)97 enable_vec(struct thread *td)
98 {
99 int msr;
100 struct pcb *pcb;
101 struct trapframe *tf;
102
103 pcb = td->td_pcb;
104 tf = trapframe(td);
105
106 /*
107 * Save the thread's SPE CPU number, and set the CPU's current
108 * vector thread
109 */
110 td->td_pcb->pcb_veccpu = PCPU_GET(cpuid);
111 PCPU_SET(vecthread, td);
112
113 /*
114 * Enable the vector unit for when the thread returns from the
115 * exception. If this is the first time the unit has been used by
116 * the thread, initialise the vector registers and VSCR to 0, and
117 * set the flag to indicate that the vector unit is in use.
118 */
119 tf->srr1 |= PSL_VEC;
120 if (!(pcb->pcb_flags & PCB_VEC)) {
121 memset(&pcb->pcb_vec, 0, sizeof pcb->pcb_vec);
122 pcb->pcb_flags |= PCB_VEC;
123 pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
124 }
125
126 /*
127 * Temporarily enable the vector unit so the registers
128 * can be restored.
129 */
130 msr = mfmsr();
131 mtmsr(msr | PSL_VEC);
132
133 /* Restore SPEFSCR and ACC. Use %r0 as the scratch for ACC. */
134 mtspr(SPR_SPEFSCR, pcb->pcb_vec.vscr);
135 __asm __volatile("isync;evldd 0, 0(%0); evmra 0,0\n"
136 :: "b"(&pcb->pcb_vec.spare[0]));
137
138 /*
139 * The lower half of each register will be restored on trap return. Use
140 * %r0 as a scratch register, and restore it last.
141 */
142 #define EVLDW(n) __asm __volatile("evldw 0, 0(%0); evmergehilo "#n",0,"#n \
143 :: "b"(&pcb->pcb_vec.vr[n]));
144 EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4);
145 EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8);
146 EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12);
147 EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16);
148 EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20);
149 EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24);
150 EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28);
151 EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0);
152 #undef EVLDW
153
154 isync();
155 mtmsr(msr);
156 }
157
158 void
save_vec(struct thread * td)159 save_vec(struct thread *td)
160 {
161 struct pcb *pcb;
162
163 pcb = td->td_pcb;
164
165 save_vec_int(td);
166
167 /*
168 * Clear the current vec thread and pcb's CPU id
169 * XXX should this be left clear to allow lazy save/restore ?
170 */
171 pcb->pcb_veccpu = INT_MAX;
172 PCPU_SET(vecthread, NULL);
173 }
174
175 /*
176 * Save SPE state without dropping ownership. This will only save state if
177 * the current vector-thread is `td'. This is used for taking core dumps, so
178 * don't leak kernel information; overwrite the low words of each vector with
179 * their real value, taken from the thread's trap frame, unconditionally.
180 */
181 void
save_vec_nodrop(struct thread * td)182 save_vec_nodrop(struct thread *td)
183 {
184 struct pcb *pcb;
185 int i;
186
187 if (td == PCPU_GET(vecthread))
188 save_vec_int(td);
189
190 pcb = td->td_pcb;
191
192 for (i = 0; i < 32; i++) {
193 pcb->pcb_vec.vr[i][1] =
194 td->td_frame ? td->td_frame->fixreg[i] : 0;
195 }
196 }
197
198 #define SPE_INST_MASK 0x31f
199 #define EADD 0x200
200 #define ESUB 0x201
201 #define EABS 0x204
202 #define ENABS 0x205
203 #define ENEG 0x206
204 #define EMUL 0x208
205 #define EDIV 0x209
206 #define ECMPGT 0x20c
207 #define ECMPLT 0x20d
208 #define ECMPEQ 0x20e
209 #define ECFUI 0x210
210 #define ECFSI 0x211
211 #define ECTUI 0x214
212 #define ECTSI 0x215
213 #define ECTUF 0x216
214 #define ECTSF 0x217
215 #define ECTUIZ 0x218
216 #define ECTSIZ 0x21a
217
218 #define SPE 0x4
219 #define SPFP 0x6
220 #define DPFP 0x7
221
222 #define SPE_OPC 4
223 #define OPC_SHIFT 26
224
225 #define EVFSADD 0x280
226 #define EVFSSUB 0x281
227 #define EVFSABS 0x284
228 #define EVFSNABS 0x285
229 #define EVFSNEG 0x286
230 #define EVFSMUL 0x288
231 #define EVFSDIV 0x289
232 #define EVFSCMPGT 0x28c
233 #define EVFSCMPLT 0x28d
234 #define EVFSCMPEQ 0x28e
235 #define EVFSCFUI 0x290
236 #define EVFSCFSI 0x291
237 #define EVFSCTUI 0x294
238 #define EVFSCTSI 0x295
239 #define EVFSCTUF 0x296
240 #define EVFSCTSF 0x297
241 #define EVFSCTUIZ 0x298
242 #define EVFSCTSIZ 0x29a
243
244 #define EFSADD 0x2c0
245 #define EFSSUB 0x2c1
246 #define EFSABS 0x2c4
247 #define EFSNABS 0x2c5
248 #define EFSNEG 0x2c6
249 #define EFSMUL 0x2c8
250 #define EFSDIV 0x2c9
251 #define EFSCMPGT 0x2cc
252 #define EFSCMPLT 0x2cd
253 #define EFSCMPEQ 0x2ce
254 #define EFSCFD 0x2cf
255 #define EFSCFUI 0x2d0
256 #define EFSCFSI 0x2d1
257 #define EFSCTUI 0x2d4
258 #define EFSCTSI 0x2d5
259 #define EFSCTUF 0x2d6
260 #define EFSCTSF 0x2d7
261 #define EFSCTUIZ 0x2d8
262 #define EFSCTSIZ 0x2da
263
264 #define EFDADD 0x2e0
265 #define EFDSUB 0x2e1
266 #define EFDABS 0x2e4
267 #define EFDNABS 0x2e5
268 #define EFDNEG 0x2e6
269 #define EFDMUL 0x2e8
270 #define EFDDIV 0x2e9
271 #define EFDCMPGT 0x2ec
272 #define EFDCMPLT 0x2ed
273 #define EFDCMPEQ 0x2ee
274 #define EFDCFS 0x2ef
275 #define EFDCFUI 0x2f0
276 #define EFDCFSI 0x2f1
277 #define EFDCTUI 0x2f4
278 #define EFDCTSI 0x2f5
279 #define EFDCTUF 0x2f6
280 #define EFDCTSF 0x2f7
281 #define EFDCTUIZ 0x2f8
282 #define EFDCTSIZ 0x2fa
283
284 enum {
285 NONE,
286 SINGLE,
287 DOUBLE,
288 VECTOR,
289 };
290
fpscr_to_spefscr(uint32_t fpscr)291 static uint32_t fpscr_to_spefscr(uint32_t fpscr)
292 {
293 uint32_t spefscr;
294
295 spefscr = 0;
296
297 if (fpscr & FPSCR_VX)
298 spefscr |= SPEFSCR_FINV;
299 if (fpscr & FPSCR_OX)
300 spefscr |= SPEFSCR_FOVF;
301 if (fpscr & FPSCR_UX)
302 spefscr |= SPEFSCR_FUNF;
303 if (fpscr & FPSCR_ZX)
304 spefscr |= SPEFSCR_FDBZ;
305 if (fpscr & FPSCR_XX)
306 spefscr |= SPEFSCR_FX;
307
308 return (spefscr);
309 }
310
311 /* Sign is 0 for unsigned, 1 for signed. */
312 static int
spe_to_int(struct fpemu * fpemu,struct fpn * fpn,uint32_t * val,int sign)313 spe_to_int(struct fpemu *fpemu, struct fpn *fpn, uint32_t *val, int sign)
314 {
315 uint32_t res[2];
316
317 res[0] = fpu_ftox(fpemu, fpn, res);
318 if (res[0] != UINT_MAX && res[0] != 0)
319 fpemu->fe_cx |= FPSCR_OX;
320 else if (sign == 0 && res[0] != 0)
321 fpemu->fe_cx |= FPSCR_UX;
322 else
323 *val = res[1];
324
325 return (0);
326 }
327
328 /* Masked instruction */
329 /*
330 * For compare instructions, returns 1 if success, 0 if not. For all others,
331 * returns -1, or -2 if no result needs recorded.
332 */
333 static int
spe_emu_instr(uint32_t instr,struct fpemu * fpemu,struct fpn ** result,uint32_t * iresult)334 spe_emu_instr(uint32_t instr, struct fpemu *fpemu,
335 struct fpn **result, uint32_t *iresult)
336 {
337 switch (instr & SPE_INST_MASK) {
338 case EABS:
339 case ENABS:
340 case ENEG:
341 /* Taken care of elsewhere. */
342 break;
343 case ECTUIZ:
344 fpemu->fe_cx &= ~FPSCR_RN;
345 fpemu->fe_cx |= FP_RZ;
346 case ECTUI:
347 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 0);
348 return (-1);
349 case ECTSIZ:
350 fpemu->fe_cx &= ~FPSCR_RN;
351 fpemu->fe_cx |= FP_RZ;
352 case ECTSI:
353 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 1);
354 return (-1);
355 case EADD:
356 *result = fpu_add(fpemu);
357 break;
358 case ESUB:
359 *result = fpu_sub(fpemu);
360 break;
361 case EMUL:
362 *result = fpu_mul(fpemu);
363 break;
364 case EDIV:
365 *result = fpu_div(fpemu);
366 break;
367 case ECMPGT:
368 fpu_compare(fpemu, 0);
369 if (fpemu->fe_cx & FPSCR_FG)
370 return (1);
371 return (0);
372 case ECMPLT:
373 fpu_compare(fpemu, 0);
374 if (fpemu->fe_cx & FPSCR_FL)
375 return (1);
376 return (0);
377 case ECMPEQ:
378 fpu_compare(fpemu, 0);
379 if (fpemu->fe_cx & FPSCR_FE)
380 return (1);
381 return (0);
382 default:
383 printf("Unknown instruction %x\n", instr);
384 }
385
386 return (-1);
387 }
388
389 static int
spe_explode(struct fpemu * fe,struct fpn * fp,uint32_t type,uint32_t hi,uint32_t lo)390 spe_explode(struct fpemu *fe, struct fpn *fp, uint32_t type,
391 uint32_t hi, uint32_t lo)
392 {
393 uint32_t s;
394
395 fp->fp_sign = hi >> 31;
396 fp->fp_sticky = 0;
397 switch (type) {
398 case SINGLE:
399 s = fpu_stof(fp, hi);
400 break;
401
402 case DOUBLE:
403 s = fpu_dtof(fp, hi, lo);
404 break;
405 }
406
407 if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) {
408 /*
409 * Input is a signalling NaN. All operations that return
410 * an input NaN operand put it through a ``NaN conversion'',
411 * which basically just means ``turn on the quiet bit''.
412 * We do this here so that all NaNs internally look quiet
413 * (we can tell signalling ones by their class).
414 */
415 fp->fp_mant[0] |= FP_QUIETBIT;
416 fe->fe_cx = FPSCR_VXSNAN; /* assert invalid operand */
417 s = FPC_SNAN;
418 }
419 fp->fp_class = s;
420
421 return (0);
422 }
423
424 /*
425 * Save the high word of a 64-bit GPR for manipulation in the exception handler.
426 */
427 static uint32_t
spe_save_reg_high(int reg)428 spe_save_reg_high(int reg)
429 {
430 uint32_t vec[2];
431 #define EVSTDW(n) case n: __asm __volatile ("evstdw %1,0(%0)" \
432 :: "b"(vec), "n"(n) : "memory"); break;
433 switch (reg) {
434 EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3);
435 EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7);
436 EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11);
437 EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15);
438 EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19);
439 EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23);
440 EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27);
441 EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31);
442 }
443 #undef EVSTDW
444
445 return (vec[0]);
446 }
447
448 /*
449 * Load the given value into the high word of the requested register.
450 */
451 static void
spe_load_reg_high(int reg,uint32_t val)452 spe_load_reg_high(int reg, uint32_t val)
453 {
454 #define EVLDW(n) case n: __asm __volatile("evmergelo "#n",%0,"#n \
455 :: "r"(val)); break;
456 switch (reg) {
457 EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4);
458 EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8);
459 EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12);
460 EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16);
461 EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20);
462 EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24);
463 EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28);
464 EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0);
465 }
466 #undef EVLDW
467
468 }
469
470 void
spe_handle_fpdata(struct trapframe * frame)471 spe_handle_fpdata(struct trapframe *frame)
472 {
473 struct fpemu fpemu;
474 struct fpn *result;
475 uint32_t instr, instr_sec_op;
476 uint32_t cr_shift, ra, rb, rd, src;
477 uint32_t high, low, res, tmp; /* For vector operations. */
478 uint32_t spefscr = 0;
479 uint32_t ftod_res[2];
480 int width; /* Single, Double, Vector, Integer */
481 int err;
482 uint32_t msr;
483
484 err = fueword32((void *)frame->srr0, &instr);
485
486 if (err != 0)
487 return;
488 /* Fault. */;
489
490 if ((instr >> OPC_SHIFT) != SPE_OPC)
491 return;
492
493 msr = mfmsr();
494 /*
495 * 'cr' field is the upper 3 bits of rd. Magically, since a) rd is 5
496 * bits, b) each 'cr' field is 4 bits, and c) Only the 'GT' bit is
497 * modified for most compare operations, the full value of rd can be
498 * used as a shift value.
499 */
500 rd = (instr >> 21) & 0x1f;
501 ra = (instr >> 16) & 0x1f;
502 rb = (instr >> 11) & 0x1f;
503 src = (instr >> 5) & 0x7;
504 cr_shift = 28 - (rd & 0x1f);
505
506 instr_sec_op = (instr & 0x7ff);
507
508 memset(&fpemu, 0, sizeof(fpemu));
509
510 width = NONE;
511 switch (src) {
512 case SPE:
513 mtmsr(msr | PSL_VEC);
514 switch (instr_sec_op) {
515 case EVFSABS:
516 high = spe_save_reg_high(ra) & ~(1U << 31);
517 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
518 spe_load_reg_high(rd, high);
519 break;
520 case EVFSNABS:
521 high = spe_save_reg_high(ra) | (1U << 31);
522 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
523 spe_load_reg_high(rd, high);
524 break;
525 case EVFSNEG:
526 high = spe_save_reg_high(ra) ^ (1U << 31);
527 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
528 spe_load_reg_high(rd, high);
529 break;
530 default:
531 /* High word */
532 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
533 spe_save_reg_high(ra), 0);
534 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
535 spe_save_reg_high(rb), 0);
536 high = spe_emu_instr(instr_sec_op, &fpemu, &result,
537 &tmp);
538
539 if (high < 0)
540 spe_load_reg_high(rd, tmp);
541
542 spefscr = fpscr_to_spefscr(fpemu.fe_cx) << 16;
543 /* Clear the fpemu to start over on the lower bits. */
544 memset(&fpemu, 0, sizeof(fpemu));
545
546 /* Now low word */
547 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
548 frame->fixreg[ra], 0);
549 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
550 frame->fixreg[rb], 0);
551 spefscr |= fpscr_to_spefscr(fpemu.fe_cx);
552 low = spe_emu_instr(instr_sec_op, &fpemu, &result,
553 &frame->fixreg[rd]);
554 if (instr_sec_op == EVFSCMPEQ ||
555 instr_sec_op == EVFSCMPGT ||
556 instr_sec_op == EVFSCMPLT) {
557 res = (high << 3) | (low << 2) |
558 ((high | low) << 1) | (high & low);
559 width = NONE;
560 } else
561 width = VECTOR;
562 break;
563 }
564 goto end;
565
566 case SPFP:
567 switch (instr_sec_op) {
568 case EFSABS:
569 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
570 break;
571 case EFSNABS:
572 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
573 break;
574 case EFSNEG:
575 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
576 break;
577 case EFSCFD:
578 mtmsr(msr | PSL_VEC);
579 spe_explode(&fpemu, &fpemu.fe_f3, DOUBLE,
580 spe_save_reg_high(rb), frame->fixreg[rb]);
581 result = &fpemu.fe_f3;
582 width = SINGLE;
583 break;
584 default:
585 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
586 frame->fixreg[ra], 0);
587 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
588 frame->fixreg[rb], 0);
589 width = SINGLE;
590 }
591 break;
592 case DPFP:
593 mtmsr(msr | PSL_VEC);
594 switch (instr_sec_op) {
595 case EFDABS:
596 high = spe_save_reg_high(ra) & ~(1U << 31);
597 frame->fixreg[rd] = frame->fixreg[ra];
598 spe_load_reg_high(rd, high);
599 break;
600 case EFDNABS:
601 high = spe_save_reg_high(ra) | (1U << 31);
602 frame->fixreg[rd] = frame->fixreg[ra];
603 spe_load_reg_high(rd, high);
604 break;
605 case EFDNEG:
606 high = spe_save_reg_high(ra) ^ (1U << 31);
607 frame->fixreg[rd] = frame->fixreg[ra];
608 spe_load_reg_high(rd, high);
609 break;
610 case EFDCFS:
611 spe_explode(&fpemu, &fpemu.fe_f3, SINGLE,
612 frame->fixreg[rb], 0);
613 result = &fpemu.fe_f3;
614 width = DOUBLE;
615 break;
616 default:
617 spe_explode(&fpemu, &fpemu.fe_f1, DOUBLE,
618 spe_save_reg_high(ra), frame->fixreg[ra]);
619 spe_explode(&fpemu, &fpemu.fe_f2, DOUBLE,
620 spe_save_reg_high(rb), frame->fixreg[rb]);
621 width = DOUBLE;
622 }
623 break;
624 }
625 switch (instr_sec_op) {
626 case EFDCFS:
627 case EFSCFD:
628 /* Already handled. */
629 break;
630 default:
631 res = spe_emu_instr(instr_sec_op, &fpemu, &result,
632 &frame->fixreg[rd]);
633 if (res != -1)
634 res <<= 2;
635 break;
636 }
637
638 switch (instr_sec_op & SPE_INST_MASK) {
639 case ECMPEQ:
640 case ECMPGT:
641 case ECMPLT:
642 frame->cr &= ~(0xf << cr_shift);
643 frame->cr |= (res << cr_shift);
644 break;
645 case ECTUI:
646 case ECTUIZ:
647 case ECTSI:
648 case ECTSIZ:
649 break;
650 default:
651 switch (width) {
652 case NONE:
653 case VECTOR:
654 break;
655 case SINGLE:
656 frame->fixreg[rd] = fpu_ftos(&fpemu, result);
657 break;
658 case DOUBLE:
659 spe_load_reg_high(rd, fpu_ftod(&fpemu, result, ftod_res));
660 frame->fixreg[rd] = ftod_res[1];
661 break;
662 default:
663 panic("Unknown storage width %d", width);
664 break;
665 }
666 }
667
668 end:
669 spefscr |= (mfspr(SPR_SPEFSCR) & ~SPEFSCR_FINVS);
670 mtspr(SPR_SPEFSCR, spefscr);
671 frame->srr0 += 4;
672 mtmsr(msr);
673
674 return;
675 }
676
677 void
spe_handle_fpround(struct trapframe * frame)678 spe_handle_fpround(struct trapframe *frame)
679 {
680
681 /*
682 * Punt fpround exceptions for now. This leaves the truncated result in
683 * the register. We'll deal with overflow/underflow later.
684 */
685 return;
686 }
687