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
49void spe_handle_fpdata(struct trapframe *);
50void spe_handle_fpround(struct trapframe *);
51static int spe_emu_instr(uint32_t, struct fpemu *, struct fpn **, uint32_t *);
52
53static void
54save_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
95void
96enable_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
157void
158save_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 */
180void
181save_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
283enum {
284	NONE,
285	SINGLE,
286	DOUBLE,
287	VECTOR,
288};
289
290static 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. */
311static int
312spe_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 */
332static int
333spe_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
388static int
389spe_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 */
426static uint32_t
427spe_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 */
450static void
451spe_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
469void
470spe_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
667end:
668	spefscr |= (mfspr(SPR_SPEFSCR) & ~SPEFSCR_FINVS);
669	mtspr(SPR_SPEFSCR, spefscr);
670	frame->srr0 += 4;
671	mtmsr(msr);
672
673	return;
674}
675
676void
677spe_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