]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/booke/spe.c
Merge llvm-project release/17.x llvmorg-17.0.0-rc4-10-g0176e8729ea4
[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/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
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
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
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
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
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
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
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
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
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
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
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
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 }