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