xref: /plan9/sys/src/9/teg2/vfp3.c (revision 6397270f2f1353bc245f3005811da89a06033226)
13de6a9c0SDavid du Colombier /*
2*9b7bf7dfSDavid du Colombier  * VFPv2 or VFPv3 floating point unit
33de6a9c0SDavid du Colombier  */
43de6a9c0SDavid du Colombier #include "u.h"
53de6a9c0SDavid du Colombier #include "../port/lib.h"
63de6a9c0SDavid du Colombier #include "mem.h"
73de6a9c0SDavid du Colombier #include "dat.h"
83de6a9c0SDavid du Colombier #include "fns.h"
93de6a9c0SDavid du Colombier #include "ureg.h"
103de6a9c0SDavid du Colombier #include "arm.h"
113de6a9c0SDavid du Colombier 
12*9b7bf7dfSDavid du Colombier /* subarchitecture code in m->havefp */
13*9b7bf7dfSDavid du Colombier enum {
14*9b7bf7dfSDavid du Colombier 	VFPv2	= 2,
15*9b7bf7dfSDavid du Colombier 	VFPv3	= 3,
16*9b7bf7dfSDavid du Colombier };
17*9b7bf7dfSDavid du Colombier 
183de6a9c0SDavid du Colombier /* fp control regs.  most are read-only */
193de6a9c0SDavid du Colombier enum {
203de6a9c0SDavid du Colombier 	Fpsid =	0,
213de6a9c0SDavid du Colombier 	Fpscr =	1,			/* rw */
223de6a9c0SDavid du Colombier 	Mvfr1 =	6,
233de6a9c0SDavid du Colombier 	Mvfr0 =	7,
243de6a9c0SDavid du Colombier 	Fpexc =	8,			/* rw */
253de6a9c0SDavid du Colombier 	Fpinst= 9,			/* optional, for exceptions */
263de6a9c0SDavid du Colombier 	Fpinst2=10,
273de6a9c0SDavid du Colombier };
283de6a9c0SDavid du Colombier enum {
293de6a9c0SDavid du Colombier 	/* Fpexc bits */
303de6a9c0SDavid du Colombier 	Fpex =		1u << 31,
313de6a9c0SDavid du Colombier 	Fpenabled =	1 << 30,
323de6a9c0SDavid du Colombier 	Fpdex =		1 << 29,	/* defined synch exception */
333de6a9c0SDavid du Colombier //	Fp2v =		1 << 28,	/* Fpinst2 reg is valid */
343de6a9c0SDavid du Colombier //	Fpvv =		1 << 27,	/* if Fpdex, vecitr is valid */
353de6a9c0SDavid du Colombier //	Fptfv = 	1 << 26,	/* trapped fault is valid */
363de6a9c0SDavid du Colombier //	Fpvecitr =	MASK(3) << 8,
373de6a9c0SDavid du Colombier 	/* FSR bits appear here */
383de6a9c0SDavid du Colombier 	Fpmbc =		Fpdex,		/* bits exception handler must clear */
393de6a9c0SDavid du Colombier 
403de6a9c0SDavid du Colombier 	/* Fpscr bits; see u.h for more */
413de6a9c0SDavid du Colombier 	Stride =	MASK(2) << 20,
423de6a9c0SDavid du Colombier 	Len =		MASK(3) << 16,
43*9b7bf7dfSDavid du Colombier 	Dn=		1 << 25,
44*9b7bf7dfSDavid du Colombier 	Fz=		1 << 24,
453de6a9c0SDavid du Colombier 	/* trap exception enables (not allowed in vfp3) */
463de6a9c0SDavid du Colombier 	FPIDNRM =	1 << 15,	/* input denormal */
473de6a9c0SDavid du Colombier 	Alltraps = FPIDNRM | FPINEX | FPUNFL | FPOVFL | FPZDIV | FPINVAL,
483de6a9c0SDavid du Colombier 	/* pending exceptions */
493de6a9c0SDavid du Colombier 	FPAIDNRM =	1 << 7,		/* input denormal */
50*9b7bf7dfSDavid du Colombier 	Allexc = FPAIDNRM | FPAINEX | FPAUNFL | FPAOVFL | FPAZDIV | FPAINVAL,
51*9b7bf7dfSDavid du Colombier 	/* condition codes */
52*9b7bf7dfSDavid du Colombier 	Allcc =		MASK(4) << 28,
533de6a9c0SDavid du Colombier };
543de6a9c0SDavid du Colombier enum {
553de6a9c0SDavid du Colombier 	/* CpCPaccess bits */
563de6a9c0SDavid du Colombier 	Cpaccnosimd =	1u << 31,
573de6a9c0SDavid du Colombier 	Cpaccd16 =	1 << 30,
583de6a9c0SDavid du Colombier };
593de6a9c0SDavid du Colombier 
603de6a9c0SDavid du Colombier static char *
subarch(int impl,uint sa)613de6a9c0SDavid du Colombier subarch(int impl, uint sa)
623de6a9c0SDavid du Colombier {
633de6a9c0SDavid du Colombier 	static char *armarchs[] = {
64*9b7bf7dfSDavid du Colombier 		"VFPv1 (unsupported)",
65*9b7bf7dfSDavid du Colombier 		"VFPv2",
663de6a9c0SDavid du Colombier 		"VFPv3+ with common VFP subarch v2",
673de6a9c0SDavid du Colombier 		"VFPv3+ with null subarch",
683de6a9c0SDavid du Colombier 		"VFPv3+ with common VFP subarch v3",
693de6a9c0SDavid du Colombier 	};
703de6a9c0SDavid du Colombier 
713de6a9c0SDavid du Colombier 	if (impl != 'A' || sa >= nelem(armarchs))
723de6a9c0SDavid du Colombier 		return "GOK";
733de6a9c0SDavid du Colombier 	else
743de6a9c0SDavid du Colombier 		return armarchs[sa];
753de6a9c0SDavid du Colombier }
763de6a9c0SDavid du Colombier 
773de6a9c0SDavid du Colombier static char *
implement(uchar impl)783de6a9c0SDavid du Colombier implement(uchar impl)
793de6a9c0SDavid du Colombier {
803de6a9c0SDavid du Colombier 	if (impl == 'A')
813de6a9c0SDavid du Colombier 		return "arm";
823de6a9c0SDavid du Colombier 	else
833de6a9c0SDavid du Colombier 		return "unknown";
843de6a9c0SDavid du Colombier }
853de6a9c0SDavid du Colombier 
863de6a9c0SDavid du Colombier static int
havefp(void)873de6a9c0SDavid du Colombier havefp(void)
883de6a9c0SDavid du Colombier {
893de6a9c0SDavid du Colombier 	int gotfp;
90*9b7bf7dfSDavid du Colombier 	ulong acc, sid;
913de6a9c0SDavid du Colombier 
923de6a9c0SDavid du Colombier 	if (m->havefpvalid)
933de6a9c0SDavid du Colombier 		return m->havefp;
943de6a9c0SDavid du Colombier 
953de6a9c0SDavid du Colombier 	m->havefp = 0;
963de6a9c0SDavid du Colombier 	gotfp = 1 << CpFP | 1 << CpDFP;
973de6a9c0SDavid du Colombier 	cpwrsc(0, CpCONTROL, 0, CpCPaccess, MASK(28));
983de6a9c0SDavid du Colombier 	acc = cprdsc(0, CpCONTROL, 0, CpCPaccess);
993de6a9c0SDavid du Colombier 	if ((acc & (MASK(2) << (2*CpFP))) == 0) {
1003de6a9c0SDavid du Colombier 		gotfp &= ~(1 << CpFP);
1013de6a9c0SDavid du Colombier 		print("fpon: no single FP coprocessor\n");
1023de6a9c0SDavid du Colombier 	}
1033de6a9c0SDavid du Colombier 	if ((acc & (MASK(2) << (2*CpDFP))) == 0) {
1043de6a9c0SDavid du Colombier 		gotfp &= ~(1 << CpDFP);
1053de6a9c0SDavid du Colombier 		print("fpon: no double FP coprocessor\n");
1063de6a9c0SDavid du Colombier 	}
1073de6a9c0SDavid du Colombier 	if (!gotfp) {
1083de6a9c0SDavid du Colombier 		print("fpon: no FP coprocessors\n");
1093de6a9c0SDavid du Colombier 		m->havefpvalid = 1;
1103de6a9c0SDavid du Colombier 		return 0;
1113de6a9c0SDavid du Colombier 	}
112*9b7bf7dfSDavid du Colombier 	m->fpon = 1;			/* don't panic */
113*9b7bf7dfSDavid du Colombier 	sid = fprd(Fpsid);
114*9b7bf7dfSDavid du Colombier 	m->fpon = 0;
115*9b7bf7dfSDavid du Colombier 	switch((sid >> 16) & MASK(7)){
116*9b7bf7dfSDavid du Colombier 	case 0:				/* VFPv1 */
117*9b7bf7dfSDavid du Colombier 		break;
118*9b7bf7dfSDavid du Colombier 	case 1:				/* VFPv2 */
119*9b7bf7dfSDavid du Colombier 		m->havefp = VFPv2;
1203de6a9c0SDavid du Colombier 		m->fpnregs = 16;
121*9b7bf7dfSDavid du Colombier 		break;
122*9b7bf7dfSDavid du Colombier 	default:			/* VFPv3 or later */
123*9b7bf7dfSDavid du Colombier 		m->havefp = VFPv3;
124*9b7bf7dfSDavid du Colombier 		m->fpnregs = (acc & Cpaccd16) ? 16 : 32;
125*9b7bf7dfSDavid du Colombier 		break;
126*9b7bf7dfSDavid du Colombier 	}
1273de6a9c0SDavid du Colombier 	if (m->machno == 0)
1283de6a9c0SDavid du Colombier 		print("fp: %d registers, %s simd\n", m->fpnregs,
1293de6a9c0SDavid du Colombier 			(acc & Cpaccnosimd? " no": ""));
1303de6a9c0SDavid du Colombier 	m->havefpvalid = 1;
1313de6a9c0SDavid du Colombier 	return 1;
1323de6a9c0SDavid du Colombier }
1333de6a9c0SDavid du Colombier 
1343de6a9c0SDavid du Colombier /*
1353de6a9c0SDavid du Colombier  * these can be called to turn the fpu on or off for user procs,
1363de6a9c0SDavid du Colombier  * not just at system start up or shutdown.
1373de6a9c0SDavid du Colombier  */
1383de6a9c0SDavid du Colombier 
1393de6a9c0SDavid du Colombier void
fpoff(void)1403de6a9c0SDavid du Colombier fpoff(void)
1413de6a9c0SDavid du Colombier {
1423de6a9c0SDavid du Colombier 	if (m->fpon) {
1433de6a9c0SDavid du Colombier 		fpwr(Fpexc, 0);
1443de6a9c0SDavid du Colombier 		m->fpon = 0;
1453de6a9c0SDavid du Colombier 	}
1463de6a9c0SDavid du Colombier }
1473de6a9c0SDavid du Colombier 
1483de6a9c0SDavid du Colombier void
fpononly(void)1493de6a9c0SDavid du Colombier fpononly(void)
1503de6a9c0SDavid du Colombier {
1513de6a9c0SDavid du Colombier 	if (!m->fpon && havefp()) {
1523de6a9c0SDavid du Colombier 		/* enable fp.  must be first operation on the FPUs. */
1533de6a9c0SDavid du Colombier 		fpwr(Fpexc, Fpenabled);
1543de6a9c0SDavid du Colombier 		m->fpon = 1;
1553de6a9c0SDavid du Colombier 	}
1563de6a9c0SDavid du Colombier }
1573de6a9c0SDavid du Colombier 
1583de6a9c0SDavid du Colombier static void
fpcfg(void)1593de6a9c0SDavid du Colombier fpcfg(void)
1603de6a9c0SDavid du Colombier {
1613de6a9c0SDavid du Colombier 	int impl;
1623de6a9c0SDavid du Colombier 	ulong sid;
1633de6a9c0SDavid du Colombier 	static int printed;
1643de6a9c0SDavid du Colombier 
1653de6a9c0SDavid du Colombier 	/* clear pending exceptions; no traps in vfp3; all v7 ops are scalar */
166*9b7bf7dfSDavid du Colombier 	m->fpscr = Dn | Fz | FPRNR | (FPINVAL | FPZDIV | FPOVFL) & ~Alltraps;
1673de6a9c0SDavid du Colombier 	fpwr(Fpscr, m->fpscr);
1683de6a9c0SDavid du Colombier 	m->fpconfiged = 1;
1693de6a9c0SDavid du Colombier 
1703de6a9c0SDavid du Colombier 	if (printed)
1713de6a9c0SDavid du Colombier 		return;
1723de6a9c0SDavid du Colombier 	sid = fprd(Fpsid);
1733de6a9c0SDavid du Colombier 	impl = sid >> 24;
174*9b7bf7dfSDavid du Colombier 	print("fp: %s arch %s; rev %ld\n", implement(impl),
1753de6a9c0SDavid du Colombier 		subarch(impl, (sid >> 16) & MASK(7)), sid & MASK(4));
1763de6a9c0SDavid du Colombier 	printed = 1;
1773de6a9c0SDavid du Colombier }
1783de6a9c0SDavid du Colombier 
1793de6a9c0SDavid du Colombier void
fpinit(void)1803de6a9c0SDavid du Colombier fpinit(void)
1813de6a9c0SDavid du Colombier {
1823de6a9c0SDavid du Colombier 	if (havefp()) {
1833de6a9c0SDavid du Colombier 		fpononly();
1843de6a9c0SDavid du Colombier 		fpcfg();
1853de6a9c0SDavid du Colombier 	}
1863de6a9c0SDavid du Colombier }
1873de6a9c0SDavid du Colombier 
1883de6a9c0SDavid du Colombier void
fpon(void)1893de6a9c0SDavid du Colombier fpon(void)
1903de6a9c0SDavid du Colombier {
1913de6a9c0SDavid du Colombier 	if (havefp()) {
1923de6a9c0SDavid du Colombier 	 	fpononly();
1933de6a9c0SDavid du Colombier 		if (m->fpconfiged)
194*9b7bf7dfSDavid du Colombier 			fpwr(Fpscr, (fprd(Fpscr) & Allcc) | m->fpscr);
1953de6a9c0SDavid du Colombier 		else
1963de6a9c0SDavid du Colombier 			fpcfg();	/* 1st time on this fpu; configure it */
1973de6a9c0SDavid du Colombier 	}
1983de6a9c0SDavid du Colombier }
1993de6a9c0SDavid du Colombier 
2003de6a9c0SDavid du Colombier void
fpclear(void)2013de6a9c0SDavid du Colombier fpclear(void)
2023de6a9c0SDavid du Colombier {
2033de6a9c0SDavid du Colombier //	ulong scr;
2043de6a9c0SDavid du Colombier 
2053de6a9c0SDavid du Colombier 	fpon();
2063de6a9c0SDavid du Colombier //	scr = fprd(Fpscr);
2073de6a9c0SDavid du Colombier //	m->fpscr = scr & ~Allexc;
2083de6a9c0SDavid du Colombier //	fpwr(Fpscr, m->fpscr);
2093de6a9c0SDavid du Colombier 
2103de6a9c0SDavid du Colombier 	fpwr(Fpexc, fprd(Fpexc) & ~Fpmbc);
2113de6a9c0SDavid du Colombier }
2123de6a9c0SDavid du Colombier 
2133de6a9c0SDavid du Colombier 
2143de6a9c0SDavid du Colombier /*
2153de6a9c0SDavid du Colombier  * Called when a note is about to be delivered to a
2163de6a9c0SDavid du Colombier  * user process, usually at the end of a system call.
2173de6a9c0SDavid du Colombier  * Note handlers are not allowed to use the FPU so
2183de6a9c0SDavid du Colombier  * the state is marked (after saving if necessary) and
2193de6a9c0SDavid du Colombier  * checked in the Device Not Available handler.
2203de6a9c0SDavid du Colombier  */
2213de6a9c0SDavid du Colombier void
fpunotify(Ureg *)2223de6a9c0SDavid du Colombier fpunotify(Ureg*)
2233de6a9c0SDavid du Colombier {
2243de6a9c0SDavid du Colombier 	if(up->fpstate == FPactive){
2253de6a9c0SDavid du Colombier 		fpsave(&up->fpsave);
2263de6a9c0SDavid du Colombier 		up->fpstate = FPinactive;
2273de6a9c0SDavid du Colombier 	}
2283de6a9c0SDavid du Colombier 	up->fpstate |= FPillegal;
2293de6a9c0SDavid du Colombier }
2303de6a9c0SDavid du Colombier 
2313de6a9c0SDavid du Colombier /*
2323de6a9c0SDavid du Colombier  * Called from sysnoted() via the machine-dependent
2333de6a9c0SDavid du Colombier  * noted() routine.
2343de6a9c0SDavid du Colombier  * Clear the flag set above in fpunotify().
2353de6a9c0SDavid du Colombier  */
2363de6a9c0SDavid du Colombier void
fpunoted(void)2373de6a9c0SDavid du Colombier fpunoted(void)
2383de6a9c0SDavid du Colombier {
2393de6a9c0SDavid du Colombier 	up->fpstate &= ~FPillegal;
2403de6a9c0SDavid du Colombier }
2413de6a9c0SDavid du Colombier 
2423de6a9c0SDavid du Colombier /*
2433de6a9c0SDavid du Colombier  * Called early in the non-interruptible path of
2443de6a9c0SDavid du Colombier  * sysrfork() via the machine-dependent syscall() routine.
2453de6a9c0SDavid du Colombier  * Save the state so that it can be easily copied
2463de6a9c0SDavid du Colombier  * to the child process later.
2473de6a9c0SDavid du Colombier  */
2483de6a9c0SDavid du Colombier void
fpusysrfork(Ureg *)2493de6a9c0SDavid du Colombier fpusysrfork(Ureg*)
2503de6a9c0SDavid du Colombier {
2513de6a9c0SDavid du Colombier 	if(up->fpstate == FPactive){
2523de6a9c0SDavid du Colombier 		fpsave(&up->fpsave);
2533de6a9c0SDavid du Colombier 		up->fpstate = FPinactive;
2543de6a9c0SDavid du Colombier 	}
2553de6a9c0SDavid du Colombier }
2563de6a9c0SDavid du Colombier 
2573de6a9c0SDavid du Colombier /*
2583de6a9c0SDavid du Colombier  * Called later in sysrfork() via the machine-dependent
2593de6a9c0SDavid du Colombier  * sysrforkchild() routine.
2603de6a9c0SDavid du Colombier  * Copy the parent FPU state to the child.
2613de6a9c0SDavid du Colombier  */
2623de6a9c0SDavid du Colombier void
fpusysrforkchild(Proc * p,Ureg *,Proc * up)2633de6a9c0SDavid du Colombier fpusysrforkchild(Proc *p, Ureg *, Proc *up)
2643de6a9c0SDavid du Colombier {
2653de6a9c0SDavid du Colombier 	/* don't penalize the child, it hasn't done FP in a note handler. */
2663de6a9c0SDavid du Colombier 	p->fpstate = up->fpstate & ~FPillegal;
2673de6a9c0SDavid du Colombier }
2683de6a9c0SDavid du Colombier 
2693de6a9c0SDavid du Colombier /* should only be called if p->fpstate == FPactive */
2703de6a9c0SDavid du Colombier void
fpsave(FPsave * fps)2713de6a9c0SDavid du Colombier fpsave(FPsave *fps)
2723de6a9c0SDavid du Colombier {
2733de6a9c0SDavid du Colombier 	int n;
2743de6a9c0SDavid du Colombier 
2753de6a9c0SDavid du Colombier 	fpon();
2763de6a9c0SDavid du Colombier 	fps->control = fps->status = fprd(Fpscr);
2773de6a9c0SDavid du Colombier 	assert(m->fpnregs);
2783de6a9c0SDavid du Colombier 	for (n = 0; n < m->fpnregs; n++)
2793de6a9c0SDavid du Colombier 		fpsavereg(n, (uvlong *)fps->regs[n]);
2803de6a9c0SDavid du Colombier 	fpoff();
2813de6a9c0SDavid du Colombier }
2823de6a9c0SDavid du Colombier 
283*9b7bf7dfSDavid du Colombier static void
fprestore(Proc * p)284*9b7bf7dfSDavid du Colombier fprestore(Proc *p)
285*9b7bf7dfSDavid du Colombier {
286*9b7bf7dfSDavid du Colombier 	int n;
287*9b7bf7dfSDavid du Colombier 
288*9b7bf7dfSDavid du Colombier 	fpon();
289*9b7bf7dfSDavid du Colombier 	fpwr(Fpscr, p->fpsave.control);
290*9b7bf7dfSDavid du Colombier 	m->fpscr = fprd(Fpscr) & ~Allcc;
291*9b7bf7dfSDavid du Colombier 	assert(m->fpnregs);
292*9b7bf7dfSDavid du Colombier 	for (n = 0; n < m->fpnregs; n++)
293*9b7bf7dfSDavid du Colombier 		fprestreg(n, *(uvlong *)p->fpsave.regs[n]);
294*9b7bf7dfSDavid du Colombier }
295*9b7bf7dfSDavid du Colombier 
2963de6a9c0SDavid du Colombier /*
2973de6a9c0SDavid du Colombier  * Called from sched() and sleep() via the machine-dependent
2983de6a9c0SDavid du Colombier  * procsave() routine.
2993de6a9c0SDavid du Colombier  * About to go in to the scheduler.
3003de6a9c0SDavid du Colombier  * If the process wasn't using the FPU
3013de6a9c0SDavid du Colombier  * there's nothing to do.
3023de6a9c0SDavid du Colombier  */
3033de6a9c0SDavid du Colombier void
fpuprocsave(Proc * p)3043de6a9c0SDavid du Colombier fpuprocsave(Proc *p)
3053de6a9c0SDavid du Colombier {
3063de6a9c0SDavid du Colombier 	if(p->fpstate == FPactive){
3073de6a9c0SDavid du Colombier 		if(p->state == Moribund)
3083de6a9c0SDavid du Colombier 			fpclear();
3093de6a9c0SDavid du Colombier 		else{
3103de6a9c0SDavid du Colombier 			/*
3113de6a9c0SDavid du Colombier 			 * Fpsave() stores without handling pending
3123de6a9c0SDavid du Colombier 			 * unmasked exeptions. Postnote() can't be called
3133de6a9c0SDavid du Colombier 			 * here as sleep() already has up->rlock, so
3143de6a9c0SDavid du Colombier 			 * the handling of pending exceptions is delayed
3153de6a9c0SDavid du Colombier 			 * until the process runs again and generates an
3163de6a9c0SDavid du Colombier 			 * emulation fault to activate the FPU.
3173de6a9c0SDavid du Colombier 			 */
3183de6a9c0SDavid du Colombier 			fpsave(&p->fpsave);
3193de6a9c0SDavid du Colombier 		}
3203de6a9c0SDavid du Colombier 		p->fpstate = FPinactive;
3213de6a9c0SDavid du Colombier 	}
3223de6a9c0SDavid du Colombier }
3233de6a9c0SDavid du Colombier 
3243de6a9c0SDavid du Colombier /*
3253de6a9c0SDavid du Colombier  * The process has been rescheduled and is about to run.
3263de6a9c0SDavid du Colombier  * Nothing to do here right now. If the process tries to use
3273de6a9c0SDavid du Colombier  * the FPU again it will cause a Device Not Available
3283de6a9c0SDavid du Colombier  * exception and the state will then be restored.
3293de6a9c0SDavid du Colombier  */
3303de6a9c0SDavid du Colombier void
fpuprocrestore(Proc *)331*9b7bf7dfSDavid du Colombier fpuprocrestore(Proc *)
3323de6a9c0SDavid du Colombier {
3333de6a9c0SDavid du Colombier }
3343de6a9c0SDavid du Colombier 
3353de6a9c0SDavid du Colombier /*
3363de6a9c0SDavid du Colombier  * Disable the FPU.
3373de6a9c0SDavid du Colombier  * Called from sysexec() via sysprocsetup() to
3383de6a9c0SDavid du Colombier  * set the FPU for the new process.
3393de6a9c0SDavid du Colombier  */
3403de6a9c0SDavid du Colombier void
fpusysprocsetup(Proc * p)3413de6a9c0SDavid du Colombier fpusysprocsetup(Proc *p)
3423de6a9c0SDavid du Colombier {
3433de6a9c0SDavid du Colombier 	p->fpstate = FPinit;
3443de6a9c0SDavid du Colombier 	fpoff();
3453de6a9c0SDavid du Colombier }
3463de6a9c0SDavid du Colombier 
3473de6a9c0SDavid du Colombier static void
mathnote(void)3483de6a9c0SDavid du Colombier mathnote(void)
3493de6a9c0SDavid du Colombier {
3503de6a9c0SDavid du Colombier 	ulong status;
3513de6a9c0SDavid du Colombier 	char *msg, note[ERRMAX];
3523de6a9c0SDavid du Colombier 
3533de6a9c0SDavid du Colombier 	status = up->fpsave.status;
3543de6a9c0SDavid du Colombier 
3553de6a9c0SDavid du Colombier 	/*
3563de6a9c0SDavid du Colombier 	 * Some attention should probably be paid here to the
3573de6a9c0SDavid du Colombier 	 * exception masks and error summary.
3583de6a9c0SDavid du Colombier 	 */
3593de6a9c0SDavid du Colombier 	if (status & FPAINEX)
3603de6a9c0SDavid du Colombier 		msg = "inexact";
3613de6a9c0SDavid du Colombier 	else if (status & FPAOVFL)
3623de6a9c0SDavid du Colombier 		msg = "overflow";
3633de6a9c0SDavid du Colombier 	else if (status & FPAUNFL)
3643de6a9c0SDavid du Colombier 		msg = "underflow";
3653de6a9c0SDavid du Colombier 	else if (status & FPAZDIV)
3663de6a9c0SDavid du Colombier 		msg = "divide by zero";
3673de6a9c0SDavid du Colombier 	else if (status & FPAINVAL)
3683de6a9c0SDavid du Colombier 		msg = "bad operation";
3693de6a9c0SDavid du Colombier 	else
3703de6a9c0SDavid du Colombier 		msg = "spurious";
3713de6a9c0SDavid du Colombier 	snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=%#lux",
3723de6a9c0SDavid du Colombier 		msg, up->fpsave.pc, status);
3733de6a9c0SDavid du Colombier 	postnote(up, 1, note, NDebug);
3743de6a9c0SDavid du Colombier }
3753de6a9c0SDavid du Colombier 
3763de6a9c0SDavid du Colombier static void
mathemu(Ureg *)3773de6a9c0SDavid du Colombier mathemu(Ureg *)
3783de6a9c0SDavid du Colombier {
3793de6a9c0SDavid du Colombier 	switch(up->fpstate){
380*9b7bf7dfSDavid du Colombier 	case FPemu:
381*9b7bf7dfSDavid du Colombier 		error("illegal instruction: VFP opcode in emulated mode");
3823de6a9c0SDavid du Colombier 	case FPinit:
3833de6a9c0SDavid du Colombier 		fpinit();
3843de6a9c0SDavid du Colombier 		up->fpstate = FPactive;
3853de6a9c0SDavid du Colombier 		break;
3863de6a9c0SDavid du Colombier 	case FPinactive:
3873de6a9c0SDavid du Colombier 		/*
3883de6a9c0SDavid du Colombier 		 * Before restoring the state, check for any pending
3893de6a9c0SDavid du Colombier 		 * exceptions.  There's no way to restore the state without
3903de6a9c0SDavid du Colombier 		 * generating an unmasked exception.
3913de6a9c0SDavid du Colombier 		 * More attention should probably be paid here to the
3923de6a9c0SDavid du Colombier 		 * exception masks and error summary.
3933de6a9c0SDavid du Colombier 		 */
3943de6a9c0SDavid du Colombier 		if(up->fpsave.status & (FPAINEX|FPAUNFL|FPAOVFL|FPAZDIV|FPAINVAL)){
3953de6a9c0SDavid du Colombier 			mathnote();
3963de6a9c0SDavid du Colombier 			break;
3973de6a9c0SDavid du Colombier 		}
398*9b7bf7dfSDavid du Colombier 		fprestore(up);
3993de6a9c0SDavid du Colombier 		up->fpstate = FPactive;
4003de6a9c0SDavid du Colombier 		break;
4013de6a9c0SDavid du Colombier 	case FPactive:
4023de6a9c0SDavid du Colombier 		error("illegal instruction: bad vfp fpu opcode");
4033de6a9c0SDavid du Colombier 		break;
4043de6a9c0SDavid du Colombier 	}
4053de6a9c0SDavid du Colombier 	fpclear();
4063de6a9c0SDavid du Colombier }
4073de6a9c0SDavid du Colombier 
4083de6a9c0SDavid du Colombier void
fpstuck(uintptr pc)4093de6a9c0SDavid du Colombier fpstuck(uintptr pc)
4103de6a9c0SDavid du Colombier {
4113de6a9c0SDavid du Colombier 	if (m->fppc == pc && m->fppid == up->pid) {
4123de6a9c0SDavid du Colombier 		m->fpcnt++;
4133de6a9c0SDavid du Colombier 		if (m->fpcnt > 4)
4143de6a9c0SDavid du Colombier 			panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
4153de6a9c0SDavid du Colombier 				"instr %#8.8lux", m->machno, up->pid, up->text,
4163de6a9c0SDavid du Colombier 				pc, *(ulong *)pc);
4173de6a9c0SDavid du Colombier 	} else {
4183de6a9c0SDavid du Colombier 		m->fppid = up->pid;
4193de6a9c0SDavid du Colombier 		m->fppc = pc;
4203de6a9c0SDavid du Colombier 		m->fpcnt = 0;
4213de6a9c0SDavid du Colombier 	}
4223de6a9c0SDavid du Colombier }
4233de6a9c0SDavid du Colombier 
4243de6a9c0SDavid du Colombier enum {
4253de6a9c0SDavid du Colombier 	N = 1<<31,
4263de6a9c0SDavid du Colombier 	Z = 1<<30,
4273de6a9c0SDavid du Colombier 	C = 1<<29,
4283de6a9c0SDavid du Colombier 	V = 1<<28,
4293de6a9c0SDavid du Colombier 	REGPC = 15,
4303de6a9c0SDavid du Colombier };
4313de6a9c0SDavid du Colombier 
4323de6a9c0SDavid du Colombier static int
condok(int cc,int c)4333de6a9c0SDavid du Colombier condok(int cc, int c)
4343de6a9c0SDavid du Colombier {
4353de6a9c0SDavid du Colombier 	switch(c){
4363de6a9c0SDavid du Colombier 	case 0:	/* Z set */
4373de6a9c0SDavid du Colombier 		return cc&Z;
4383de6a9c0SDavid du Colombier 	case 1:	/* Z clear */
4393de6a9c0SDavid du Colombier 		return (cc&Z) == 0;
4403de6a9c0SDavid du Colombier 	case 2:	/* C set */
4413de6a9c0SDavid du Colombier 		return cc&C;
4423de6a9c0SDavid du Colombier 	case 3:	/* C clear */
4433de6a9c0SDavid du Colombier 		return (cc&C) == 0;
4443de6a9c0SDavid du Colombier 	case 4:	/* N set */
4453de6a9c0SDavid du Colombier 		return cc&N;
4463de6a9c0SDavid du Colombier 	case 5:	/* N clear */
4473de6a9c0SDavid du Colombier 		return (cc&N) == 0;
4483de6a9c0SDavid du Colombier 	case 6:	/* V set */
4493de6a9c0SDavid du Colombier 		return cc&V;
4503de6a9c0SDavid du Colombier 	case 7:	/* V clear */
4513de6a9c0SDavid du Colombier 		return (cc&V) == 0;
4523de6a9c0SDavid du Colombier 	case 8:	/* C set and Z clear */
4533de6a9c0SDavid du Colombier 		return cc&C && (cc&Z) == 0;
4543de6a9c0SDavid du Colombier 	case 9:	/* C clear or Z set */
4553de6a9c0SDavid du Colombier 		return (cc&C) == 0 || cc&Z;
4563de6a9c0SDavid du Colombier 	case 10:	/* N set and V set, or N clear and V clear */
4573de6a9c0SDavid du Colombier 		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
4583de6a9c0SDavid du Colombier 	case 11:	/* N set and V clear, or N clear and V set */
4593de6a9c0SDavid du Colombier 		return (cc&(N|V))==N || (cc&(N|V))==V;
4603de6a9c0SDavid du Colombier 	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
4613de6a9c0SDavid du Colombier 		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
4623de6a9c0SDavid du Colombier 	case 13:	/* Z set, or N set and V clear or N clear and V set */
4633de6a9c0SDavid du Colombier 		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
4643de6a9c0SDavid du Colombier 	case 14:	/* always */
4653de6a9c0SDavid du Colombier 		return 1;
4663de6a9c0SDavid du Colombier 	case 15:	/* never (reserved) */
4673de6a9c0SDavid du Colombier 		return 0;
4683de6a9c0SDavid du Colombier 	}
4693de6a9c0SDavid du Colombier 	return 0;	/* not reached */
4703de6a9c0SDavid du Colombier }
4713de6a9c0SDavid du Colombier 
4723de6a9c0SDavid du Colombier /* only called to deal with user-mode instruction faults */
4733de6a9c0SDavid du Colombier int
fpuemu(Ureg * ureg)4743de6a9c0SDavid du Colombier fpuemu(Ureg* ureg)
4753de6a9c0SDavid du Colombier {
476*9b7bf7dfSDavid du Colombier 	int s, nfp, cop, op;
4773de6a9c0SDavid du Colombier 	uintptr pc;
4783de6a9c0SDavid du Colombier 
4793de6a9c0SDavid du Colombier 	if(waserror()){
4803de6a9c0SDavid du Colombier 		postnote(up, 1, up->errstr, NDebug);
4813de6a9c0SDavid du Colombier 		return 1;
4823de6a9c0SDavid du Colombier 	}
4833de6a9c0SDavid du Colombier 
4843de6a9c0SDavid du Colombier 	if(up->fpstate & FPillegal)
4853de6a9c0SDavid du Colombier 		error("floating point in note handler");
4863de6a9c0SDavid du Colombier 
4873de6a9c0SDavid du Colombier 	nfp = 0;
4883de6a9c0SDavid du Colombier 	pc = ureg->pc;
4893de6a9c0SDavid du Colombier 	validaddr(pc, 4, 0);
4903de6a9c0SDavid du Colombier 	if(!condok(ureg->psr, *(ulong*)pc >> 28))
4913de6a9c0SDavid du Colombier 		iprint("fpuemu: conditional instr shouldn't have got here\n");
4923de6a9c0SDavid du Colombier 	op  = (*(ulong *)pc >> 24) & MASK(4);
4933de6a9c0SDavid du Colombier 	cop = (*(ulong *)pc >>  8) & MASK(4);
494*9b7bf7dfSDavid du Colombier 	if(m->fpon)
4953de6a9c0SDavid du Colombier 		fpstuck(pc);		/* debugging; could move down 1 line */
4963de6a9c0SDavid du Colombier 	if (ISFPAOP(cop, op)) {		/* old arm 7500 fpa opcode? */
497*9b7bf7dfSDavid du Colombier //		iprint("fpuemu: fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
498*9b7bf7dfSDavid du Colombier //		error("illegal instruction: old arm 7500 fpa opcode");
499*9b7bf7dfSDavid du Colombier 		s = spllo();
500*9b7bf7dfSDavid du Colombier 		if(waserror()){
501*9b7bf7dfSDavid du Colombier 			splx(s);
502*9b7bf7dfSDavid du Colombier 			nexterror();
503*9b7bf7dfSDavid du Colombier 		}
504*9b7bf7dfSDavid du Colombier 		nfp = fpiarm(ureg);	/* advances pc past emulated instr(s) */
505*9b7bf7dfSDavid du Colombier 		if (nfp > 1)		/* could adjust this threshold */
506*9b7bf7dfSDavid du Colombier 			m->fppc = m->fpcnt = 0;
507*9b7bf7dfSDavid du Colombier 		splx(s);
508*9b7bf7dfSDavid du Colombier 		poperror();
5093de6a9c0SDavid du Colombier 	} else if (ISVFPOP(cop, op)) {	/* if vfp, fpu must be off */
5103de6a9c0SDavid du Colombier 		mathemu(ureg);		/* enable fpu & retry */
5113de6a9c0SDavid du Colombier 		nfp = 1;
5123de6a9c0SDavid du Colombier 	}
5133de6a9c0SDavid du Colombier 
5143de6a9c0SDavid du Colombier 	poperror();
5153de6a9c0SDavid du Colombier 	return nfp;
5163de6a9c0SDavid du Colombier }
517