xref: /plan9-contrib/sys/src/9/bcm/vfp3.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier  * VFPv2 or VFPv3 floating point unit
3*5c47fe09SDavid du Colombier  */
4*5c47fe09SDavid du Colombier #include "u.h"
5*5c47fe09SDavid du Colombier #include "../port/lib.h"
6*5c47fe09SDavid du Colombier #include "mem.h"
7*5c47fe09SDavid du Colombier #include "dat.h"
8*5c47fe09SDavid du Colombier #include "fns.h"
9*5c47fe09SDavid du Colombier #include "ureg.h"
10*5c47fe09SDavid du Colombier #include "arm.h"
11*5c47fe09SDavid du Colombier 
12*5c47fe09SDavid du Colombier /* subarchitecture code in m->havefp */
13*5c47fe09SDavid du Colombier enum {
14*5c47fe09SDavid du Colombier 	VFPv2	= 2,
15*5c47fe09SDavid du Colombier 	VFPv3	= 3,
16*5c47fe09SDavid du Colombier };
17*5c47fe09SDavid du Colombier 
18*5c47fe09SDavid du Colombier /* fp control regs.  most are read-only */
19*5c47fe09SDavid du Colombier enum {
20*5c47fe09SDavid du Colombier 	Fpsid =	0,
21*5c47fe09SDavid du Colombier 	Fpscr =	1,			/* rw */
22*5c47fe09SDavid du Colombier 	Mvfr1 =	6,
23*5c47fe09SDavid du Colombier 	Mvfr0 =	7,
24*5c47fe09SDavid du Colombier 	Fpexc =	8,			/* rw */
25*5c47fe09SDavid du Colombier 	Fpinst= 9,			/* optional, for exceptions */
26*5c47fe09SDavid du Colombier 	Fpinst2=10,
27*5c47fe09SDavid du Colombier };
28*5c47fe09SDavid du Colombier enum {
29*5c47fe09SDavid du Colombier 	/* Fpexc bits */
30*5c47fe09SDavid du Colombier 	Fpex =		1u << 31,
31*5c47fe09SDavid du Colombier 	Fpenabled =	1 << 30,
32*5c47fe09SDavid du Colombier 	Fpdex =		1 << 29,	/* defined synch exception */
33*5c47fe09SDavid du Colombier //	Fp2v =		1 << 28,	/* Fpinst2 reg is valid */
34*5c47fe09SDavid du Colombier //	Fpvv =		1 << 27,	/* if Fpdex, vecitr is valid */
35*5c47fe09SDavid du Colombier //	Fptfv = 	1 << 26,	/* trapped fault is valid */
36*5c47fe09SDavid du Colombier //	Fpvecitr =	MASK(3) << 8,
37*5c47fe09SDavid du Colombier 	/* FSR bits appear here */
38*5c47fe09SDavid du Colombier 	Fpmbc =		Fpdex,		/* bits exception handler must clear */
39*5c47fe09SDavid du Colombier 
40*5c47fe09SDavid du Colombier 	/* Fpscr bits; see u.h for more */
41*5c47fe09SDavid du Colombier 	Stride =	MASK(2) << 20,
42*5c47fe09SDavid du Colombier 	Len =		MASK(3) << 16,
43*5c47fe09SDavid du Colombier 	Dn=		1 << 25,
44*5c47fe09SDavid du Colombier 	Fz=		1 << 24,
45*5c47fe09SDavid du Colombier 	/* trap exception enables (not allowed in vfp3) */
46*5c47fe09SDavid du Colombier 	FPIDNRM =	1 << 15,	/* input denormal */
47*5c47fe09SDavid du Colombier 	Alltraps = FPIDNRM | FPINEX | FPUNFL | FPOVFL | FPZDIV | FPINVAL,
48*5c47fe09SDavid du Colombier 	/* pending exceptions */
49*5c47fe09SDavid du Colombier 	FPAIDNRM =	1 << 7,		/* input denormal */
50*5c47fe09SDavid du Colombier 	Allexc = FPAIDNRM | FPAINEX | FPAUNFL | FPAOVFL | FPAZDIV | FPAINVAL,
51*5c47fe09SDavid du Colombier 	/* condition codes */
52*5c47fe09SDavid du Colombier 	Allcc =		MASK(4) << 28,
53*5c47fe09SDavid du Colombier };
54*5c47fe09SDavid du Colombier enum {
55*5c47fe09SDavid du Colombier 	/* CpCPaccess bits */
56*5c47fe09SDavid du Colombier 	Cpaccnosimd =	1u << 31,
57*5c47fe09SDavid du Colombier 	Cpaccd16 =	1 << 30,
58*5c47fe09SDavid du Colombier };
59*5c47fe09SDavid du Colombier 
60*5c47fe09SDavid du Colombier static char *
subarch(int impl,uint sa)61*5c47fe09SDavid du Colombier subarch(int impl, uint sa)
62*5c47fe09SDavid du Colombier {
63*5c47fe09SDavid du Colombier 	static char *armarchs[] = {
64*5c47fe09SDavid du Colombier 		"VFPv1 (unsupported)",
65*5c47fe09SDavid du Colombier 		"VFPv2",
66*5c47fe09SDavid du Colombier 		"VFPv3+ with common VFP subarch v2",
67*5c47fe09SDavid du Colombier 		"VFPv3+ with null subarch",
68*5c47fe09SDavid du Colombier 		"VFPv3+ with common VFP subarch v3",
69*5c47fe09SDavid du Colombier 	};
70*5c47fe09SDavid du Colombier 
71*5c47fe09SDavid du Colombier 	if (impl != 'A' || sa >= nelem(armarchs))
72*5c47fe09SDavid du Colombier 		return "GOK";
73*5c47fe09SDavid du Colombier 	else
74*5c47fe09SDavid du Colombier 		return armarchs[sa];
75*5c47fe09SDavid du Colombier }
76*5c47fe09SDavid du Colombier 
77*5c47fe09SDavid du Colombier static char *
implement(uchar impl)78*5c47fe09SDavid du Colombier implement(uchar impl)
79*5c47fe09SDavid du Colombier {
80*5c47fe09SDavid du Colombier 	if (impl == 'A')
81*5c47fe09SDavid du Colombier 		return "arm";
82*5c47fe09SDavid du Colombier 	else
83*5c47fe09SDavid du Colombier 		return "unknown";
84*5c47fe09SDavid du Colombier }
85*5c47fe09SDavid du Colombier 
86*5c47fe09SDavid du Colombier static int
havefp(void)87*5c47fe09SDavid du Colombier havefp(void)
88*5c47fe09SDavid du Colombier {
89*5c47fe09SDavid du Colombier 	int gotfp;
90*5c47fe09SDavid du Colombier 	ulong acc, sid;
91*5c47fe09SDavid du Colombier 
92*5c47fe09SDavid du Colombier 	if (m->havefpvalid)
93*5c47fe09SDavid du Colombier 		return m->havefp;
94*5c47fe09SDavid du Colombier 
95*5c47fe09SDavid du Colombier 	m->havefp = 0;
96*5c47fe09SDavid du Colombier 	gotfp = 1 << CpFP | 1 << CpDFP;
97*5c47fe09SDavid du Colombier 	cpwrcpaccess(MASK(28));
98*5c47fe09SDavid du Colombier 	acc = cprdcpaccess();
99*5c47fe09SDavid du Colombier 	if ((acc & (MASK(2) << (2*CpFP))) == 0) {
100*5c47fe09SDavid du Colombier 		gotfp &= ~(1 << CpFP);
101*5c47fe09SDavid du Colombier 		print("fpon: no single FP coprocessor\n");
102*5c47fe09SDavid du Colombier 	}
103*5c47fe09SDavid du Colombier 	if ((acc & (MASK(2) << (2*CpDFP))) == 0) {
104*5c47fe09SDavid du Colombier 		gotfp &= ~(1 << CpDFP);
105*5c47fe09SDavid du Colombier 		print("fpon: no double FP coprocessor\n");
106*5c47fe09SDavid du Colombier 	}
107*5c47fe09SDavid du Colombier 	if (!gotfp) {
108*5c47fe09SDavid du Colombier 		print("fpon: no FP coprocessors\n");
109*5c47fe09SDavid du Colombier 		m->havefpvalid = 1;
110*5c47fe09SDavid du Colombier 		return 0;
111*5c47fe09SDavid du Colombier 	}
112*5c47fe09SDavid du Colombier 	m->fpon = 1;			/* don't panic */
113*5c47fe09SDavid du Colombier 	sid = fprdsid();
114*5c47fe09SDavid du Colombier 	m->fpon = 0;
115*5c47fe09SDavid du Colombier 	switch((sid >> 16) & MASK(7)){
116*5c47fe09SDavid du Colombier 	case 0:				/* VFPv1 */
117*5c47fe09SDavid du Colombier 		break;
118*5c47fe09SDavid du Colombier 	case 1:				/* VFPv2 */
119*5c47fe09SDavid du Colombier 		m->havefp = VFPv2;
120*5c47fe09SDavid du Colombier 		m->fpnregs = 16;
121*5c47fe09SDavid du Colombier 		break;
122*5c47fe09SDavid du Colombier 	default:			/* VFPv3 or later */
123*5c47fe09SDavid du Colombier 		m->havefp = VFPv3;
124*5c47fe09SDavid du Colombier 		m->fpnregs = (acc & Cpaccd16) ? 16 : 32;
125*5c47fe09SDavid du Colombier 		break;
126*5c47fe09SDavid du Colombier 	}
127*5c47fe09SDavid du Colombier 	if (m->machno == 0)
128*5c47fe09SDavid du Colombier 		print("fp: %d registers, %s simd\n", m->fpnregs,
129*5c47fe09SDavid du Colombier 			(acc & Cpaccnosimd? " no": ""));
130*5c47fe09SDavid du Colombier 	m->havefpvalid = 1;
131*5c47fe09SDavid du Colombier 	return 1;
132*5c47fe09SDavid du Colombier }
133*5c47fe09SDavid du Colombier 
134*5c47fe09SDavid du Colombier /*
135*5c47fe09SDavid du Colombier  * these can be called to turn the fpu on or off for user procs,
136*5c47fe09SDavid du Colombier  * not just at system start up or shutdown.
137*5c47fe09SDavid du Colombier  */
138*5c47fe09SDavid du Colombier 
139*5c47fe09SDavid du Colombier void
fpoff(void)140*5c47fe09SDavid du Colombier fpoff(void)
141*5c47fe09SDavid du Colombier {
142*5c47fe09SDavid du Colombier 	if (m->fpon) {
143*5c47fe09SDavid du Colombier 		fpwrexc(0);
144*5c47fe09SDavid du Colombier 		m->fpon = 0;
145*5c47fe09SDavid du Colombier 	}
146*5c47fe09SDavid du Colombier }
147*5c47fe09SDavid du Colombier 
148*5c47fe09SDavid du Colombier void
fpononly(void)149*5c47fe09SDavid du Colombier fpononly(void)
150*5c47fe09SDavid du Colombier {
151*5c47fe09SDavid du Colombier 	if (!m->fpon && havefp()) {
152*5c47fe09SDavid du Colombier 		/* enable fp.  must be first operation on the FPUs. */
153*5c47fe09SDavid du Colombier 		fpwrexc(Fpenabled);
154*5c47fe09SDavid du Colombier 		m->fpon = 1;
155*5c47fe09SDavid du Colombier 	}
156*5c47fe09SDavid du Colombier }
157*5c47fe09SDavid du Colombier 
158*5c47fe09SDavid du Colombier static void
fpcfg(void)159*5c47fe09SDavid du Colombier fpcfg(void)
160*5c47fe09SDavid du Colombier {
161*5c47fe09SDavid du Colombier 	int impl;
162*5c47fe09SDavid du Colombier 	ulong sid;
163*5c47fe09SDavid du Colombier 	static int printed;
164*5c47fe09SDavid du Colombier 
165*5c47fe09SDavid du Colombier 	/* clear pending exceptions; no traps in vfp3; all v7 ops are scalar */
166*5c47fe09SDavid du Colombier 	m->fpscr = Dn | FPRNR | (FPINVAL | FPZDIV | FPOVFL) & ~Alltraps;
167*5c47fe09SDavid du Colombier 	/* VFPv2 needs software support for underflows, so force them to zero */
168*5c47fe09SDavid du Colombier 	if(m->havefp == VFPv2)
169*5c47fe09SDavid du Colombier 		m->fpscr |= Fz;
170*5c47fe09SDavid du Colombier 	fpwrscr(m->fpscr);
171*5c47fe09SDavid du Colombier 	m->fpconfiged = 1;
172*5c47fe09SDavid du Colombier 
173*5c47fe09SDavid du Colombier 	if (printed)
174*5c47fe09SDavid du Colombier 		return;
175*5c47fe09SDavid du Colombier 	sid = fprdsid();
176*5c47fe09SDavid du Colombier 	impl = sid >> 24;
177*5c47fe09SDavid du Colombier 	print("fp: %s arch %s; rev %ld\n", implement(impl),
178*5c47fe09SDavid du Colombier 		subarch(impl, (sid >> 16) & MASK(7)), sid & MASK(4));
179*5c47fe09SDavid du Colombier 	printed = 1;
180*5c47fe09SDavid du Colombier }
181*5c47fe09SDavid du Colombier 
182*5c47fe09SDavid du Colombier void
fpinit(void)183*5c47fe09SDavid du Colombier fpinit(void)
184*5c47fe09SDavid du Colombier {
185*5c47fe09SDavid du Colombier 	if (havefp()) {
186*5c47fe09SDavid du Colombier 		fpononly();
187*5c47fe09SDavid du Colombier 		fpcfg();
188*5c47fe09SDavid du Colombier 	}
189*5c47fe09SDavid du Colombier }
190*5c47fe09SDavid du Colombier 
191*5c47fe09SDavid du Colombier void
fpon(void)192*5c47fe09SDavid du Colombier fpon(void)
193*5c47fe09SDavid du Colombier {
194*5c47fe09SDavid du Colombier 	if (havefp()) {
195*5c47fe09SDavid du Colombier 	 	fpononly();
196*5c47fe09SDavid du Colombier 		if (m->fpconfiged)
197*5c47fe09SDavid du Colombier 			fpwrscr((fprdscr() & Allcc) | m->fpscr);
198*5c47fe09SDavid du Colombier 		else
199*5c47fe09SDavid du Colombier 			fpcfg();	/* 1st time on this fpu; configure it */
200*5c47fe09SDavid du Colombier 	}
201*5c47fe09SDavid du Colombier }
202*5c47fe09SDavid du Colombier 
203*5c47fe09SDavid du Colombier void
fpclear(void)204*5c47fe09SDavid du Colombier fpclear(void)
205*5c47fe09SDavid du Colombier {
206*5c47fe09SDavid du Colombier //	ulong scr;
207*5c47fe09SDavid du Colombier 
208*5c47fe09SDavid du Colombier 	fpon();
209*5c47fe09SDavid du Colombier //	scr = fprdscr();
210*5c47fe09SDavid du Colombier //	m->fpscr = scr & ~Allexc;
211*5c47fe09SDavid du Colombier //	fpwrscr(m->fpscr);
212*5c47fe09SDavid du Colombier 
213*5c47fe09SDavid du Colombier 	fpwrexc(fprdexc() & ~Fpmbc);
214*5c47fe09SDavid du Colombier }
215*5c47fe09SDavid du Colombier 
216*5c47fe09SDavid du Colombier 
217*5c47fe09SDavid du Colombier /*
218*5c47fe09SDavid du Colombier  * Called when a note is about to be delivered to a
219*5c47fe09SDavid du Colombier  * user process, usually at the end of a system call.
220*5c47fe09SDavid du Colombier  * Note handlers are not allowed to use the FPU so
221*5c47fe09SDavid du Colombier  * the state is marked (after saving if necessary) and
222*5c47fe09SDavid du Colombier  * checked in the Device Not Available handler.
223*5c47fe09SDavid du Colombier  */
224*5c47fe09SDavid du Colombier void
fpunotify(Ureg *)225*5c47fe09SDavid du Colombier fpunotify(Ureg*)
226*5c47fe09SDavid du Colombier {
227*5c47fe09SDavid du Colombier 	if(up->fpstate == FPactive){
228*5c47fe09SDavid du Colombier 		fpsave(&up->fpsave);
229*5c47fe09SDavid du Colombier 		up->fpstate = FPinactive;
230*5c47fe09SDavid du Colombier 	}
231*5c47fe09SDavid du Colombier 	up->fpstate |= FPillegal;
232*5c47fe09SDavid du Colombier }
233*5c47fe09SDavid du Colombier 
234*5c47fe09SDavid du Colombier /*
235*5c47fe09SDavid du Colombier  * Called from sysnoted() via the machine-dependent
236*5c47fe09SDavid du Colombier  * noted() routine.
237*5c47fe09SDavid du Colombier  * Clear the flag set above in fpunotify().
238*5c47fe09SDavid du Colombier  */
239*5c47fe09SDavid du Colombier void
fpunoted(void)240*5c47fe09SDavid du Colombier fpunoted(void)
241*5c47fe09SDavid du Colombier {
242*5c47fe09SDavid du Colombier 	up->fpstate &= ~FPillegal;
243*5c47fe09SDavid du Colombier }
244*5c47fe09SDavid du Colombier 
245*5c47fe09SDavid du Colombier /*
246*5c47fe09SDavid du Colombier  * Called early in the non-interruptible path of
247*5c47fe09SDavid du Colombier  * sysrfork() via the machine-dependent syscall() routine.
248*5c47fe09SDavid du Colombier  * Save the state so that it can be easily copied
249*5c47fe09SDavid du Colombier  * to the child process later.
250*5c47fe09SDavid du Colombier  */
251*5c47fe09SDavid du Colombier void
fpusysrfork(Ureg *)252*5c47fe09SDavid du Colombier fpusysrfork(Ureg*)
253*5c47fe09SDavid du Colombier {
254*5c47fe09SDavid du Colombier 	if(up->fpstate == FPactive){
255*5c47fe09SDavid du Colombier 		fpsave(&up->fpsave);
256*5c47fe09SDavid du Colombier 		up->fpstate = FPinactive;
257*5c47fe09SDavid du Colombier 	}
258*5c47fe09SDavid du Colombier }
259*5c47fe09SDavid du Colombier 
260*5c47fe09SDavid du Colombier /*
261*5c47fe09SDavid du Colombier  * Called later in sysrfork() via the machine-dependent
262*5c47fe09SDavid du Colombier  * sysrforkchild() routine.
263*5c47fe09SDavid du Colombier  * Copy the parent FPU state to the child.
264*5c47fe09SDavid du Colombier  */
265*5c47fe09SDavid du Colombier void
fpusysrforkchild(Proc * p,Ureg *,Proc * up)266*5c47fe09SDavid du Colombier fpusysrforkchild(Proc *p, Ureg *, Proc *up)
267*5c47fe09SDavid du Colombier {
268*5c47fe09SDavid du Colombier 	/* don't penalize the child, it hasn't done FP in a note handler. */
269*5c47fe09SDavid du Colombier 	p->fpstate = up->fpstate & ~FPillegal;
270*5c47fe09SDavid du Colombier }
271*5c47fe09SDavid du Colombier 
272*5c47fe09SDavid du Colombier /* should only be called if p->fpstate == FPactive */
273*5c47fe09SDavid du Colombier void
fpsave(FPsave * fps)274*5c47fe09SDavid du Colombier fpsave(FPsave *fps)
275*5c47fe09SDavid du Colombier {
276*5c47fe09SDavid du Colombier 	fpon();
277*5c47fe09SDavid du Colombier 	fps->control = fps->status = fprdscr();
278*5c47fe09SDavid du Colombier 	assert(m->fpnregs);
279*5c47fe09SDavid du Colombier 	fpsaveregs((uvlong*)fps->regs, m->fpnregs);
280*5c47fe09SDavid du Colombier 	fpoff();
281*5c47fe09SDavid du Colombier }
282*5c47fe09SDavid du Colombier 
283*5c47fe09SDavid du Colombier static void
fprestore(Proc * p)284*5c47fe09SDavid du Colombier fprestore(Proc *p)
285*5c47fe09SDavid du Colombier {
286*5c47fe09SDavid du Colombier 	fpon();
287*5c47fe09SDavid du Colombier 	fpwrscr(p->fpsave.control);
288*5c47fe09SDavid du Colombier 	m->fpscr = fprdscr() & ~Allcc;
289*5c47fe09SDavid du Colombier 	assert(m->fpnregs);
290*5c47fe09SDavid du Colombier 	fprestregs((uvlong*)p->fpsave.regs, m->fpnregs);
291*5c47fe09SDavid du Colombier }
292*5c47fe09SDavid du Colombier 
293*5c47fe09SDavid du Colombier /*
294*5c47fe09SDavid du Colombier  * Called from sched() and sleep() via the machine-dependent
295*5c47fe09SDavid du Colombier  * procsave() routine.
296*5c47fe09SDavid du Colombier  * About to go in to the scheduler.
297*5c47fe09SDavid du Colombier  * If the process wasn't using the FPU
298*5c47fe09SDavid du Colombier  * there's nothing to do.
299*5c47fe09SDavid du Colombier  */
300*5c47fe09SDavid du Colombier void
fpuprocsave(Proc * p)301*5c47fe09SDavid du Colombier fpuprocsave(Proc *p)
302*5c47fe09SDavid du Colombier {
303*5c47fe09SDavid du Colombier 	if(p->fpstate == FPactive){
304*5c47fe09SDavid du Colombier 		if(p->state == Moribund)
305*5c47fe09SDavid du Colombier 			fpoff();
306*5c47fe09SDavid du Colombier 		else{
307*5c47fe09SDavid du Colombier 			/*
308*5c47fe09SDavid du Colombier 			 * Fpsave() stores without handling pending
309*5c47fe09SDavid du Colombier 			 * unmasked exeptions. Postnote() can't be called
310*5c47fe09SDavid du Colombier 			 * here as sleep() already has up->rlock, so
311*5c47fe09SDavid du Colombier 			 * the handling of pending exceptions is delayed
312*5c47fe09SDavid du Colombier 			 * until the process runs again and generates an
313*5c47fe09SDavid du Colombier 			 * emulation fault to activate the FPU.
314*5c47fe09SDavid du Colombier 			 */
315*5c47fe09SDavid du Colombier 			fpsave(&p->fpsave);
316*5c47fe09SDavid du Colombier 		}
317*5c47fe09SDavid du Colombier 		p->fpstate = FPinactive;
318*5c47fe09SDavid du Colombier 	}
319*5c47fe09SDavid du Colombier }
320*5c47fe09SDavid du Colombier 
321*5c47fe09SDavid du Colombier /*
322*5c47fe09SDavid du Colombier  * The process has been rescheduled and is about to run.
323*5c47fe09SDavid du Colombier  * Nothing to do here right now. If the process tries to use
324*5c47fe09SDavid du Colombier  * the FPU again it will cause a Device Not Available
325*5c47fe09SDavid du Colombier  * exception and the state will then be restored.
326*5c47fe09SDavid du Colombier  */
327*5c47fe09SDavid du Colombier void
fpuprocrestore(Proc *)328*5c47fe09SDavid du Colombier fpuprocrestore(Proc *)
329*5c47fe09SDavid du Colombier {
330*5c47fe09SDavid du Colombier }
331*5c47fe09SDavid du Colombier 
332*5c47fe09SDavid du Colombier /*
333*5c47fe09SDavid du Colombier  * Disable the FPU.
334*5c47fe09SDavid du Colombier  * Called from sysexec() via sysprocsetup() to
335*5c47fe09SDavid du Colombier  * set the FPU for the new process.
336*5c47fe09SDavid du Colombier  */
337*5c47fe09SDavid du Colombier void
fpusysprocsetup(Proc * p)338*5c47fe09SDavid du Colombier fpusysprocsetup(Proc *p)
339*5c47fe09SDavid du Colombier {
340*5c47fe09SDavid du Colombier 	p->fpstate = FPinit;
341*5c47fe09SDavid du Colombier 	fpoff();
342*5c47fe09SDavid du Colombier }
343*5c47fe09SDavid du Colombier 
344*5c47fe09SDavid du Colombier static void
mathnote(void)345*5c47fe09SDavid du Colombier mathnote(void)
346*5c47fe09SDavid du Colombier {
347*5c47fe09SDavid du Colombier 	ulong status;
348*5c47fe09SDavid du Colombier 	char *msg, note[ERRMAX];
349*5c47fe09SDavid du Colombier 
350*5c47fe09SDavid du Colombier 	status = up->fpsave.status;
351*5c47fe09SDavid du Colombier 
352*5c47fe09SDavid du Colombier 	/*
353*5c47fe09SDavid du Colombier 	 * Some attention should probably be paid here to the
354*5c47fe09SDavid du Colombier 	 * exception masks and error summary.
355*5c47fe09SDavid du Colombier 	 */
356*5c47fe09SDavid du Colombier 	if (status & FPAINEX)
357*5c47fe09SDavid du Colombier 		msg = "inexact";
358*5c47fe09SDavid du Colombier 	else if (status & FPAOVFL)
359*5c47fe09SDavid du Colombier 		msg = "overflow";
360*5c47fe09SDavid du Colombier 	else if (status & FPAUNFL)
361*5c47fe09SDavid du Colombier 		msg = "underflow";
362*5c47fe09SDavid du Colombier 	else if (status & FPAZDIV)
363*5c47fe09SDavid du Colombier 		msg = "divide by zero";
364*5c47fe09SDavid du Colombier 	else if (status & FPAINVAL)
365*5c47fe09SDavid du Colombier 		msg = "bad operation";
366*5c47fe09SDavid du Colombier 	else
367*5c47fe09SDavid du Colombier 		msg = "spurious";
368*5c47fe09SDavid du Colombier 	snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=%#lux",
369*5c47fe09SDavid du Colombier 		msg, up->fpsave.pc, status);
370*5c47fe09SDavid du Colombier 	postnote(up, 1, note, NDebug);
371*5c47fe09SDavid du Colombier }
372*5c47fe09SDavid du Colombier 
373*5c47fe09SDavid du Colombier static void
mathemu(Ureg *)374*5c47fe09SDavid du Colombier mathemu(Ureg *)
375*5c47fe09SDavid du Colombier {
376*5c47fe09SDavid du Colombier 	switch(up->fpstate){
377*5c47fe09SDavid du Colombier 	case FPemu:
378*5c47fe09SDavid du Colombier 		error("illegal instruction: VFP opcode in emulated mode");
379*5c47fe09SDavid du Colombier 	case FPinit:
380*5c47fe09SDavid du Colombier 		fpinit();
381*5c47fe09SDavid du Colombier 		up->fpstate = FPactive;
382*5c47fe09SDavid du Colombier 		break;
383*5c47fe09SDavid du Colombier 	case FPinactive:
384*5c47fe09SDavid du Colombier 		/*
385*5c47fe09SDavid du Colombier 		 * Before restoring the state, check for any pending
386*5c47fe09SDavid du Colombier 		 * exceptions.  There's no way to restore the state without
387*5c47fe09SDavid du Colombier 		 * generating an unmasked exception.
388*5c47fe09SDavid du Colombier 		 * More attention should probably be paid here to the
389*5c47fe09SDavid du Colombier 		 * exception masks and error summary.
390*5c47fe09SDavid du Colombier 		 */
391*5c47fe09SDavid du Colombier 		if(up->fpsave.status & (FPAINEX|FPAUNFL|FPAOVFL|FPAZDIV|FPAINVAL)){
392*5c47fe09SDavid du Colombier 			mathnote();
393*5c47fe09SDavid du Colombier 			break;
394*5c47fe09SDavid du Colombier 		}
395*5c47fe09SDavid du Colombier 		fprestore(up);
396*5c47fe09SDavid du Colombier 		up->fpstate = FPactive;
397*5c47fe09SDavid du Colombier 		break;
398*5c47fe09SDavid du Colombier 	case FPactive:
399*5c47fe09SDavid du Colombier 		error("sys: illegal instruction: bad vfp fpu opcode");
400*5c47fe09SDavid du Colombier 		break;
401*5c47fe09SDavid du Colombier 	}
402*5c47fe09SDavid du Colombier 	fpclear();
403*5c47fe09SDavid du Colombier }
404*5c47fe09SDavid du Colombier 
405*5c47fe09SDavid du Colombier void
fpstuck(uintptr pc)406*5c47fe09SDavid du Colombier fpstuck(uintptr pc)
407*5c47fe09SDavid du Colombier {
408*5c47fe09SDavid du Colombier 	if (m->fppc == pc && m->fppid == up->pid) {
409*5c47fe09SDavid du Colombier 		m->fpcnt++;
410*5c47fe09SDavid du Colombier 		if (m->fpcnt > 4)
411*5c47fe09SDavid du Colombier 			panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
412*5c47fe09SDavid du Colombier 				"instr %#8.8lux", m->machno, up->pid, up->text,
413*5c47fe09SDavid du Colombier 				pc, *(ulong *)pc);
414*5c47fe09SDavid du Colombier 	} else {
415*5c47fe09SDavid du Colombier 		m->fppid = up->pid;
416*5c47fe09SDavid du Colombier 		m->fppc = pc;
417*5c47fe09SDavid du Colombier 		m->fpcnt = 0;
418*5c47fe09SDavid du Colombier 	}
419*5c47fe09SDavid du Colombier }
420*5c47fe09SDavid du Colombier 
421*5c47fe09SDavid du Colombier enum {
422*5c47fe09SDavid du Colombier 	N = 1<<31,
423*5c47fe09SDavid du Colombier 	Z = 1<<30,
424*5c47fe09SDavid du Colombier 	C = 1<<29,
425*5c47fe09SDavid du Colombier 	V = 1<<28,
426*5c47fe09SDavid du Colombier 	REGPC = 15,
427*5c47fe09SDavid du Colombier };
428*5c47fe09SDavid du Colombier 
429*5c47fe09SDavid du Colombier static int
condok(int cc,int c)430*5c47fe09SDavid du Colombier condok(int cc, int c)
431*5c47fe09SDavid du Colombier {
432*5c47fe09SDavid du Colombier 	switch(c){
433*5c47fe09SDavid du Colombier 	case 0:	/* Z set */
434*5c47fe09SDavid du Colombier 		return cc&Z;
435*5c47fe09SDavid du Colombier 	case 1:	/* Z clear */
436*5c47fe09SDavid du Colombier 		return (cc&Z) == 0;
437*5c47fe09SDavid du Colombier 	case 2:	/* C set */
438*5c47fe09SDavid du Colombier 		return cc&C;
439*5c47fe09SDavid du Colombier 	case 3:	/* C clear */
440*5c47fe09SDavid du Colombier 		return (cc&C) == 0;
441*5c47fe09SDavid du Colombier 	case 4:	/* N set */
442*5c47fe09SDavid du Colombier 		return cc&N;
443*5c47fe09SDavid du Colombier 	case 5:	/* N clear */
444*5c47fe09SDavid du Colombier 		return (cc&N) == 0;
445*5c47fe09SDavid du Colombier 	case 6:	/* V set */
446*5c47fe09SDavid du Colombier 		return cc&V;
447*5c47fe09SDavid du Colombier 	case 7:	/* V clear */
448*5c47fe09SDavid du Colombier 		return (cc&V) == 0;
449*5c47fe09SDavid du Colombier 	case 8:	/* C set and Z clear */
450*5c47fe09SDavid du Colombier 		return cc&C && (cc&Z) == 0;
451*5c47fe09SDavid du Colombier 	case 9:	/* C clear or Z set */
452*5c47fe09SDavid du Colombier 		return (cc&C) == 0 || cc&Z;
453*5c47fe09SDavid du Colombier 	case 10:	/* N set and V set, or N clear and V clear */
454*5c47fe09SDavid du Colombier 		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
455*5c47fe09SDavid du Colombier 	case 11:	/* N set and V clear, or N clear and V set */
456*5c47fe09SDavid du Colombier 		return (cc&(N|V))==N || (cc&(N|V))==V;
457*5c47fe09SDavid du Colombier 	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
458*5c47fe09SDavid du Colombier 		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
459*5c47fe09SDavid du Colombier 	case 13:	/* Z set, or N set and V clear or N clear and V set */
460*5c47fe09SDavid du Colombier 		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
461*5c47fe09SDavid du Colombier 	case 14:	/* always */
462*5c47fe09SDavid du Colombier 		return 1;
463*5c47fe09SDavid du Colombier 	case 15:	/* never (reserved) */
464*5c47fe09SDavid du Colombier 		return 0;
465*5c47fe09SDavid du Colombier 	}
466*5c47fe09SDavid du Colombier 	return 0;	/* not reached */
467*5c47fe09SDavid du Colombier }
468*5c47fe09SDavid du Colombier 
469*5c47fe09SDavid du Colombier /* only called to deal with user-mode instruction faults */
470*5c47fe09SDavid du Colombier int
fpuemu(Ureg * ureg)471*5c47fe09SDavid du Colombier fpuemu(Ureg* ureg)
472*5c47fe09SDavid du Colombier {
473*5c47fe09SDavid du Colombier 	int s, nfp, cop, op;
474*5c47fe09SDavid du Colombier 	uintptr pc;
475*5c47fe09SDavid du Colombier 	static int already;
476*5c47fe09SDavid du Colombier 
477*5c47fe09SDavid du Colombier 	if(waserror()){
478*5c47fe09SDavid du Colombier 		postnote(up, 1, up->errstr, NDebug);
479*5c47fe09SDavid du Colombier 		return 1;
480*5c47fe09SDavid du Colombier 	}
481*5c47fe09SDavid du Colombier 
482*5c47fe09SDavid du Colombier 	if(up->fpstate & FPillegal)
483*5c47fe09SDavid du Colombier 		error("floating point in note handler");
484*5c47fe09SDavid du Colombier 
485*5c47fe09SDavid du Colombier 	nfp = 0;
486*5c47fe09SDavid du Colombier 	pc = ureg->pc;
487*5c47fe09SDavid du Colombier 	validaddr(pc, 4, 0);
488*5c47fe09SDavid du Colombier 	op  = (*(ulong *)pc >> 24) & MASK(4);
489*5c47fe09SDavid du Colombier 	cop = (*(ulong *)pc >>  8) & MASK(4);
490*5c47fe09SDavid du Colombier 	if(m->fpon)
491*5c47fe09SDavid du Colombier 		fpstuck(pc);		/* debugging; could move down 1 line */
492*5c47fe09SDavid du Colombier 	if (ISFPAOP(cop, op)) {		/* old arm 7500 fpa opcode? */
493*5c47fe09SDavid du Colombier 		s = spllo();
494*5c47fe09SDavid du Colombier 		if(!already++)
495*5c47fe09SDavid du Colombier 			pprint("warning: emulated arm7500 fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
496*5c47fe09SDavid du Colombier 		if(waserror()){
497*5c47fe09SDavid du Colombier 			splx(s);
498*5c47fe09SDavid du Colombier 			nexterror();
499*5c47fe09SDavid du Colombier 		}
500*5c47fe09SDavid du Colombier 		nfp = fpiarm(ureg);	/* advances pc past emulated instr(s) */
501*5c47fe09SDavid du Colombier 		if (nfp > 1)		/* could adjust this threshold */
502*5c47fe09SDavid du Colombier 			m->fppc = m->fpcnt = 0;
503*5c47fe09SDavid du Colombier 		splx(s);
504*5c47fe09SDavid du Colombier 		poperror();
505*5c47fe09SDavid du Colombier 	} else if (ISVFPOP(cop, op)) {	/* if vfp, fpu off or unsupported instruction */
506*5c47fe09SDavid du Colombier 		mathemu(ureg);		/* enable fpu & retry */
507*5c47fe09SDavid du Colombier 		nfp = 1;
508*5c47fe09SDavid du Colombier 	}
509*5c47fe09SDavid du Colombier 
510*5c47fe09SDavid du Colombier 	poperror();
511*5c47fe09SDavid du Colombier 	return nfp;
512*5c47fe09SDavid du Colombier }
513