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