xref: /freebsd/sys/powerpc/booke/spe.c (revision 783d3ff6d7fae619db8a7990b8a6387de0c677b5)
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 }
686