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