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