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