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