xref: /freebsd-src/sys/powerpc/booke/spe.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1dc9b124dSJustin Hibbits /*-
2dc9b124dSJustin Hibbits  * Copyright (C) 1996 Wolfgang Solfrank.
3dc9b124dSJustin Hibbits  * Copyright (C) 1996 TooLs GmbH.
4dc9b124dSJustin Hibbits  * All rights reserved.
5dc9b124dSJustin Hibbits  *
6dc9b124dSJustin Hibbits  * Redistribution and use in source and binary forms, with or without
7dc9b124dSJustin Hibbits  * modification, are permitted provided that the following conditions
8dc9b124dSJustin Hibbits  * are met:
9dc9b124dSJustin Hibbits  * 1. Redistributions of source code must retain the above copyright
10dc9b124dSJustin Hibbits  *    notice, this list of conditions and the following disclaimer.
11dc9b124dSJustin Hibbits  * 2. Redistributions in binary form must reproduce the above copyright
12dc9b124dSJustin Hibbits  *    notice, this list of conditions and the following disclaimer in the
13dc9b124dSJustin Hibbits  *    documentation and/or other materials provided with the distribution.
14dc9b124dSJustin Hibbits  * 3. All advertising materials mentioning features or use of this software
15dc9b124dSJustin Hibbits  *    must display the following acknowledgement:
16dc9b124dSJustin Hibbits  *	This product includes software developed by TooLs GmbH.
17dc9b124dSJustin Hibbits  * 4. The name of TooLs GmbH may not be used to endorse or promote products
18dc9b124dSJustin Hibbits  *    derived from this software without specific prior written permission.
19dc9b124dSJustin Hibbits  *
20dc9b124dSJustin Hibbits  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21dc9b124dSJustin Hibbits  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22dc9b124dSJustin Hibbits  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23dc9b124dSJustin Hibbits  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24dc9b124dSJustin Hibbits  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25dc9b124dSJustin Hibbits  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26dc9b124dSJustin Hibbits  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27dc9b124dSJustin Hibbits  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28dc9b124dSJustin Hibbits  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29dc9b124dSJustin Hibbits  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30dc9b124dSJustin Hibbits  *
31dc9b124dSJustin Hibbits  *	$NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $
32dc9b124dSJustin Hibbits  */
33dc9b124dSJustin Hibbits 
34dc9b124dSJustin Hibbits #include <sys/param.h>
35dc9b124dSJustin Hibbits #include <sys/proc.h>
36dc9b124dSJustin Hibbits #include <sys/systm.h>
37dc9b124dSJustin Hibbits #include <sys/limits.h>
38dc9b124dSJustin Hibbits 
39dc9b124dSJustin Hibbits #include <machine/altivec.h>
40289041e2SJustin Hibbits #include <machine/fpu.h>
41289041e2SJustin Hibbits #include <machine/ieeefp.h>
42dc9b124dSJustin Hibbits #include <machine/pcb.h>
43dc9b124dSJustin Hibbits #include <machine/psl.h>
44dc9b124dSJustin Hibbits 
45289041e2SJustin Hibbits #include <powerpc/fpu/fpu_arith.h>
46289041e2SJustin Hibbits #include <powerpc/fpu/fpu_emu.h>
47289041e2SJustin Hibbits #include <powerpc/fpu/fpu_extern.h>
48289041e2SJustin Hibbits 
49289041e2SJustin Hibbits void spe_handle_fpdata(struct trapframe *);
50289041e2SJustin Hibbits void spe_handle_fpround(struct trapframe *);
51289041e2SJustin Hibbits static int spe_emu_instr(uint32_t, struct fpemu *, struct fpn **, uint32_t *);
52289041e2SJustin Hibbits 
53dc9b124dSJustin Hibbits static void
save_vec_int(struct thread * td)54dc9b124dSJustin Hibbits save_vec_int(struct thread *td)
55dc9b124dSJustin Hibbits {
56dc9b124dSJustin Hibbits 	int	msr;
57dc9b124dSJustin Hibbits 	struct	pcb *pcb;
58dc9b124dSJustin Hibbits 
59dc9b124dSJustin Hibbits 	pcb = td->td_pcb;
60dc9b124dSJustin Hibbits 
61dc9b124dSJustin Hibbits 	/*
62dc9b124dSJustin Hibbits 	 * Temporarily re-enable the vector unit during the save
63dc9b124dSJustin Hibbits 	 */
64dc9b124dSJustin Hibbits 	msr = mfmsr();
65dc9b124dSJustin Hibbits 	mtmsr(msr | PSL_VEC);
66dc9b124dSJustin Hibbits 
67dc9b124dSJustin Hibbits 	/*
68dc9b124dSJustin Hibbits 	 * Save the vector registers and SPEFSCR to the PCB
69dc9b124dSJustin Hibbits 	 */
70dc9b124dSJustin Hibbits #define EVSTDW(n)   __asm ("evstdw %1,0(%0)" \
71dc9b124dSJustin Hibbits 		:: "b"(pcb->pcb_vec.vr[n]), "n"(n));
72dc9b124dSJustin Hibbits 	EVSTDW(0);	EVSTDW(1);	EVSTDW(2);	EVSTDW(3);
73dc9b124dSJustin Hibbits 	EVSTDW(4);	EVSTDW(5);	EVSTDW(6);	EVSTDW(7);
74dc9b124dSJustin Hibbits 	EVSTDW(8);	EVSTDW(9);	EVSTDW(10);	EVSTDW(11);
75dc9b124dSJustin Hibbits 	EVSTDW(12);	EVSTDW(13);	EVSTDW(14);	EVSTDW(15);
76dc9b124dSJustin Hibbits 	EVSTDW(16);	EVSTDW(17);	EVSTDW(18);	EVSTDW(19);
77dc9b124dSJustin Hibbits 	EVSTDW(20);	EVSTDW(21);	EVSTDW(22);	EVSTDW(23);
78dc9b124dSJustin Hibbits 	EVSTDW(24);	EVSTDW(25);	EVSTDW(26);	EVSTDW(27);
79dc9b124dSJustin Hibbits 	EVSTDW(28);	EVSTDW(29);	EVSTDW(30);	EVSTDW(31);
80dc9b124dSJustin Hibbits #undef EVSTDW
81dc9b124dSJustin Hibbits 
82dc9b124dSJustin Hibbits 	__asm ( "evxor 0,0,0\n"
83a638bf2aSBrandon Bergren 		"evmwumiaa 0,0,0\n"
84289041e2SJustin Hibbits 		"evstdd 0,0(%0)" :: "b"(&pcb->pcb_vec.spare[0]));
85dc9b124dSJustin Hibbits 	pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
86dc9b124dSJustin Hibbits 
87dc9b124dSJustin Hibbits 	/*
88dc9b124dSJustin Hibbits 	 * Disable vector unit again
89dc9b124dSJustin Hibbits 	 */
90dc9b124dSJustin Hibbits 	isync();
91dc9b124dSJustin Hibbits 	mtmsr(msr);
92dc9b124dSJustin Hibbits 
93dc9b124dSJustin Hibbits }
94dc9b124dSJustin Hibbits 
95dc9b124dSJustin Hibbits void
enable_vec(struct thread * td)96dc9b124dSJustin Hibbits enable_vec(struct thread *td)
97dc9b124dSJustin Hibbits {
98dc9b124dSJustin Hibbits 	int	msr;
99dc9b124dSJustin Hibbits 	struct	pcb *pcb;
100dc9b124dSJustin Hibbits 	struct	trapframe *tf;
101dc9b124dSJustin Hibbits 
102dc9b124dSJustin Hibbits 	pcb = td->td_pcb;
103dc9b124dSJustin Hibbits 	tf = trapframe(td);
104dc9b124dSJustin Hibbits 
105dc9b124dSJustin Hibbits 	/*
106dc9b124dSJustin Hibbits 	 * Save the thread's SPE CPU number, and set the CPU's current
107dc9b124dSJustin Hibbits 	 * vector thread
108dc9b124dSJustin Hibbits 	 */
109dc9b124dSJustin Hibbits 	td->td_pcb->pcb_veccpu = PCPU_GET(cpuid);
110dc9b124dSJustin Hibbits 	PCPU_SET(vecthread, td);
111dc9b124dSJustin Hibbits 
112dc9b124dSJustin Hibbits 	/*
113dc9b124dSJustin Hibbits 	 * Enable the vector unit for when the thread returns from the
114dc9b124dSJustin Hibbits 	 * exception. If this is the first time the unit has been used by
115dc9b124dSJustin Hibbits 	 * the thread, initialise the vector registers and VSCR to 0, and
116dc9b124dSJustin Hibbits 	 * set the flag to indicate that the vector unit is in use.
117dc9b124dSJustin Hibbits 	 */
118dc9b124dSJustin Hibbits 	tf->srr1 |= PSL_VEC;
119dc9b124dSJustin Hibbits 	if (!(pcb->pcb_flags & PCB_VEC)) {
120dc9b124dSJustin Hibbits 		memset(&pcb->pcb_vec, 0, sizeof pcb->pcb_vec);
121dc9b124dSJustin Hibbits 		pcb->pcb_flags |= PCB_VEC;
122289041e2SJustin Hibbits 		pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
123dc9b124dSJustin Hibbits 	}
124dc9b124dSJustin Hibbits 
125dc9b124dSJustin Hibbits 	/*
126dc9b124dSJustin Hibbits 	 * Temporarily enable the vector unit so the registers
127dc9b124dSJustin Hibbits 	 * can be restored.
128dc9b124dSJustin Hibbits 	 */
129dc9b124dSJustin Hibbits 	msr = mfmsr();
130dc9b124dSJustin Hibbits 	mtmsr(msr | PSL_VEC);
131dc9b124dSJustin Hibbits 
132dc9b124dSJustin Hibbits 	/* Restore SPEFSCR and ACC.  Use %r0 as the scratch for ACC. */
133dc9b124dSJustin Hibbits 	mtspr(SPR_SPEFSCR, pcb->pcb_vec.vscr);
1344f9ed315SBrandon Bergren 	__asm __volatile("isync;evldd 0, 0(%0); evmra 0,0\n"
135289041e2SJustin Hibbits 	    :: "b"(&pcb->pcb_vec.spare[0]));
136dc9b124dSJustin Hibbits 
137dc9b124dSJustin Hibbits 	/*
138dc9b124dSJustin Hibbits 	 * The lower half of each register will be restored on trap return.  Use
139dc9b124dSJustin Hibbits 	 * %r0 as a scratch register, and restore it last.
140dc9b124dSJustin Hibbits 	 */
141dc9b124dSJustin Hibbits #define	EVLDW(n)   __asm __volatile("evldw 0, 0(%0); evmergehilo "#n",0,"#n \
142dc9b124dSJustin Hibbits 	    :: "b"(&pcb->pcb_vec.vr[n]));
143dc9b124dSJustin Hibbits 	EVLDW(1);	EVLDW(2);	EVLDW(3);	EVLDW(4);
144dc9b124dSJustin Hibbits 	EVLDW(5);	EVLDW(6);	EVLDW(7);	EVLDW(8);
145dc9b124dSJustin Hibbits 	EVLDW(9);	EVLDW(10);	EVLDW(11);	EVLDW(12);
146dc9b124dSJustin Hibbits 	EVLDW(13);	EVLDW(14);	EVLDW(15);	EVLDW(16);
147dc9b124dSJustin Hibbits 	EVLDW(17);	EVLDW(18);	EVLDW(19);	EVLDW(20);
148dc9b124dSJustin Hibbits 	EVLDW(21);	EVLDW(22);	EVLDW(23);	EVLDW(24);
149dc9b124dSJustin Hibbits 	EVLDW(25);	EVLDW(26);	EVLDW(27);	EVLDW(28);
150dc9b124dSJustin Hibbits 	EVLDW(29);	EVLDW(30);	EVLDW(31);	EVLDW(0);
151dc9b124dSJustin Hibbits #undef EVLDW
152dc9b124dSJustin Hibbits 
153dc9b124dSJustin Hibbits 	isync();
154dc9b124dSJustin Hibbits 	mtmsr(msr);
155dc9b124dSJustin Hibbits }
156dc9b124dSJustin Hibbits 
157dc9b124dSJustin Hibbits void
save_vec(struct thread * td)158dc9b124dSJustin Hibbits save_vec(struct thread *td)
159dc9b124dSJustin Hibbits {
160dc9b124dSJustin Hibbits 	struct pcb *pcb;
161dc9b124dSJustin Hibbits 
162dc9b124dSJustin Hibbits 	pcb = td->td_pcb;
163dc9b124dSJustin Hibbits 
164dc9b124dSJustin Hibbits 	save_vec_int(td);
165dc9b124dSJustin Hibbits 
166dc9b124dSJustin Hibbits 	/*
167dc9b124dSJustin Hibbits 	 * Clear the current vec thread and pcb's CPU id
168dc9b124dSJustin Hibbits 	 * XXX should this be left clear to allow lazy save/restore ?
169dc9b124dSJustin Hibbits 	 */
170dc9b124dSJustin Hibbits 	pcb->pcb_veccpu = INT_MAX;
171dc9b124dSJustin Hibbits 	PCPU_SET(vecthread, NULL);
172dc9b124dSJustin Hibbits }
173dc9b124dSJustin Hibbits 
174dc9b124dSJustin Hibbits /*
175dc9b124dSJustin Hibbits  * Save SPE state without dropping ownership.  This will only save state if
1766f83eb8bSJustin Hibbits  * the current vector-thread is `td'.  This is used for taking core dumps, so
1776f83eb8bSJustin Hibbits  * don't leak kernel information; overwrite the low words of each vector with
1786f83eb8bSJustin Hibbits  * their real value, taken from the thread's trap frame, unconditionally.
179dc9b124dSJustin Hibbits  */
180dc9b124dSJustin Hibbits void
save_vec_nodrop(struct thread * td)181dc9b124dSJustin Hibbits save_vec_nodrop(struct thread *td)
182dc9b124dSJustin Hibbits {
1836f83eb8bSJustin Hibbits 	struct pcb *pcb;
1846f83eb8bSJustin Hibbits 	int i;
185dc9b124dSJustin Hibbits 
186*8cf2c8edSJustin Hibbits 	if (td == PCPU_GET(vecthread))
1876f83eb8bSJustin Hibbits 		save_vec_int(td);
188dc9b124dSJustin Hibbits 
1896f83eb8bSJustin Hibbits 	pcb = td->td_pcb;
1906f83eb8bSJustin Hibbits 
1916f83eb8bSJustin Hibbits 	for (i = 0; i < 32; i++) {
1926f83eb8bSJustin Hibbits 		pcb->pcb_vec.vr[i][1] =
1936f83eb8bSJustin Hibbits 		    td->td_frame ? td->td_frame->fixreg[i] : 0;
1946f83eb8bSJustin Hibbits 	}
195dc9b124dSJustin Hibbits }
196289041e2SJustin Hibbits 
197289041e2SJustin Hibbits #define	SPE_INST_MASK	0x31f
198289041e2SJustin Hibbits #define	EADD	0x200
199289041e2SJustin Hibbits #define	ESUB	0x201
200289041e2SJustin Hibbits #define	EABS	0x204
201289041e2SJustin Hibbits #define	ENABS	0x205
202289041e2SJustin Hibbits #define	ENEG	0x206
203289041e2SJustin Hibbits #define	EMUL	0x208
204289041e2SJustin Hibbits #define	EDIV	0x209
205289041e2SJustin Hibbits #define	ECMPGT	0x20c
206289041e2SJustin Hibbits #define	ECMPLT	0x20d
207289041e2SJustin Hibbits #define	ECMPEQ	0x20e
208289041e2SJustin Hibbits #define	ECFUI	0x210
209289041e2SJustin Hibbits #define	ECFSI	0x211
210289041e2SJustin Hibbits #define	ECTUI	0x214
211289041e2SJustin Hibbits #define	ECTSI	0x215
212289041e2SJustin Hibbits #define	ECTUF	0x216
213289041e2SJustin Hibbits #define	ECTSF	0x217
214289041e2SJustin Hibbits #define	ECTUIZ	0x218
215289041e2SJustin Hibbits #define	ECTSIZ	0x21a
216289041e2SJustin Hibbits 
217289041e2SJustin Hibbits #define	SPE		0x4
218289041e2SJustin Hibbits #define	SPFP		0x6
219289041e2SJustin Hibbits #define	DPFP		0x7
220289041e2SJustin Hibbits 
221289041e2SJustin Hibbits #define	SPE_OPC		4
222289041e2SJustin Hibbits #define	OPC_SHIFT	26
223289041e2SJustin Hibbits 
224289041e2SJustin Hibbits #define	EVFSADD		0x280
225289041e2SJustin Hibbits #define	EVFSSUB		0x281
226289041e2SJustin Hibbits #define	EVFSABS		0x284
227289041e2SJustin Hibbits #define	EVFSNABS	0x285
228289041e2SJustin Hibbits #define	EVFSNEG		0x286
229289041e2SJustin Hibbits #define	EVFSMUL		0x288
230289041e2SJustin Hibbits #define	EVFSDIV		0x289
231289041e2SJustin Hibbits #define	EVFSCMPGT	0x28c
232289041e2SJustin Hibbits #define	EVFSCMPLT	0x28d
233289041e2SJustin Hibbits #define	EVFSCMPEQ	0x28e
234289041e2SJustin Hibbits #define	EVFSCFUI	0x290
235289041e2SJustin Hibbits #define	EVFSCFSI	0x291
236289041e2SJustin Hibbits #define	EVFSCTUI	0x294
237289041e2SJustin Hibbits #define	EVFSCTSI	0x295
238289041e2SJustin Hibbits #define	EVFSCTUF	0x296
239289041e2SJustin Hibbits #define	EVFSCTSF	0x297
240289041e2SJustin Hibbits #define	EVFSCTUIZ	0x298
241289041e2SJustin Hibbits #define	EVFSCTSIZ	0x29a
242289041e2SJustin Hibbits 
243289041e2SJustin Hibbits #define	EFSADD		0x2c0
244289041e2SJustin Hibbits #define	EFSSUB		0x2c1
245289041e2SJustin Hibbits #define	EFSABS		0x2c4
246289041e2SJustin Hibbits #define	EFSNABS		0x2c5
247289041e2SJustin Hibbits #define	EFSNEG		0x2c6
248289041e2SJustin Hibbits #define	EFSMUL		0x2c8
249289041e2SJustin Hibbits #define	EFSDIV		0x2c9
250289041e2SJustin Hibbits #define	EFSCMPGT	0x2cc
251289041e2SJustin Hibbits #define	EFSCMPLT	0x2cd
252289041e2SJustin Hibbits #define	EFSCMPEQ	0x2ce
253289041e2SJustin Hibbits #define	EFSCFD		0x2cf
254289041e2SJustin Hibbits #define	EFSCFUI		0x2d0
255289041e2SJustin Hibbits #define	EFSCFSI		0x2d1
256289041e2SJustin Hibbits #define	EFSCTUI		0x2d4
257289041e2SJustin Hibbits #define	EFSCTSI		0x2d5
258289041e2SJustin Hibbits #define	EFSCTUF		0x2d6
259289041e2SJustin Hibbits #define	EFSCTSF		0x2d7
260289041e2SJustin Hibbits #define	EFSCTUIZ	0x2d8
261289041e2SJustin Hibbits #define	EFSCTSIZ	0x2da
262289041e2SJustin Hibbits 
263289041e2SJustin Hibbits #define	EFDADD		0x2e0
264289041e2SJustin Hibbits #define	EFDSUB		0x2e1
265289041e2SJustin Hibbits #define	EFDABS		0x2e4
266289041e2SJustin Hibbits #define	EFDNABS		0x2e5
267289041e2SJustin Hibbits #define	EFDNEG		0x2e6
268289041e2SJustin Hibbits #define	EFDMUL		0x2e8
269289041e2SJustin Hibbits #define	EFDDIV		0x2e9
270289041e2SJustin Hibbits #define	EFDCMPGT	0x2ec
271289041e2SJustin Hibbits #define	EFDCMPLT	0x2ed
272289041e2SJustin Hibbits #define	EFDCMPEQ	0x2ee
273289041e2SJustin Hibbits #define	EFDCFS		0x2ef
274289041e2SJustin Hibbits #define	EFDCFUI		0x2f0
275289041e2SJustin Hibbits #define	EFDCFSI		0x2f1
276289041e2SJustin Hibbits #define	EFDCTUI		0x2f4
277289041e2SJustin Hibbits #define	EFDCTSI		0x2f5
278289041e2SJustin Hibbits #define	EFDCTUF		0x2f6
279289041e2SJustin Hibbits #define	EFDCTSF		0x2f7
280289041e2SJustin Hibbits #define	EFDCTUIZ	0x2f8
281289041e2SJustin Hibbits #define	EFDCTSIZ	0x2fa
282289041e2SJustin Hibbits 
283289041e2SJustin Hibbits enum {
284289041e2SJustin Hibbits 	NONE,
285289041e2SJustin Hibbits 	SINGLE,
286289041e2SJustin Hibbits 	DOUBLE,
287289041e2SJustin Hibbits 	VECTOR,
288289041e2SJustin Hibbits };
289289041e2SJustin Hibbits 
fpscr_to_spefscr(uint32_t fpscr)290289041e2SJustin Hibbits static uint32_t fpscr_to_spefscr(uint32_t fpscr)
291289041e2SJustin Hibbits {
292289041e2SJustin Hibbits 	uint32_t spefscr;
293289041e2SJustin Hibbits 
294289041e2SJustin Hibbits 	spefscr = 0;
295289041e2SJustin Hibbits 
296289041e2SJustin Hibbits 	if (fpscr & FPSCR_VX)
297289041e2SJustin Hibbits 		spefscr |= SPEFSCR_FINV;
298289041e2SJustin Hibbits 	if (fpscr & FPSCR_OX)
299289041e2SJustin Hibbits 		spefscr |= SPEFSCR_FOVF;
300289041e2SJustin Hibbits 	if (fpscr & FPSCR_UX)
301289041e2SJustin Hibbits 		spefscr |= SPEFSCR_FUNF;
302289041e2SJustin Hibbits 	if (fpscr & FPSCR_ZX)
303289041e2SJustin Hibbits 		spefscr |= SPEFSCR_FDBZ;
304289041e2SJustin Hibbits 	if (fpscr & FPSCR_XX)
305289041e2SJustin Hibbits 		spefscr |= SPEFSCR_FX;
306289041e2SJustin Hibbits 
307289041e2SJustin Hibbits 	return (spefscr);
308289041e2SJustin Hibbits }
309289041e2SJustin Hibbits 
310289041e2SJustin Hibbits /* Sign is 0 for unsigned, 1 for signed. */
311289041e2SJustin Hibbits static int
spe_to_int(struct fpemu * fpemu,struct fpn * fpn,uint32_t * val,int sign)312289041e2SJustin Hibbits spe_to_int(struct fpemu *fpemu, struct fpn *fpn, uint32_t *val, int sign)
313289041e2SJustin Hibbits {
314289041e2SJustin Hibbits 	uint32_t res[2];
315289041e2SJustin Hibbits 
316289041e2SJustin Hibbits 	res[0] = fpu_ftox(fpemu, fpn, res);
317289041e2SJustin Hibbits 	if (res[0] != UINT_MAX && res[0] != 0)
318289041e2SJustin Hibbits 		fpemu->fe_cx |= FPSCR_OX;
319289041e2SJustin Hibbits 	else if (sign == 0 && res[0] != 0)
320289041e2SJustin Hibbits 		fpemu->fe_cx |= FPSCR_UX;
321289041e2SJustin Hibbits 	else
322289041e2SJustin Hibbits 		*val = res[1];
323289041e2SJustin Hibbits 
324289041e2SJustin Hibbits 	return (0);
325289041e2SJustin Hibbits }
326289041e2SJustin Hibbits 
327289041e2SJustin Hibbits /* Masked instruction */
328289041e2SJustin Hibbits /*
329289041e2SJustin Hibbits  * For compare instructions, returns 1 if success, 0 if not.  For all others,
330289041e2SJustin Hibbits  * returns -1, or -2 if no result needs recorded.
331289041e2SJustin Hibbits  */
332289041e2SJustin Hibbits static int
spe_emu_instr(uint32_t instr,struct fpemu * fpemu,struct fpn ** result,uint32_t * iresult)333289041e2SJustin Hibbits spe_emu_instr(uint32_t instr, struct fpemu *fpemu,
334289041e2SJustin Hibbits     struct fpn **result, uint32_t *iresult)
335289041e2SJustin Hibbits {
336289041e2SJustin Hibbits 	switch (instr & SPE_INST_MASK) {
337289041e2SJustin Hibbits 	case EABS:
338289041e2SJustin Hibbits 	case ENABS:
339289041e2SJustin Hibbits 	case ENEG:
340289041e2SJustin Hibbits 		/* Taken care of elsewhere. */
341289041e2SJustin Hibbits 		break;
342289041e2SJustin Hibbits 	case ECTUIZ:
343289041e2SJustin Hibbits 		fpemu->fe_cx &= ~FPSCR_RN;
344289041e2SJustin Hibbits 		fpemu->fe_cx |= FP_RZ;
345289041e2SJustin Hibbits 	case ECTUI:
346289041e2SJustin Hibbits 		spe_to_int(fpemu, &fpemu->fe_f2, iresult, 0);
347289041e2SJustin Hibbits 		return (-1);
348289041e2SJustin Hibbits 	case ECTSIZ:
349289041e2SJustin Hibbits 		fpemu->fe_cx &= ~FPSCR_RN;
350289041e2SJustin Hibbits 		fpemu->fe_cx |= FP_RZ;
351289041e2SJustin Hibbits 	case ECTSI:
352289041e2SJustin Hibbits 		spe_to_int(fpemu, &fpemu->fe_f2, iresult, 1);
353289041e2SJustin Hibbits 		return (-1);
354289041e2SJustin Hibbits 	case EADD:
355289041e2SJustin Hibbits 		*result = fpu_add(fpemu);
356289041e2SJustin Hibbits 		break;
357289041e2SJustin Hibbits 	case ESUB:
358289041e2SJustin Hibbits 		*result = fpu_sub(fpemu);
359289041e2SJustin Hibbits 		break;
360289041e2SJustin Hibbits 	case EMUL:
361289041e2SJustin Hibbits 		*result = fpu_mul(fpemu);
362289041e2SJustin Hibbits 		break;
363289041e2SJustin Hibbits 	case EDIV:
364289041e2SJustin Hibbits 		*result = fpu_div(fpemu);
365289041e2SJustin Hibbits 		break;
366289041e2SJustin Hibbits 	case ECMPGT:
367289041e2SJustin Hibbits 		fpu_compare(fpemu, 0);
368289041e2SJustin Hibbits 		if (fpemu->fe_cx & FPSCR_FG)
369289041e2SJustin Hibbits 			return (1);
370289041e2SJustin Hibbits 		return (0);
371289041e2SJustin Hibbits 	case ECMPLT:
372289041e2SJustin Hibbits 		fpu_compare(fpemu, 0);
373289041e2SJustin Hibbits 		if (fpemu->fe_cx & FPSCR_FL)
374289041e2SJustin Hibbits 			return (1);
375289041e2SJustin Hibbits 		return (0);
376289041e2SJustin Hibbits 	case ECMPEQ:
377289041e2SJustin Hibbits 		fpu_compare(fpemu, 0);
378289041e2SJustin Hibbits 		if (fpemu->fe_cx & FPSCR_FE)
379289041e2SJustin Hibbits 			return (1);
380289041e2SJustin Hibbits 		return (0);
381289041e2SJustin Hibbits 	default:
382289041e2SJustin Hibbits 		printf("Unknown instruction %x\n", instr);
383289041e2SJustin Hibbits 	}
384289041e2SJustin Hibbits 
385289041e2SJustin Hibbits 	return (-1);
386289041e2SJustin Hibbits }
387289041e2SJustin Hibbits 
388289041e2SJustin Hibbits static int
spe_explode(struct fpemu * fe,struct fpn * fp,uint32_t type,uint32_t hi,uint32_t lo)389289041e2SJustin Hibbits spe_explode(struct fpemu *fe, struct fpn *fp, uint32_t type,
390289041e2SJustin Hibbits     uint32_t hi, uint32_t lo)
391289041e2SJustin Hibbits {
392289041e2SJustin Hibbits 	uint32_t s;
393289041e2SJustin Hibbits 
394289041e2SJustin Hibbits 	fp->fp_sign = hi >> 31;
395289041e2SJustin Hibbits 	fp->fp_sticky = 0;
396289041e2SJustin Hibbits 	switch (type) {
397289041e2SJustin Hibbits 	case SINGLE:
398289041e2SJustin Hibbits 		s = fpu_stof(fp, hi);
399289041e2SJustin Hibbits 		break;
400289041e2SJustin Hibbits 
401289041e2SJustin Hibbits 	case DOUBLE:
402289041e2SJustin Hibbits 		s = fpu_dtof(fp, hi, lo);
403289041e2SJustin Hibbits 		break;
404289041e2SJustin Hibbits 	}
405289041e2SJustin Hibbits 
406289041e2SJustin Hibbits 	if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) {
407289041e2SJustin Hibbits 		/*
408289041e2SJustin Hibbits 		 * Input is a signalling NaN.  All operations that return
409289041e2SJustin Hibbits 		 * an input NaN operand put it through a ``NaN conversion'',
410289041e2SJustin Hibbits 		 * which basically just means ``turn on the quiet bit''.
411289041e2SJustin Hibbits 		 * We do this here so that all NaNs internally look quiet
412289041e2SJustin Hibbits 		 * (we can tell signalling ones by their class).
413289041e2SJustin Hibbits 		 */
414289041e2SJustin Hibbits 		fp->fp_mant[0] |= FP_QUIETBIT;
415289041e2SJustin Hibbits 		fe->fe_cx = FPSCR_VXSNAN;	/* assert invalid operand */
416289041e2SJustin Hibbits 		s = FPC_SNAN;
417289041e2SJustin Hibbits 	}
418289041e2SJustin Hibbits 	fp->fp_class = s;
419289041e2SJustin Hibbits 
420289041e2SJustin Hibbits 	return (0);
421289041e2SJustin Hibbits }
422289041e2SJustin Hibbits 
4233067a880SJustin Hibbits /*
4243067a880SJustin Hibbits  * Save the high word of a 64-bit GPR for manipulation in the exception handler.
4253067a880SJustin Hibbits  */
4263067a880SJustin Hibbits static uint32_t
spe_save_reg_high(int reg)4273067a880SJustin Hibbits spe_save_reg_high(int reg)
4283067a880SJustin Hibbits {
4293067a880SJustin Hibbits 	uint32_t vec[2];
4302da4e52dSJustin Hibbits #define EVSTDW(n)   case n: __asm __volatile ("evstdw %1,0(%0)" \
431fe627769SJustin Hibbits 		:: "b"(vec), "n"(n) : "memory"); break;
4323067a880SJustin Hibbits 	switch (reg) {
4333067a880SJustin Hibbits 	EVSTDW(0);	EVSTDW(1);	EVSTDW(2);	EVSTDW(3);
4343067a880SJustin Hibbits 	EVSTDW(4);	EVSTDW(5);	EVSTDW(6);	EVSTDW(7);
4353067a880SJustin Hibbits 	EVSTDW(8);	EVSTDW(9);	EVSTDW(10);	EVSTDW(11);
4363067a880SJustin Hibbits 	EVSTDW(12);	EVSTDW(13);	EVSTDW(14);	EVSTDW(15);
4373067a880SJustin Hibbits 	EVSTDW(16);	EVSTDW(17);	EVSTDW(18);	EVSTDW(19);
4383067a880SJustin Hibbits 	EVSTDW(20);	EVSTDW(21);	EVSTDW(22);	EVSTDW(23);
4393067a880SJustin Hibbits 	EVSTDW(24);	EVSTDW(25);	EVSTDW(26);	EVSTDW(27);
4403067a880SJustin Hibbits 	EVSTDW(28);	EVSTDW(29);	EVSTDW(30);	EVSTDW(31);
4413067a880SJustin Hibbits 	}
4423067a880SJustin Hibbits #undef EVSTDW
4433067a880SJustin Hibbits 
4443067a880SJustin Hibbits 	return (vec[0]);
4453067a880SJustin Hibbits }
4463067a880SJustin Hibbits 
4473067a880SJustin Hibbits /*
4483067a880SJustin Hibbits  * Load the given value into the high word of the requested register.
4493067a880SJustin Hibbits  */
4503067a880SJustin Hibbits static void
spe_load_reg_high(int reg,uint32_t val)4513067a880SJustin Hibbits spe_load_reg_high(int reg, uint32_t val)
4523067a880SJustin Hibbits {
4532da4e52dSJustin Hibbits #define	EVLDW(n)   case n: __asm __volatile("evmergelo "#n",%0,"#n \
4543067a880SJustin Hibbits 	    :: "r"(val)); break;
4553067a880SJustin Hibbits 	switch (reg) {
4563067a880SJustin Hibbits 	EVLDW(1);	EVLDW(2);	EVLDW(3);	EVLDW(4);
4573067a880SJustin Hibbits 	EVLDW(5);	EVLDW(6);	EVLDW(7);	EVLDW(8);
4583067a880SJustin Hibbits 	EVLDW(9);	EVLDW(10);	EVLDW(11);	EVLDW(12);
4593067a880SJustin Hibbits 	EVLDW(13);	EVLDW(14);	EVLDW(15);	EVLDW(16);
4603067a880SJustin Hibbits 	EVLDW(17);	EVLDW(18);	EVLDW(19);	EVLDW(20);
4613067a880SJustin Hibbits 	EVLDW(21);	EVLDW(22);	EVLDW(23);	EVLDW(24);
4623067a880SJustin Hibbits 	EVLDW(25);	EVLDW(26);	EVLDW(27);	EVLDW(28);
4633067a880SJustin Hibbits 	EVLDW(29);	EVLDW(30);	EVLDW(31);	EVLDW(0);
4643067a880SJustin Hibbits 	}
4653067a880SJustin Hibbits #undef EVLDW
4663067a880SJustin Hibbits 
4673067a880SJustin Hibbits }
4683067a880SJustin Hibbits 
469289041e2SJustin Hibbits void
spe_handle_fpdata(struct trapframe * frame)470289041e2SJustin Hibbits spe_handle_fpdata(struct trapframe *frame)
471289041e2SJustin Hibbits {
472289041e2SJustin Hibbits 	struct fpemu fpemu;
473289041e2SJustin Hibbits 	struct fpn *result;
474289041e2SJustin Hibbits 	uint32_t instr, instr_sec_op;
475289041e2SJustin Hibbits 	uint32_t cr_shift, ra, rb, rd, src;
4763067a880SJustin Hibbits 	uint32_t high, low, res, tmp; /* For vector operations. */
477289041e2SJustin Hibbits 	uint32_t spefscr = 0;
478289041e2SJustin Hibbits 	uint32_t ftod_res[2];
479289041e2SJustin Hibbits 	int width; /* Single, Double, Vector, Integer */
480289041e2SJustin Hibbits 	int err;
4813067a880SJustin Hibbits 	uint32_t msr;
482289041e2SJustin Hibbits 
483289041e2SJustin Hibbits 	err = fueword32((void *)frame->srr0, &instr);
484289041e2SJustin Hibbits 
485289041e2SJustin Hibbits 	if (err != 0)
486289041e2SJustin Hibbits 		return;
487289041e2SJustin Hibbits 		/* Fault. */;
488289041e2SJustin Hibbits 
489289041e2SJustin Hibbits 	if ((instr >> OPC_SHIFT) != SPE_OPC)
490289041e2SJustin Hibbits 		return;
491289041e2SJustin Hibbits 
4923067a880SJustin Hibbits 	msr = mfmsr();
493289041e2SJustin Hibbits 	/*
494289041e2SJustin Hibbits 	 * 'cr' field is the upper 3 bits of rd.  Magically, since a) rd is 5
495289041e2SJustin Hibbits 	 * bits, b) each 'cr' field is 4 bits, and c) Only the 'GT' bit is
496289041e2SJustin Hibbits 	 * modified for most compare operations, the full value of rd can be
497289041e2SJustin Hibbits 	 * used as a shift value.
498289041e2SJustin Hibbits 	 */
499289041e2SJustin Hibbits 	rd = (instr >> 21) & 0x1f;
500289041e2SJustin Hibbits 	ra = (instr >> 16) & 0x1f;
501289041e2SJustin Hibbits 	rb = (instr >> 11) & 0x1f;
502289041e2SJustin Hibbits 	src = (instr >> 5) & 0x7;
503289041e2SJustin Hibbits 	cr_shift = 28 - (rd & 0x1f);
504289041e2SJustin Hibbits 
505289041e2SJustin Hibbits 	instr_sec_op = (instr & 0x7ff);
506289041e2SJustin Hibbits 
507289041e2SJustin Hibbits 	memset(&fpemu, 0, sizeof(fpemu));
508289041e2SJustin Hibbits 
509289041e2SJustin Hibbits 	width = NONE;
510289041e2SJustin Hibbits 	switch (src) {
511289041e2SJustin Hibbits 	case SPE:
5123067a880SJustin Hibbits 		mtmsr(msr | PSL_VEC);
513289041e2SJustin Hibbits 		switch (instr_sec_op) {
514289041e2SJustin Hibbits 		case EVFSABS:
5153067a880SJustin Hibbits 			high = spe_save_reg_high(ra) & ~(1U << 31);
516289041e2SJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
5173067a880SJustin Hibbits 			spe_load_reg_high(rd, high);
518289041e2SJustin Hibbits 			break;
519289041e2SJustin Hibbits 		case EVFSNABS:
5203067a880SJustin Hibbits 			high = spe_save_reg_high(ra) | (1U << 31);
521289041e2SJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
5223067a880SJustin Hibbits 			spe_load_reg_high(rd, high);
523289041e2SJustin Hibbits 			break;
524289041e2SJustin Hibbits 		case EVFSNEG:
5253067a880SJustin Hibbits 			high = spe_save_reg_high(ra) ^ (1U << 31);
526289041e2SJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
5273067a880SJustin Hibbits 			spe_load_reg_high(rd, high);
528289041e2SJustin Hibbits 			break;
529289041e2SJustin Hibbits 		default:
530289041e2SJustin Hibbits 			/* High word */
531289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
5323067a880SJustin Hibbits 			    spe_save_reg_high(ra), 0);
533289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
5343067a880SJustin Hibbits 			    spe_save_reg_high(rb), 0);
535289041e2SJustin Hibbits 			high = spe_emu_instr(instr_sec_op, &fpemu, &result,
5363067a880SJustin Hibbits 			    &tmp);
5373067a880SJustin Hibbits 
5383067a880SJustin Hibbits 			if (high < 0)
5393067a880SJustin Hibbits 				spe_load_reg_high(rd, tmp);
540289041e2SJustin Hibbits 
541289041e2SJustin Hibbits 			spefscr = fpscr_to_spefscr(fpemu.fe_cx) << 16;
542289041e2SJustin Hibbits 			/* Clear the fpemu to start over on the lower bits. */
543289041e2SJustin Hibbits 			memset(&fpemu, 0, sizeof(fpemu));
544289041e2SJustin Hibbits 
545289041e2SJustin Hibbits 			/* Now low word */
546289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
547289041e2SJustin Hibbits 			    frame->fixreg[ra], 0);
548289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
549289041e2SJustin Hibbits 			    frame->fixreg[rb], 0);
550289041e2SJustin Hibbits 			spefscr |= fpscr_to_spefscr(fpemu.fe_cx);
551289041e2SJustin Hibbits 			low = spe_emu_instr(instr_sec_op, &fpemu, &result,
552289041e2SJustin Hibbits 			    &frame->fixreg[rd]);
553289041e2SJustin Hibbits 			if (instr_sec_op == EVFSCMPEQ ||
554289041e2SJustin Hibbits 			    instr_sec_op == EVFSCMPGT ||
555289041e2SJustin Hibbits 			    instr_sec_op == EVFSCMPLT) {
556289041e2SJustin Hibbits 				res = (high << 3) | (low << 2) |
557289041e2SJustin Hibbits 				    ((high | low) << 1) | (high & low);
558289041e2SJustin Hibbits 				width = NONE;
559289041e2SJustin Hibbits 			} else
560289041e2SJustin Hibbits 				width = VECTOR;
561289041e2SJustin Hibbits 			break;
562289041e2SJustin Hibbits 		}
563289041e2SJustin Hibbits 		goto end;
564289041e2SJustin Hibbits 
565289041e2SJustin Hibbits 	case SPFP:
566289041e2SJustin Hibbits 		switch (instr_sec_op) {
567289041e2SJustin Hibbits 		case EFSABS:
568289041e2SJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
569289041e2SJustin Hibbits 			break;
570289041e2SJustin Hibbits 		case EFSNABS:
571289041e2SJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
572289041e2SJustin Hibbits 			break;
573289041e2SJustin Hibbits 		case EFSNEG:
574289041e2SJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
575289041e2SJustin Hibbits 			break;
576289041e2SJustin Hibbits 		case EFSCFD:
577cafceaebSJustin Hibbits 			mtmsr(msr | PSL_VEC);
578289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f3, DOUBLE,
5793067a880SJustin Hibbits 			    spe_save_reg_high(rb), frame->fixreg[rb]);
580289041e2SJustin Hibbits 			result = &fpemu.fe_f3;
581289041e2SJustin Hibbits 			width = SINGLE;
582289041e2SJustin Hibbits 			break;
583289041e2SJustin Hibbits 		default:
584289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
585289041e2SJustin Hibbits 			    frame->fixreg[ra], 0);
586289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
587289041e2SJustin Hibbits 			    frame->fixreg[rb], 0);
588289041e2SJustin Hibbits 			width = SINGLE;
589289041e2SJustin Hibbits 		}
590289041e2SJustin Hibbits 		break;
591289041e2SJustin Hibbits 	case DPFP:
5923067a880SJustin Hibbits 		mtmsr(msr | PSL_VEC);
593289041e2SJustin Hibbits 		switch (instr_sec_op) {
594289041e2SJustin Hibbits 		case EFDABS:
5953067a880SJustin Hibbits 			high = spe_save_reg_high(ra) & ~(1U << 31);
596ddc6c1faSJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra];
5973067a880SJustin Hibbits 			spe_load_reg_high(rd, high);
598289041e2SJustin Hibbits 			break;
599289041e2SJustin Hibbits 		case EFDNABS:
6003067a880SJustin Hibbits 			high = spe_save_reg_high(ra) | (1U << 31);
601ddc6c1faSJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra];
6023067a880SJustin Hibbits 			spe_load_reg_high(rd, high);
603289041e2SJustin Hibbits 			break;
604289041e2SJustin Hibbits 		case EFDNEG:
6053067a880SJustin Hibbits 			high = spe_save_reg_high(ra) ^ (1U << 31);
606ddc6c1faSJustin Hibbits 			frame->fixreg[rd] = frame->fixreg[ra];
6073067a880SJustin Hibbits 			spe_load_reg_high(rd, high);
608289041e2SJustin Hibbits 			break;
609289041e2SJustin Hibbits 		case EFDCFS:
610289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f3, SINGLE,
611289041e2SJustin Hibbits 			    frame->fixreg[rb], 0);
612289041e2SJustin Hibbits 			result = &fpemu.fe_f3;
613289041e2SJustin Hibbits 			width = DOUBLE;
614289041e2SJustin Hibbits 			break;
615289041e2SJustin Hibbits 		default:
616289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f1, DOUBLE,
6173067a880SJustin Hibbits 			    spe_save_reg_high(ra), frame->fixreg[ra]);
618289041e2SJustin Hibbits 			spe_explode(&fpemu, &fpemu.fe_f2, DOUBLE,
6193067a880SJustin Hibbits 			    spe_save_reg_high(rb), frame->fixreg[rb]);
620289041e2SJustin Hibbits 			width = DOUBLE;
621289041e2SJustin Hibbits 		}
622289041e2SJustin Hibbits 		break;
623289041e2SJustin Hibbits 	}
624289041e2SJustin Hibbits 	switch (instr_sec_op) {
625289041e2SJustin Hibbits 	case EFDCFS:
626289041e2SJustin Hibbits 	case EFSCFD:
627289041e2SJustin Hibbits 		/* Already handled. */
628289041e2SJustin Hibbits 		break;
629289041e2SJustin Hibbits 	default:
630289041e2SJustin Hibbits 		res = spe_emu_instr(instr_sec_op, &fpemu, &result,
631289041e2SJustin Hibbits 		    &frame->fixreg[rd]);
632289041e2SJustin Hibbits 		if (res != -1)
633289041e2SJustin Hibbits 			res <<= 2;
634289041e2SJustin Hibbits 		break;
635289041e2SJustin Hibbits 	}
636289041e2SJustin Hibbits 
637289041e2SJustin Hibbits 	switch (instr_sec_op & SPE_INST_MASK) {
638289041e2SJustin Hibbits 	case ECMPEQ:
639289041e2SJustin Hibbits 	case ECMPGT:
640289041e2SJustin Hibbits 	case ECMPLT:
641289041e2SJustin Hibbits 		frame->cr &= ~(0xf << cr_shift);
642289041e2SJustin Hibbits 		frame->cr |= (res << cr_shift);
643289041e2SJustin Hibbits 		break;
644289041e2SJustin Hibbits 	case ECTUI:
645289041e2SJustin Hibbits 	case ECTUIZ:
646289041e2SJustin Hibbits 	case ECTSI:
647289041e2SJustin Hibbits 	case ECTSIZ:
648289041e2SJustin Hibbits 		break;
649289041e2SJustin Hibbits 	default:
650289041e2SJustin Hibbits 		switch (width) {
651289041e2SJustin Hibbits 		case NONE:
652289041e2SJustin Hibbits 		case VECTOR:
653289041e2SJustin Hibbits 			break;
654289041e2SJustin Hibbits 		case SINGLE:
655289041e2SJustin Hibbits 			frame->fixreg[rd] = fpu_ftos(&fpemu, result);
656289041e2SJustin Hibbits 			break;
657289041e2SJustin Hibbits 		case DOUBLE:
6583067a880SJustin Hibbits 			spe_load_reg_high(rd, fpu_ftod(&fpemu, result, ftod_res));
659289041e2SJustin Hibbits 			frame->fixreg[rd] = ftod_res[1];
660289041e2SJustin Hibbits 			break;
661289041e2SJustin Hibbits 		default:
662289041e2SJustin Hibbits 			panic("Unknown storage width %d", width);
663289041e2SJustin Hibbits 			break;
664289041e2SJustin Hibbits 		}
665289041e2SJustin Hibbits 	}
666289041e2SJustin Hibbits 
667289041e2SJustin Hibbits end:
668289041e2SJustin Hibbits 	spefscr |= (mfspr(SPR_SPEFSCR) & ~SPEFSCR_FINVS);
669289041e2SJustin Hibbits 	mtspr(SPR_SPEFSCR, spefscr);
670289041e2SJustin Hibbits 	frame->srr0 += 4;
6713067a880SJustin Hibbits 	mtmsr(msr);
672289041e2SJustin Hibbits 
673289041e2SJustin Hibbits 	return;
674289041e2SJustin Hibbits }
675289041e2SJustin Hibbits 
676289041e2SJustin Hibbits void
spe_handle_fpround(struct trapframe * frame)677289041e2SJustin Hibbits spe_handle_fpround(struct trapframe *frame)
678289041e2SJustin Hibbits {
679289041e2SJustin Hibbits 
680289041e2SJustin Hibbits 	/*
681289041e2SJustin Hibbits 	 * Punt fpround exceptions for now.  This leaves the truncated result in
682289041e2SJustin Hibbits 	 * the register.  We'll deal with overflow/underflow later.
683289041e2SJustin Hibbits 	 */
684289041e2SJustin Hibbits 	return;
685289041e2SJustin Hibbits }
686