1*9ef1f84bSDavid du Colombier /*
2*9ef1f84bSDavid du Colombier * SIMD Floating Point.
3*9ef1f84bSDavid du Colombier * Assembler support to get at the individual instructions
4*9ef1f84bSDavid du Colombier * is in l64fpu.s.
5*9ef1f84bSDavid du Colombier * There are opportunities to be lazier about saving and
6*9ef1f84bSDavid du Colombier * restoring the state and allocating the storage needed.
7*9ef1f84bSDavid du Colombier */
8*9ef1f84bSDavid du Colombier #include "u.h"
9*9ef1f84bSDavid du Colombier #include "../port/lib.h"
10*9ef1f84bSDavid du Colombier #include "mem.h"
11*9ef1f84bSDavid du Colombier #include "dat.h"
12*9ef1f84bSDavid du Colombier #include "fns.h"
13*9ef1f84bSDavid du Colombier
14*9ef1f84bSDavid du Colombier #include "amd64.h"
15*9ef1f84bSDavid du Colombier #include "ureg.h"
16*9ef1f84bSDavid du Colombier
17*9ef1f84bSDavid du Colombier enum { /* FCW, FSW and MXCSR */
18*9ef1f84bSDavid du Colombier I = 0x00000001, /* Invalid-Operation */
19*9ef1f84bSDavid du Colombier D = 0x00000002, /* Denormalized-Operand */
20*9ef1f84bSDavid du Colombier Z = 0x00000004, /* Zero-Divide */
21*9ef1f84bSDavid du Colombier O = 0x00000008, /* Overflow */
22*9ef1f84bSDavid du Colombier U = 0x00000010, /* Underflow */
23*9ef1f84bSDavid du Colombier P = 0x00000020, /* Precision */
24*9ef1f84bSDavid du Colombier };
25*9ef1f84bSDavid du Colombier
26*9ef1f84bSDavid du Colombier enum { /* FCW */
27*9ef1f84bSDavid du Colombier PCs = 0x00000000, /* Precision Control -Single */
28*9ef1f84bSDavid du Colombier PCd = 0x00000200, /* -Double */
29*9ef1f84bSDavid du Colombier PCde = 0x00000300, /* -Double Extended */
30*9ef1f84bSDavid du Colombier RCn = 0x00000000, /* Rounding Control -Nearest */
31*9ef1f84bSDavid du Colombier RCd = 0x00000400, /* -Down */
32*9ef1f84bSDavid du Colombier RCu = 0x00000800, /* -Up */
33*9ef1f84bSDavid du Colombier RCz = 0x00000C00, /* -Toward Zero */
34*9ef1f84bSDavid du Colombier };
35*9ef1f84bSDavid du Colombier
36*9ef1f84bSDavid du Colombier enum { /* FSW */
37*9ef1f84bSDavid du Colombier Sff = 0x00000040, /* Stack Fault Flag */
38*9ef1f84bSDavid du Colombier Es = 0x00000080, /* Error Summary Status */
39*9ef1f84bSDavid du Colombier C0 = 0x00000100, /* ZF - Condition Code Bits */
40*9ef1f84bSDavid du Colombier C1 = 0x00000200, /* O/U# */
41*9ef1f84bSDavid du Colombier C2 = 0x00000400, /* PF */
42*9ef1f84bSDavid du Colombier C3 = 0x00004000, /* ZF */
43*9ef1f84bSDavid du Colombier B = 0x00008000, /* Busy */
44*9ef1f84bSDavid du Colombier };
45*9ef1f84bSDavid du Colombier
46*9ef1f84bSDavid du Colombier enum { /* MXCSR */
47*9ef1f84bSDavid du Colombier Daz = 0x00000040, /* Denormals are Zeros */
48*9ef1f84bSDavid du Colombier Im = 0x00000080, /* I Mask */
49*9ef1f84bSDavid du Colombier Dm = 0x00000100, /* D Mask */
50*9ef1f84bSDavid du Colombier Zm = 0x00000200, /* Z Mask */
51*9ef1f84bSDavid du Colombier Om = 0x00000400, /* O Mask */
52*9ef1f84bSDavid du Colombier Um = 0x00000800, /* U Mask */
53*9ef1f84bSDavid du Colombier Pm = 0x00001000, /* P Mask */
54*9ef1f84bSDavid du Colombier Rn = 0x00000000, /* Round to Nearest */
55*9ef1f84bSDavid du Colombier Rd = 0x00002000, /* Round Down */
56*9ef1f84bSDavid du Colombier Ru = 0x00004000, /* Round Up */
57*9ef1f84bSDavid du Colombier Rz = 0x00006000, /* Round toward Zero */
58*9ef1f84bSDavid du Colombier Fz = 0x00008000, /* Flush to Zero for Um */
59*9ef1f84bSDavid du Colombier };
60*9ef1f84bSDavid du Colombier
61*9ef1f84bSDavid du Colombier enum { /* PFPU.state */
62*9ef1f84bSDavid du Colombier Init = 0, /* The FPU has not been used */
63*9ef1f84bSDavid du Colombier Busy = 1, /* The FPU is being used */
64*9ef1f84bSDavid du Colombier Idle = 2, /* The FPU has been used */
65*9ef1f84bSDavid du Colombier
66*9ef1f84bSDavid du Colombier Hold = 4, /* Handling an FPU note */
67*9ef1f84bSDavid du Colombier };
68*9ef1f84bSDavid du Colombier
69*9ef1f84bSDavid du Colombier extern void _clts(void);
70*9ef1f84bSDavid du Colombier extern void _fldcw(u16int);
71*9ef1f84bSDavid du Colombier extern void _fnclex(void);
72*9ef1f84bSDavid du Colombier extern void _fninit(void);
73*9ef1f84bSDavid du Colombier extern void _fxrstor(Fxsave*);
74*9ef1f84bSDavid du Colombier extern void _fxsave(Fxsave*);
75*9ef1f84bSDavid du Colombier extern void _fwait(void);
76*9ef1f84bSDavid du Colombier extern void _ldmxcsr(u32int);
77*9ef1f84bSDavid du Colombier extern void _stts(void);
78*9ef1f84bSDavid du Colombier
79*9ef1f84bSDavid du Colombier int
fpudevprocio(Proc * proc,void * a,long n,uintptr offset,int write)80*9ef1f84bSDavid du Colombier fpudevprocio(Proc* proc, void* a, long n, uintptr offset, int write)
81*9ef1f84bSDavid du Colombier {
82*9ef1f84bSDavid du Colombier uchar *p;
83*9ef1f84bSDavid du Colombier
84*9ef1f84bSDavid du Colombier /*
85*9ef1f84bSDavid du Colombier * Called from procdevtab.read and procdevtab.write
86*9ef1f84bSDavid du Colombier * allow user process access to the FPU registers.
87*9ef1f84bSDavid du Colombier * This is the only FPU routine which is called directly
88*9ef1f84bSDavid du Colombier * from the port code; it would be nice to have dynamic
89*9ef1f84bSDavid du Colombier * creation of entries in the device file trees...
90*9ef1f84bSDavid du Colombier */
91*9ef1f84bSDavid du Colombier if(offset >= sizeof(Fxsave))
92*9ef1f84bSDavid du Colombier return 0;
93*9ef1f84bSDavid du Colombier if((p = proc->fpusave) == nil)
94*9ef1f84bSDavid du Colombier return 0;
95*9ef1f84bSDavid du Colombier switch(write){
96*9ef1f84bSDavid du Colombier default:
97*9ef1f84bSDavid du Colombier if(offset+n > sizeof(Fxsave))
98*9ef1f84bSDavid du Colombier n = sizeof(Fxsave) - offset;
99*9ef1f84bSDavid du Colombier memmove(p+offset, a, n);
100*9ef1f84bSDavid du Colombier break;
101*9ef1f84bSDavid du Colombier case 0:
102*9ef1f84bSDavid du Colombier if(offset+n > sizeof(Fxsave))
103*9ef1f84bSDavid du Colombier n = sizeof(Fxsave) - offset;
104*9ef1f84bSDavid du Colombier memmove(a, p+offset, n);
105*9ef1f84bSDavid du Colombier break;
106*9ef1f84bSDavid du Colombier }
107*9ef1f84bSDavid du Colombier
108*9ef1f84bSDavid du Colombier return n;
109*9ef1f84bSDavid du Colombier }
110*9ef1f84bSDavid du Colombier
111*9ef1f84bSDavid du Colombier void
fpunotify(Ureg *)112*9ef1f84bSDavid du Colombier fpunotify(Ureg*)
113*9ef1f84bSDavid du Colombier {
114*9ef1f84bSDavid du Colombier /*
115*9ef1f84bSDavid du Colombier * Called when a note is about to be delivered to a
116*9ef1f84bSDavid du Colombier * user process, usually at the end of a system call.
117*9ef1f84bSDavid du Colombier * Note handlers are not allowed to use the FPU so
118*9ef1f84bSDavid du Colombier * the state is marked (after saving if necessary) and
119*9ef1f84bSDavid du Colombier * checked in the Device Not Available handler.
120*9ef1f84bSDavid du Colombier */
121*9ef1f84bSDavid du Colombier if(up->fpustate == Busy){
122*9ef1f84bSDavid du Colombier _fxsave(up->fpusave);
123*9ef1f84bSDavid du Colombier _stts();
124*9ef1f84bSDavid du Colombier up->fpustate = Idle;
125*9ef1f84bSDavid du Colombier }
126*9ef1f84bSDavid du Colombier up->fpustate |= Hold;
127*9ef1f84bSDavid du Colombier }
128*9ef1f84bSDavid du Colombier
129*9ef1f84bSDavid du Colombier void
fpunoted(void)130*9ef1f84bSDavid du Colombier fpunoted(void)
131*9ef1f84bSDavid du Colombier {
132*9ef1f84bSDavid du Colombier /*
133*9ef1f84bSDavid du Colombier * Called from sysnoted() via the machine-dependent
134*9ef1f84bSDavid du Colombier * noted() routine.
135*9ef1f84bSDavid du Colombier * Clear the flag set above in fpunotify().
136*9ef1f84bSDavid du Colombier */
137*9ef1f84bSDavid du Colombier up->fpustate &= ~Hold;
138*9ef1f84bSDavid du Colombier }
139*9ef1f84bSDavid du Colombier
140*9ef1f84bSDavid du Colombier void
fpusysrfork(Ureg *)141*9ef1f84bSDavid du Colombier fpusysrfork(Ureg*)
142*9ef1f84bSDavid du Colombier {
143*9ef1f84bSDavid du Colombier /*
144*9ef1f84bSDavid du Colombier * Called early in the non-interruptible path of
145*9ef1f84bSDavid du Colombier * sysrfork() via the machine-dependent syscall() routine.
146*9ef1f84bSDavid du Colombier * Save the state so that it can be easily copied
147*9ef1f84bSDavid du Colombier * to the child process later.
148*9ef1f84bSDavid du Colombier */
149*9ef1f84bSDavid du Colombier if(up->fpustate != Busy)
150*9ef1f84bSDavid du Colombier return;
151*9ef1f84bSDavid du Colombier
152*9ef1f84bSDavid du Colombier _fxsave(up->fpusave);
153*9ef1f84bSDavid du Colombier _stts();
154*9ef1f84bSDavid du Colombier up->fpustate = Idle;
155*9ef1f84bSDavid du Colombier }
156*9ef1f84bSDavid du Colombier
157*9ef1f84bSDavid du Colombier void
fpusysrforkchild(Proc * child,Proc * parent)158*9ef1f84bSDavid du Colombier fpusysrforkchild(Proc* child, Proc* parent)
159*9ef1f84bSDavid du Colombier {
160*9ef1f84bSDavid du Colombier /*
161*9ef1f84bSDavid du Colombier * Called later in sysrfork() via the machine-dependent
162*9ef1f84bSDavid du Colombier * sysrforkchild() routine.
163*9ef1f84bSDavid du Colombier * Copy the parent FPU state to the child.
164*9ef1f84bSDavid du Colombier */
165*9ef1f84bSDavid du Colombier child->fpustate = parent->fpustate;
166*9ef1f84bSDavid du Colombier child->fpusave = (void*)((PTR2UINT(up->fxsave) + 15) & ~15);
167*9ef1f84bSDavid du Colombier if(child->fpustate == Init)
168*9ef1f84bSDavid du Colombier return;
169*9ef1f84bSDavid du Colombier
170*9ef1f84bSDavid du Colombier memmove(child->fpusave, parent->fpusave, sizeof(Fxsave));
171*9ef1f84bSDavid du Colombier }
172*9ef1f84bSDavid du Colombier
173*9ef1f84bSDavid du Colombier void
fpuprocsave(Proc * p)174*9ef1f84bSDavid du Colombier fpuprocsave(Proc* p)
175*9ef1f84bSDavid du Colombier {
176*9ef1f84bSDavid du Colombier /*
177*9ef1f84bSDavid du Colombier * Called from sched() and sleep() via the machine-dependent
178*9ef1f84bSDavid du Colombier * procsave() routine.
179*9ef1f84bSDavid du Colombier * About to go in to the scheduler.
180*9ef1f84bSDavid du Colombier * If the process wasn't using the FPU
181*9ef1f84bSDavid du Colombier * there's nothing to do.
182*9ef1f84bSDavid du Colombier */
183*9ef1f84bSDavid du Colombier if(p->fpustate != Busy)
184*9ef1f84bSDavid du Colombier return;
185*9ef1f84bSDavid du Colombier
186*9ef1f84bSDavid du Colombier /*
187*9ef1f84bSDavid du Colombier * The process is dead so clear and disable the FPU
188*9ef1f84bSDavid du Colombier * and set the state for whoever gets this proc struct
189*9ef1f84bSDavid du Colombier * next.
190*9ef1f84bSDavid du Colombier */
191*9ef1f84bSDavid du Colombier if(p->state == Moribund){
192*9ef1f84bSDavid du Colombier _clts();
193*9ef1f84bSDavid du Colombier _fnclex();
194*9ef1f84bSDavid du Colombier _stts();
195*9ef1f84bSDavid du Colombier p->fpustate = Init;
196*9ef1f84bSDavid du Colombier return;
197*9ef1f84bSDavid du Colombier }
198*9ef1f84bSDavid du Colombier
199*9ef1f84bSDavid du Colombier /*
200*9ef1f84bSDavid du Colombier * Save the FPU state without handling pending
201*9ef1f84bSDavid du Colombier * unmasked exceptions and disable. Postnote() can't
202*9ef1f84bSDavid du Colombier * be called here as sleep() already has up->rlock,
203*9ef1f84bSDavid du Colombier * so the handling of pending exceptions is delayed
204*9ef1f84bSDavid du Colombier * until the process runs again and generates a
205*9ef1f84bSDavid du Colombier * Device Not Available exception fault to activate
206*9ef1f84bSDavid du Colombier * the FPU.
207*9ef1f84bSDavid du Colombier */
208*9ef1f84bSDavid du Colombier _fxsave(p->fpusave);
209*9ef1f84bSDavid du Colombier _stts();
210*9ef1f84bSDavid du Colombier p->fpustate = Idle;
211*9ef1f84bSDavid du Colombier }
212*9ef1f84bSDavid du Colombier
213*9ef1f84bSDavid du Colombier void
fpuprocrestore(Proc * p)214*9ef1f84bSDavid du Colombier fpuprocrestore(Proc* p)
215*9ef1f84bSDavid du Colombier {
216*9ef1f84bSDavid du Colombier /*
217*9ef1f84bSDavid du Colombier * The process has been rescheduled and is about to run.
218*9ef1f84bSDavid du Colombier * Nothing to do here right now. If the process tries to use
219*9ef1f84bSDavid du Colombier * the FPU again it will cause a Device Not Available
220*9ef1f84bSDavid du Colombier * exception and the state will then be restored.
221*9ef1f84bSDavid du Colombier */
222*9ef1f84bSDavid du Colombier USED(p);
223*9ef1f84bSDavid du Colombier }
224*9ef1f84bSDavid du Colombier
225*9ef1f84bSDavid du Colombier void
fpusysprocsetup(Proc * p)226*9ef1f84bSDavid du Colombier fpusysprocsetup(Proc* p)
227*9ef1f84bSDavid du Colombier {
228*9ef1f84bSDavid du Colombier /*
229*9ef1f84bSDavid du Colombier * Disable the FPU.
230*9ef1f84bSDavid du Colombier * Called from sysexec() via sysprocsetup() to
231*9ef1f84bSDavid du Colombier * set the FPU for the new process.
232*9ef1f84bSDavid du Colombier */
233*9ef1f84bSDavid du Colombier if(p->fpustate != Init){
234*9ef1f84bSDavid du Colombier _clts();
235*9ef1f84bSDavid du Colombier _fnclex();
236*9ef1f84bSDavid du Colombier _stts();
237*9ef1f84bSDavid du Colombier p->fpustate = Init;
238*9ef1f84bSDavid du Colombier }
239*9ef1f84bSDavid du Colombier }
240*9ef1f84bSDavid du Colombier
241*9ef1f84bSDavid du Colombier static void
fpupostnote(void)242*9ef1f84bSDavid du Colombier fpupostnote(void)
243*9ef1f84bSDavid du Colombier {
244*9ef1f84bSDavid du Colombier ushort fsw;
245*9ef1f84bSDavid du Colombier Fxsave *fpusave;
246*9ef1f84bSDavid du Colombier char *m, n[ERRMAX];
247*9ef1f84bSDavid du Colombier
248*9ef1f84bSDavid du Colombier /*
249*9ef1f84bSDavid du Colombier * The Sff bit is sticky, meaning it should be explicitly
250*9ef1f84bSDavid du Colombier * cleared or there's no way to tell if the exception was an
251*9ef1f84bSDavid du Colombier * invalid operation or a stack fault.
252*9ef1f84bSDavid du Colombier */
253*9ef1f84bSDavid du Colombier fpusave = up->fpusave;
254*9ef1f84bSDavid du Colombier fsw = (fpusave->fsw & ~fpusave->fcw) & (Sff|P|U|O|Z|D|I);
255*9ef1f84bSDavid du Colombier if(fsw & I){
256*9ef1f84bSDavid du Colombier if(fsw & Sff){
257*9ef1f84bSDavid du Colombier if(fsw & C1)
258*9ef1f84bSDavid du Colombier m = "Stack Overflow";
259*9ef1f84bSDavid du Colombier else
260*9ef1f84bSDavid du Colombier m = "Stack Underflow";
261*9ef1f84bSDavid du Colombier }
262*9ef1f84bSDavid du Colombier else
263*9ef1f84bSDavid du Colombier m = "Invalid Operation";
264*9ef1f84bSDavid du Colombier }
265*9ef1f84bSDavid du Colombier else if(fsw & D)
266*9ef1f84bSDavid du Colombier m = "Denormal Operand";
267*9ef1f84bSDavid du Colombier else if(fsw & Z)
268*9ef1f84bSDavid du Colombier m = "Divide-By-Zero";
269*9ef1f84bSDavid du Colombier else if(fsw & O)
270*9ef1f84bSDavid du Colombier m = "Numeric Overflow";
271*9ef1f84bSDavid du Colombier else if(fsw & U)
272*9ef1f84bSDavid du Colombier m = "Numeric Underflow";
273*9ef1f84bSDavid du Colombier else if(fsw & P)
274*9ef1f84bSDavid du Colombier m = "Precision";
275*9ef1f84bSDavid du Colombier else
276*9ef1f84bSDavid du Colombier m = "Unknown";
277*9ef1f84bSDavid du Colombier
278*9ef1f84bSDavid du Colombier snprint(n, sizeof(n), "sys: fp: %s Exception ipo=%#llux fsw=%#ux",
279*9ef1f84bSDavid du Colombier m, fpusave->rip, fsw);
280*9ef1f84bSDavid du Colombier postnote(up, 1, n, NDebug);
281*9ef1f84bSDavid du Colombier }
282*9ef1f84bSDavid du Colombier
283*9ef1f84bSDavid du Colombier static void
fpuxf(Ureg * ureg,void *)284*9ef1f84bSDavid du Colombier fpuxf(Ureg* ureg, void*)
285*9ef1f84bSDavid du Colombier {
286*9ef1f84bSDavid du Colombier u32int mxcsr;
287*9ef1f84bSDavid du Colombier Fxsave *fpusave;
288*9ef1f84bSDavid du Colombier char *m, n[ERRMAX];
289*9ef1f84bSDavid du Colombier
290*9ef1f84bSDavid du Colombier /*
291*9ef1f84bSDavid du Colombier * #XF - SIMD Floating Point Exception (Vector 18).
292*9ef1f84bSDavid du Colombier */
293*9ef1f84bSDavid du Colombier
294*9ef1f84bSDavid du Colombier /*
295*9ef1f84bSDavid du Colombier * Save FPU state to check out the error.
296*9ef1f84bSDavid du Colombier */
297*9ef1f84bSDavid du Colombier fpusave = up->fpusave;
298*9ef1f84bSDavid du Colombier _fxsave(fpusave);
299*9ef1f84bSDavid du Colombier _stts();
300*9ef1f84bSDavid du Colombier up->fpustate = Idle;
301*9ef1f84bSDavid du Colombier
302*9ef1f84bSDavid du Colombier if(ureg->ip & KZERO)
303*9ef1f84bSDavid du Colombier panic("#MF: ip=%#p", ureg->ip);
304*9ef1f84bSDavid du Colombier
305*9ef1f84bSDavid du Colombier /*
306*9ef1f84bSDavid du Colombier * Notify the user process.
307*9ef1f84bSDavid du Colombier * The path here is similar to the x87 path described
308*9ef1f84bSDavid du Colombier * in fpupostnote above but without the fpupostnote()
309*9ef1f84bSDavid du Colombier * call.
310*9ef1f84bSDavid du Colombier */
311*9ef1f84bSDavid du Colombier mxcsr = fpusave->mxcsr;
312*9ef1f84bSDavid du Colombier if((mxcsr & (Im|I)) == I)
313*9ef1f84bSDavid du Colombier m = "Invalid Operation";
314*9ef1f84bSDavid du Colombier else if((mxcsr & (Dm|D)) == D)
315*9ef1f84bSDavid du Colombier m = "Denormal Operand";
316*9ef1f84bSDavid du Colombier else if((mxcsr & (Zm|Z)) == Z)
317*9ef1f84bSDavid du Colombier m = "Divide-By-Zero";
318*9ef1f84bSDavid du Colombier else if((mxcsr & (Om|O)) == O)
319*9ef1f84bSDavid du Colombier m = "Numeric Overflow";
320*9ef1f84bSDavid du Colombier else if((mxcsr & (Um|U)) == U)
321*9ef1f84bSDavid du Colombier m = "Numeric Underflow";
322*9ef1f84bSDavid du Colombier else if((mxcsr & (Pm|P)) == P)
323*9ef1f84bSDavid du Colombier m = "Precision";
324*9ef1f84bSDavid du Colombier else
325*9ef1f84bSDavid du Colombier m = "Unknown";
326*9ef1f84bSDavid du Colombier
327*9ef1f84bSDavid du Colombier snprint(n, sizeof(n), "sys: fp: %s Exception mxcsr=%#ux", m, mxcsr);
328*9ef1f84bSDavid du Colombier postnote(up, 1, n, NDebug);
329*9ef1f84bSDavid du Colombier }
330*9ef1f84bSDavid du Colombier
331*9ef1f84bSDavid du Colombier static void
fpumf(Ureg * ureg,void *)332*9ef1f84bSDavid du Colombier fpumf(Ureg* ureg, void*)
333*9ef1f84bSDavid du Colombier {
334*9ef1f84bSDavid du Colombier Fxsave *fpusave;
335*9ef1f84bSDavid du Colombier
336*9ef1f84bSDavid du Colombier /*
337*9ef1f84bSDavid du Colombier * #MF - x87 Floating Point Exception Pending (Vector 16).
338*9ef1f84bSDavid du Colombier */
339*9ef1f84bSDavid du Colombier
340*9ef1f84bSDavid du Colombier /*
341*9ef1f84bSDavid du Colombier * Save FPU state to check out the error.
342*9ef1f84bSDavid du Colombier */
343*9ef1f84bSDavid du Colombier fpusave = up->fpusave;
344*9ef1f84bSDavid du Colombier _fxsave(fpusave);
345*9ef1f84bSDavid du Colombier _stts();
346*9ef1f84bSDavid du Colombier up->fpustate = Idle;
347*9ef1f84bSDavid du Colombier
348*9ef1f84bSDavid du Colombier if(ureg->ip & KZERO)
349*9ef1f84bSDavid du Colombier panic("#MF: ip=%#p rip=%#p", ureg->ip, fpusave->rip);
350*9ef1f84bSDavid du Colombier
351*9ef1f84bSDavid du Colombier /*
352*9ef1f84bSDavid du Colombier * Notify the user process.
353*9ef1f84bSDavid du Colombier * The path here is
354*9ef1f84bSDavid du Colombier * call trap->fpumf->fpupostnote->postnote
355*9ef1f84bSDavid du Colombier * return ->fpupostnote->fpumf->trap
356*9ef1f84bSDavid du Colombier * call notify->fpunotify
357*9ef1f84bSDavid du Colombier * return ->notify
358*9ef1f84bSDavid du Colombier * then either
359*9ef1f84bSDavid du Colombier * call pexit
360*9ef1f84bSDavid du Colombier * or
361*9ef1f84bSDavid du Colombier * return ->trap
362*9ef1f84bSDavid du Colombier * return ->user note handler
363*9ef1f84bSDavid du Colombier */
364*9ef1f84bSDavid du Colombier fpupostnote();
365*9ef1f84bSDavid du Colombier }
366*9ef1f84bSDavid du Colombier
367*9ef1f84bSDavid du Colombier static void
fpunm(Ureg * ureg,void *)368*9ef1f84bSDavid du Colombier fpunm(Ureg* ureg, void*)
369*9ef1f84bSDavid du Colombier {
370*9ef1f84bSDavid du Colombier Fxsave *fpusave;
371*9ef1f84bSDavid du Colombier
372*9ef1f84bSDavid du Colombier /*
373*9ef1f84bSDavid du Colombier * #NM - Device Not Available (Vector 7).
374*9ef1f84bSDavid du Colombier */
375*9ef1f84bSDavid du Colombier if(up == nil)
376*9ef1f84bSDavid du Colombier panic("#NM: fpu in kernel: ip %#p\n", ureg->ip);
377*9ef1f84bSDavid du Colombier
378*9ef1f84bSDavid du Colombier /*
379*9ef1f84bSDavid du Colombier * Someone tried to use the FPU in a note handler.
380*9ef1f84bSDavid du Colombier * That's a no-no.
381*9ef1f84bSDavid du Colombier */
382*9ef1f84bSDavid du Colombier if(up->fpustate & Hold){
383*9ef1f84bSDavid du Colombier postnote(up, 1, "sys: floating point in note handler", NDebug);
384*9ef1f84bSDavid du Colombier return;
385*9ef1f84bSDavid du Colombier }
386*9ef1f84bSDavid du Colombier if(ureg->ip & KZERO)
387*9ef1f84bSDavid du Colombier panic("#NM: proc %d %s state %d ip %#p\n",
388*9ef1f84bSDavid du Colombier up->pid, up->text, up->fpustate, ureg->ip);
389*9ef1f84bSDavid du Colombier
390*9ef1f84bSDavid du Colombier switch(up->fpustate){
391*9ef1f84bSDavid du Colombier case Busy:
392*9ef1f84bSDavid du Colombier default:
393*9ef1f84bSDavid du Colombier panic("#NM: state %d ip %#p\n", up->fpustate, ureg->ip);
394*9ef1f84bSDavid du Colombier break;
395*9ef1f84bSDavid du Colombier case Init:
396*9ef1f84bSDavid du Colombier /*
397*9ef1f84bSDavid du Colombier * A process tries to use the FPU for the
398*9ef1f84bSDavid du Colombier * first time and generates a 'device not available'
399*9ef1f84bSDavid du Colombier * exception.
400*9ef1f84bSDavid du Colombier * Turn the FPU on and initialise it for use.
401*9ef1f84bSDavid du Colombier * Set the precision and mask the exceptions
402*9ef1f84bSDavid du Colombier * we don't care about from the generic Mach value.
403*9ef1f84bSDavid du Colombier */
404*9ef1f84bSDavid du Colombier _clts();
405*9ef1f84bSDavid du Colombier _fninit();
406*9ef1f84bSDavid du Colombier _fwait();
407*9ef1f84bSDavid du Colombier _fldcw(m->fcw);
408*9ef1f84bSDavid du Colombier _ldmxcsr(m->mxcsr);
409*9ef1f84bSDavid du Colombier up->fpusave = (void*)((PTR2UINT(up->fxsave) + 15) & ~15);
410*9ef1f84bSDavid du Colombier up->fpustate = Busy;
411*9ef1f84bSDavid du Colombier break;
412*9ef1f84bSDavid du Colombier case Idle:
413*9ef1f84bSDavid du Colombier /*
414*9ef1f84bSDavid du Colombier * Before restoring the state, check for any pending
415*9ef1f84bSDavid du Colombier * exceptions, there's no way to restore the state without
416*9ef1f84bSDavid du Colombier * generating an unmasked exception.
417*9ef1f84bSDavid du Colombier */
418*9ef1f84bSDavid du Colombier fpusave = up->fpusave;
419*9ef1f84bSDavid du Colombier if((fpusave->fsw & ~fpusave->fcw) & (Sff|P|U|O|Z|D|I)){
420*9ef1f84bSDavid du Colombier fpupostnote();
421*9ef1f84bSDavid du Colombier break;
422*9ef1f84bSDavid du Colombier }
423*9ef1f84bSDavid du Colombier
424*9ef1f84bSDavid du Colombier /*
425*9ef1f84bSDavid du Colombier * Sff is sticky.
426*9ef1f84bSDavid du Colombier */
427*9ef1f84bSDavid du Colombier fpusave->fcw &= ~Sff;
428*9ef1f84bSDavid du Colombier _clts();
429*9ef1f84bSDavid du Colombier _fxrstor(fpusave);
430*9ef1f84bSDavid du Colombier up->fpustate = Busy;
431*9ef1f84bSDavid du Colombier break;
432*9ef1f84bSDavid du Colombier }
433*9ef1f84bSDavid du Colombier }
434*9ef1f84bSDavid du Colombier
435*9ef1f84bSDavid du Colombier void
fpuinit(void)436*9ef1f84bSDavid du Colombier fpuinit(void)
437*9ef1f84bSDavid du Colombier {
438*9ef1f84bSDavid du Colombier u64int r;
439*9ef1f84bSDavid du Colombier Fxsave *fxsave;
440*9ef1f84bSDavid du Colombier uchar buf[sizeof(Fxsave)+15];
441*9ef1f84bSDavid du Colombier
442*9ef1f84bSDavid du Colombier /*
443*9ef1f84bSDavid du Colombier * It's assumed there is an integrated FPU, so Em is cleared;
444*9ef1f84bSDavid du Colombier */
445*9ef1f84bSDavid du Colombier r = cr0get();
446*9ef1f84bSDavid du Colombier r &= ~(Ts|Em);
447*9ef1f84bSDavid du Colombier r |= Ne|Mp;
448*9ef1f84bSDavid du Colombier cr0put(r);
449*9ef1f84bSDavid du Colombier
450*9ef1f84bSDavid du Colombier r = cr4get();
451*9ef1f84bSDavid du Colombier r |= Osxmmexcpt|Osfxsr;
452*9ef1f84bSDavid du Colombier cr4put(r);
453*9ef1f84bSDavid du Colombier
454*9ef1f84bSDavid du Colombier _fninit();
455*9ef1f84bSDavid du Colombier fxsave = (Fxsave*)((PTR2UINT(buf) + 15) & ~15);
456*9ef1f84bSDavid du Colombier memset(fxsave, 0, sizeof(Fxsave));
457*9ef1f84bSDavid du Colombier _fxsave(fxsave);
458*9ef1f84bSDavid du Colombier m->fcw = RCn|PCd|P|U|D;
459*9ef1f84bSDavid du Colombier if(fxsave->mxcsrmask == 0)
460*9ef1f84bSDavid du Colombier m->mxcsrmask = 0x0000FFBF;
461*9ef1f84bSDavid du Colombier else
462*9ef1f84bSDavid du Colombier m->mxcsrmask = fxsave->mxcsrmask;
463*9ef1f84bSDavid du Colombier m->mxcsr = (Rn|Pm|Um|Dm) & m->mxcsrmask;
464*9ef1f84bSDavid du Colombier _stts();
465*9ef1f84bSDavid du Colombier
466*9ef1f84bSDavid du Colombier if(m->machno != 0)
467*9ef1f84bSDavid du Colombier return;
468*9ef1f84bSDavid du Colombier
469*9ef1f84bSDavid du Colombier /*
470*9ef1f84bSDavid du Colombier * Set up the exception handlers.
471*9ef1f84bSDavid du Colombier */
472*9ef1f84bSDavid du Colombier trapenable(IdtNM, fpunm, 0, "#NM");
473*9ef1f84bSDavid du Colombier trapenable(IdtMF, fpumf, 0, "#MF");
474*9ef1f84bSDavid du Colombier trapenable(IdtXF, fpuxf, 0, "#XF");
475*9ef1f84bSDavid du Colombier }
476