1 /* $OpenBSD: npx.c,v 1.40.2.1 2006/11/15 03:06:15 brad Exp $ */
2 /* $NetBSD: npx.c,v 1.57 1996/05/12 23:12:24 mycroft Exp $ */
3
4 #if 0
5 #define iprintf(x) printf x
6 #else
7 #define iprintf(x)
8 #endif
9
10 /*-
11 * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
12 * Copyright (c) 1990 William Jolitz.
13 * Copyright (c) 1991 The Regents of the University of California.
14 * All rights reserved.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * @(#)npx.c 7.2 (Berkeley) 5/12/91
41 */
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/conf.h>
46 #include <sys/file.h>
47 #include <sys/proc.h>
48 #include <sys/signalvar.h>
49 #include <sys/user.h>
50 #include <sys/ioctl.h>
51 #include <sys/device.h>
52
53 #include <uvm/uvm_extern.h>
54
55 #include <machine/cpu.h>
56 #include <machine/intr.h>
57 #include <machine/pio.h>
58 #include <machine/cpufunc.h>
59 #include <machine/pcb.h>
60 #include <machine/trap.h>
61 #include <machine/specialreg.h>
62
63 #include <dev/isa/isareg.h>
64 #include <dev/isa/isavar.h>
65 #include <i386/isa/icu.h>
66
67 #if 0
68 #define IPRINTF(x) printf x
69 #else
70 #define IPRINTF(x)
71 #endif
72
73 /*
74 * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
75 *
76 * We do lazy initialization and switching using the TS bit in cr0 and the
77 * MDP_USEDFPU bit in mdproc.
78 *
79 * DNA exceptions are handled like this:
80 *
81 * 1) If there is no NPX, return and go to the emulator.
82 * 2) If someone else has used the NPX, save its state into that process's PCB.
83 * 3a) If MDP_USEDFPU is not set, set it and initialize the NPX.
84 * 3b) Otherwise, reload the process's previous NPX state.
85 *
86 * When a process is created or exec()s, its saved cr0 image has the TS bit
87 * set and the MDP_USEDFPU bit clear. The MDP_USEDFPU bit is set when the
88 * process first gets a DNA and the NPX is initialized. The TS bit is turned
89 * off when the NPX is used, and turned on again later when the process's NPX
90 * state is saved.
91 */
92
93 #define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
94 #define fnclex() __asm("fnclex")
95 #define fninit() __asm("fninit")
96 #define fnsave(addr) __asm("fnsave %0" : "=m" (*addr))
97 #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr))
98 #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr))
99 #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
100 #define frstor(addr) __asm("frstor %0" : : "m" (*addr))
101 #define fwait() __asm("fwait")
102 #define clts() __asm("clts")
103 #define stts() lcr0(rcr0() | CR0_TS)
104
105 int npxintr(void *);
106 static int npxprobe1(struct isa_attach_args *);
107 static void npxsave1(void);
108
109 struct npx_softc {
110 struct device sc_dev;
111 void *sc_ih;
112 };
113
114 int npxprobe(struct device *, void *, void *);
115 void npxattach(struct device *, struct device *, void *);
116
117 struct cfattach npx_ca = {
118 sizeof(struct npx_softc), npxprobe, npxattach
119 };
120
121 struct cfdriver npx_cd = {
122 NULL, "npx", DV_DULL
123 };
124
125 enum npx_type {
126 NPX_NONE = 0,
127 NPX_INTERRUPT,
128 NPX_EXCEPTION,
129 NPX_BROKEN,
130 NPX_CPUID,
131 };
132
133 struct proc *npxproc;
134
135 static enum npx_type npx_type;
136 static int npx_nointr;
137 static volatile u_int npx_intrs_while_probing;
138 static volatile u_int npx_traps_while_probing;
139
140 extern int i386_fpu_present;
141 extern int i386_fpu_exception;
142 extern int i386_fpu_fdivbug;
143
144 #ifdef I686_CPU
145 #define fxsave(addr) __asm("fxsave %0" : "=m" (*addr))
146 #define fxrstor(addr) __asm("fxrstor %0" : : "m" (*addr))
147 #endif /* I686_CPU */
148
149 static __inline void
fpu_save(union savefpu * addr)150 fpu_save(union savefpu *addr)
151 {
152
153 #ifdef I686_CPU
154 if (i386_use_fxsave) {
155 fxsave(&addr->sv_xmm);
156 /* FXSAVE doesn't FNINIT like FNSAVE does -- so do it here. */
157 fninit();
158 } else
159 #endif /* I686_CPU */
160 fnsave(&addr->sv_87);
161 }
162
163 static int
npxdna_notset(struct proc * p)164 npxdna_notset(struct proc *p)
165 {
166 panic("npxdna vector not initialized");
167 }
168
169 int (*npxdna_func)(struct proc *) = npxdna_notset;
170 int npxdna_s87(struct proc *);
171 #ifdef I686_CPU
172 int npxdna_xmm(struct proc *);
173 #endif /* I686_CPU */
174 void npxexit(void);
175
176 /*
177 * Special interrupt handlers. Someday intr0-intr15 will be used to count
178 * interrupts. We'll still need a special exception 16 handler. The busy
179 * latch stuff in probintr() can be moved to npxprobe().
180 */
181 void probeintr(void);
182 asm (".text\n\t"
183 "probeintr:\n\t"
184 "ss\n\t"
185 "incl npx_intrs_while_probing\n\t"
186 "pushl %eax\n\t"
187 "movb $0x20,%al # EOI (asm in strings loses cpp features)\n\t"
188 "outb %al,$0xa0 # IO_ICU2\n\t"
189 "outb %al,$0x20 # IO_ICU1\n\t"
190 "movb $0,%al\n\t"
191 "outb %al,$0xf0 # clear BUSY# latch\n\t"
192 "popl %eax\n\t"
193 "iret\n\t");
194
195 void probetrap(void);
196 asm (".text\n\t"
197 "probetrap:\n\t"
198 "ss\n\t"
199 "incl npx_traps_while_probing\n\t"
200 "fnclex\n\t"
201 "iret\n\t");
202
203 static inline int
npxprobe1(ia)204 npxprobe1(ia)
205 struct isa_attach_args *ia;
206 {
207 #ifndef ALWAYS_MATH_EMULATE
208 int control;
209 int status;
210
211 ia->ia_iosize = 16;
212 ia->ia_msize = 0;
213
214 /*
215 * Finish resetting the coprocessor, if any. If there is an error
216 * pending, then we may get a bogus IRQ13, but probeintr() will handle
217 * it OK. Bogus halts have never been observed, but we enabled
218 * IRQ13 and cleared the BUSY# latch early to handle them anyway.
219 */
220 fninit();
221 delay(1000); /* wait for any IRQ13 (fwait might hang) */
222
223 /*
224 * Check for a status of mostly zero.
225 */
226 status = 0x5a5a;
227 fnstsw(&status);
228 if ((status & 0xb8ff) == 0) {
229 /*
230 * Good, now check for a proper control word.
231 */
232 control = 0x5a5a;
233 fnstcw(&control);
234 if ((control & 0x1f3f) == 0x033f) {
235 /*
236 * We have an npx, now divide by 0 to see if exception
237 * 16 works.
238 */
239 control &= ~(1 << 2); /* enable divide by 0 trap */
240 fldcw(&control);
241 npx_traps_while_probing = npx_intrs_while_probing = 0;
242 fp_divide_by_0();
243 delay(1);
244 if (npx_traps_while_probing != 0) {
245 /*
246 * Good, exception 16 works.
247 */
248 npx_type = NPX_EXCEPTION;
249 ia->ia_irq = IRQUNK; /* zap the interrupt */
250 i386_fpu_exception = 1;
251 } else if (npx_intrs_while_probing != 0) {
252 /*
253 * Bad, we are stuck with IRQ13.
254 */
255 npx_type = NPX_INTERRUPT;
256 } else {
257 /*
258 * Worse, even IRQ13 is broken. Use emulator.
259 */
260 npx_type = NPX_BROKEN;
261 ia->ia_irq = IRQUNK;
262 }
263 return 1;
264 }
265 }
266 #else
267 npx_intrs_while_probing = npx_traps_while_probing = 0;
268 #endif
269
270 /*
271 * Probe failed. There is no usable FPU.
272 */
273 npx_type = NPX_NONE;
274 return 0;
275 }
276
277 /*
278 * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
279 * whether the device exists or not (XXX should be elsewhere). Set flags
280 * to tell npxattach() what to do. Modify device struct if npx doesn't
281 * need to use interrupts. Return 1 if device exists.
282 */
283 int
npxprobe(parent,match,aux)284 npxprobe(parent, match, aux)
285 struct device *parent;
286 void *match, *aux;
287 {
288 struct isa_attach_args *ia = aux;
289 int irq;
290 int result;
291 u_long save_eflags;
292 unsigned save_imen;
293 struct gate_descriptor save_idt_npxintr;
294 struct gate_descriptor save_idt_npxtrap;
295
296 if (cpu_feature & CPUID_FPU) {
297 npx_type = NPX_CPUID;
298 i386_fpu_exception = 1;
299 ia->ia_irq = IRQUNK; /* Don't want the interrupt vector */
300 ia->ia_iosize = 16;
301 ia->ia_msize = 0;
302 return 1;
303 }
304
305 /*
306 * This routine is now just a wrapper for npxprobe1(), to install
307 * special npx interrupt and trap handlers, to enable npx interrupts
308 * and to disable other interrupts. Someday isa_configure() will
309 * install suitable handlers and run with interrupts enabled so we
310 * won't need to do so much here.
311 */
312 irq = NRSVIDT + ia->ia_irq;
313 save_eflags = read_eflags();
314 disable_intr();
315 save_idt_npxintr = idt[irq];
316 save_idt_npxtrap = idt[16];
317 setgate(&idt[irq], probeintr, 0, SDT_SYS386IGT, SEL_KPL, GICODE_SEL);
318 setgate(&idt[16], probetrap, 0, SDT_SYS386TGT, SEL_KPL, GCODE_SEL);
319 save_imen = imen;
320 imen = ~((1 << IRQ_SLAVE) | (1 << ia->ia_irq));
321 SET_ICUS();
322
323 /*
324 * Partially reset the coprocessor, if any. Some BIOS's don't reset
325 * it after a warm boot.
326 */
327 outb(0xf1, 0); /* full reset on some systems, NOP on others */
328 delay(1000);
329 outb(0xf0, 0); /* clear BUSY# latch */
330
331 /*
332 * We set CR0 in locore to trap all ESC and WAIT instructions.
333 * We have to turn off the CR0_EM bit temporarily while probing.
334 */
335 lcr0(rcr0() & ~(CR0_EM|CR0_TS));
336 enable_intr();
337 result = npxprobe1(ia);
338 disable_intr();
339 lcr0(rcr0() | (CR0_EM|CR0_TS));
340
341 imen = save_imen;
342 SET_ICUS();
343 idt[irq] = save_idt_npxintr;
344 idt[16] = save_idt_npxtrap;
345 write_eflags(save_eflags);
346 return (result);
347 }
348
349 int npx586bug1(int, int);
350 asm (".text\n\t"
351 "npx586bug1:\n\t"
352 "fildl 4(%esp) # x\n\t"
353 "fildl 8(%esp) # y\n\t"
354 "fld %st(1)\n\t"
355 "fdiv %st(1),%st # x/y\n\t"
356 "fmulp %st,%st(1) # (x/y)*y\n\t"
357 "fsubrp %st,%st(1) # x-(x/y)*y\n\t"
358 "pushl $0\n\t"
359 "fistpl (%esp)\n\t"
360 "popl %eax\n\t"
361 "ret\n\t");
362
363 /*
364 * Attach routine - announce which it is, and wire into system
365 */
366 void
npxattach(parent,self,aux)367 npxattach(parent, self, aux)
368 struct device *parent, *self;
369 void *aux;
370 {
371 struct npx_softc *sc = (void *)self;
372 struct isa_attach_args *ia = aux;
373
374 switch (npx_type) {
375 case NPX_INTERRUPT:
376 printf("\n");
377 lcr0(rcr0() & ~CR0_NE);
378 sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq,
379 IST_EDGE, IPL_NONE, npxintr, 0, sc->sc_dev.dv_xname);
380 break;
381 case NPX_EXCEPTION:
382 printf(": using exception 16\n");
383 break;
384 case NPX_CPUID:
385 printf(": reported by CPUID; using exception 16\n");
386 npx_type = NPX_EXCEPTION;
387 break;
388 case NPX_BROKEN:
389 printf(": error reporting broken; not using\n");
390 npx_type = NPX_NONE;
391 return;
392 case NPX_NONE:
393 return;
394 }
395
396 lcr0(rcr0() & ~(CR0_EM|CR0_TS));
397 fninit();
398 if (npx586bug1(4195835, 3145727) != 0) {
399 i386_fpu_fdivbug = 1;
400 printf("WARNING: Pentium FDIV bug detected!\n");
401 }
402 lcr0(rcr0() | (CR0_TS));
403 i386_fpu_present = 1;
404
405 #ifdef I686_CPU
406 if (i386_use_fxsave)
407 npxdna_func = npxdna_xmm;
408 else
409 #endif /* I686_CPU */
410 npxdna_func = npxdna_s87;
411 }
412
413 /*
414 * Record the FPU state and reinitialize it all except for the control word.
415 * Then generate a SIGFPE.
416 *
417 * Reinitializing the state allows naive SIGFPE handlers to longjmp without
418 * doing any fixups.
419 *
420 * XXX there is currently no way to pass the full error state to signal
421 * handlers, and if this is a nested interrupt there is no way to pass even
422 * a status code! So there is no way to have a non-naive SIGFPE handler. At
423 * best a handler could do an fninit followed by an fldcw of a static value.
424 * fnclex would be of little use because it would leave junk on the FPU stack.
425 * Returning from the handler would be even less safe than usual because
426 * IRQ13 exception handling makes exceptions even less precise than usual.
427 */
428 int
npxintr(arg)429 npxintr(arg)
430 void *arg;
431 {
432 register struct proc *p = npxproc;
433 union savefpu *addr;
434 struct intrframe *frame = arg;
435 int code;
436 union sigval sv;
437
438 uvmexp.traps++;
439 iprintf(("Intr"));
440
441 if (p == 0 || npx_type == NPX_NONE) {
442 /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
443 printf("npxintr: p = %lx, curproc = %lx, npx_type = %d\n",
444 (u_long) p, (u_long) curproc, npx_type);
445 panic("npxintr from nowhere");
446 }
447 /*
448 * Clear the interrupt latch.
449 */
450 outb(0xf0, 0);
451 /*
452 * If we're saving, ignore the interrupt. The FPU will happily
453 * generate another one when we restore the state later.
454 */
455 if (npx_nointr != 0)
456 return (1);
457 /*
458 * Find the address of npxproc's savefpu. This is not necessarily
459 * the one in curpcb.
460 */
461 addr = &p->p_addr->u_pcb.pcb_savefpu;
462 /*
463 * Save state. This does an implied fninit. It had better not halt
464 * the cpu or we'll hang.
465 */
466 fpu_save(addr);
467 fwait();
468 /*
469 * Restore control word (was clobbered by fpu_save).
470 */
471 if (i386_use_fxsave) {
472 fldcw(&addr->sv_xmm.sv_env.en_cw);
473 /*
474 * FNINIT doesn't affect MXCSR or the XMM registers;
475 * no need to re-load MXCSR here.
476 */
477 } else
478 fldcw(&addr->sv_87.sv_env.en_cw);
479 fwait();
480 /*
481 * Remember the exception status word and tag word. The current
482 * (almost fninit'ed) fpu state is in the fpu and the exception
483 * state just saved will soon be junk. However, the implied fninit
484 * doesn't change the error pointers or register contents, and we
485 * preserved the control word and will copy the status and tag
486 * words, so the complete exception state can be recovered.
487 */
488 if (i386_use_fxsave) {
489 addr->sv_xmm.sv_ex_sw = addr->sv_xmm.sv_env.en_sw;
490 addr->sv_xmm.sv_ex_tw = addr->sv_xmm.sv_env.en_tw;
491 } else {
492 addr->sv_87.sv_ex_sw = addr->sv_87.sv_env.en_sw;
493 addr->sv_87.sv_ex_tw = addr->sv_87.sv_env.en_tw;
494 }
495
496 /*
497 * Pass exception to process. If it's the current process, try to do
498 * it immediately.
499 */
500 if (p == curproc && USERMODE(frame->if_cs, frame->if_eflags)) {
501 /*
502 * Interrupt is essentially a trap, so we can afford to call
503 * the SIGFPE handler (if any) as soon as the interrupt
504 * returns.
505 *
506 * XXX little or nothing is gained from this, and plenty is
507 * lost - the interrupt frame has to contain the trap frame
508 * (this is otherwise only necessary for the rescheduling trap
509 * in doreti, and the frame for that could easily be set up
510 * just before it is used).
511 */
512 p->p_md.md_regs = (struct trapframe *)&frame->if_es;
513
514 /*
515 * Encode the appropriate code for detailed information on
516 * this exception.
517 */
518 if (addr->sv_87.sv_ex_sw & EN_SW_IE)
519 code = FPE_FLTINV;
520 #ifdef notyet
521 else if (addr->sv_ex_sw & EN_SW_DE)
522 code = FPE_FLTDEN;
523 #endif
524 else if (addr->sv_87.sv_ex_sw & EN_SW_ZE)
525 code = FPE_FLTDIV;
526 else if (addr->sv_87.sv_ex_sw & EN_SW_OE)
527 code = FPE_FLTOVF;
528 else if (addr->sv_87.sv_ex_sw & EN_SW_UE)
529 code = FPE_FLTUND;
530 else if (addr->sv_87.sv_ex_sw & EN_SW_PE)
531 code = FPE_FLTRES;
532 else
533 code = 0; /* XXX unknown */
534 sv.sival_int = frame->if_eip;
535 trapsignal(p, SIGFPE, T_ARITHTRAP, code, sv);
536 } else {
537 /*
538 * Nested interrupt. These losers occur when:
539 * o an IRQ13 is bogusly generated at a bogus time, e.g.:
540 * o immediately after an fnsave or frstor of an
541 * error state.
542 * o a couple of 386 instructions after
543 * "fstpl _memvar" causes a stack overflow.
544 * These are especially nasty when combined with a
545 * trace trap.
546 * o an IRQ13 occurs at the same time as another higher-
547 * priority interrupt.
548 *
549 * Treat them like a true async interrupt.
550 */
551 psignal(p, SIGFPE);
552 }
553
554 return (1);
555 }
556
557 /*
558 * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
559 * pending, then fnsave generates a bogus IRQ13 on some systems. Force any
560 * IRQ13 to be handled immediately, and then ignore it.
561 *
562 * This routine is always called at spl0. If it might called with the NPX
563 * interrupt masked, it would be necessary to forcibly unmask the NPX interrupt
564 * so that it could succeed.
565 */
566 static __inline void
npxsave1(void)567 npxsave1(void)
568 {
569 register struct pcb *pcb;
570
571 npx_nointr = 1;
572 pcb = &npxproc->p_addr->u_pcb;
573 fpu_save(&pcb->pcb_savefpu);
574 pcb->pcb_cr0 |= CR0_TS;
575 fwait();
576 npx_nointr = 0;
577 }
578
579 /*
580 * Implement device not available (DNA) exception
581 *
582 * If the we were the last process to use the FPU, we can simply return.
583 * Otherwise, we save the previous state, if necessary, and restore our last
584 * saved state.
585 */
586 #ifdef I686_CPU
587 int
npxdna_xmm(struct proc * p)588 npxdna_xmm(struct proc *p)
589 {
590
591 #ifdef DIAGNOSTIC
592 if (cpl != 0 || npx_nointr != 0)
593 panic("npxdna: masked");
594 #endif
595
596 p->p_addr->u_pcb.pcb_cr0 &= ~CR0_TS;
597 clts();
598
599 /*
600 * Initialize the FPU state to clear any exceptions. If someone else
601 * was using the FPU, save their state (which does an implicit
602 * initialization).
603 */
604 npx_nointr = 1;
605 if (npxproc != 0 && npxproc != p) {
606 IPRINTF(("Save"));
607 npxsave1();
608 } else {
609 IPRINTF(("Init"));
610 fninit();
611 fwait();
612 }
613 npx_nointr = 0;
614 npxproc = p;
615
616 if ((p->p_md.md_flags & MDP_USEDFPU) == 0) {
617 fldcw(&p->p_addr->u_pcb.pcb_savefpu.sv_xmm.sv_env.en_cw);
618 p->p_md.md_flags |= MDP_USEDFPU;
619 } else {
620 static double zero = 0.0;
621
622 /*
623 * amd fpu does not restore fip, fdp, fop on fxrstor
624 * thus leaking other process' execution history.
625 */
626 fnclex();
627 __asm __volatile("ffree %%st(7)\n\tfld %0" : : "m" (zero));
628 fxrstor(&p->p_addr->u_pcb.pcb_savefpu.sv_xmm);
629 }
630
631 return (1);
632 }
633 #endif /* I686_CPU */
634
635 int
npxdna_s87(struct proc * p)636 npxdna_s87(struct proc *p)
637 {
638 static u_short control = __NPXCW__;
639
640 if (npx_type == NPX_NONE) {
641 iprintf(("Emul"));
642 return (0);
643 }
644
645 #ifdef DIAGNOSTIC
646 if (cpl != IPL_NONE || npx_nointr != 0)
647 panic("npxdna: masked");
648 #endif
649
650 p->p_addr->u_pcb.pcb_cr0 &= ~CR0_TS;
651 clts();
652
653 if ((p->p_md.md_flags & MDP_USEDFPU) == 0) {
654 p->p_md.md_flags |= MDP_USEDFPU;
655 iprintf(("Init"));
656 if (npxproc != 0 && npxproc != p)
657 npxsave1();
658 else {
659 npx_nointr = 1;
660 fninit();
661 fwait();
662 npx_nointr = 0;
663 }
664 npxproc = p;
665 fldcw(&control);
666 } else {
667 if (npxproc != 0) {
668 #ifdef DIAGNOSTIC
669 if (npxproc == p)
670 panic("npxdna: same process");
671 #endif
672 iprintf(("Save"));
673 npxsave1();
674 }
675 npxproc = p;
676 /*
677 * The following frstor may cause an IRQ13 when the state being
678 * restored has a pending error. The error will appear to have
679 * been triggered by the current (npx) user instruction even
680 * when that instruction is a no-wait instruction that should
681 * not trigger an error (e.g., fnclex). On at least one 486
682 * system all of the no-wait instructions are broken the same
683 * as frstor, so our treatment does not amplify the breakage.
684 * On at least one 386/Cyrix 387 system, fnclex works correctly
685 * while frstor and fnsave are broken, so our treatment breaks
686 * fnclex if it is the first FPU instruction after a context
687 * switch.
688 */
689 frstor(&p->p_addr->u_pcb.pcb_savefpu.sv_87);
690 }
691
692 return (1);
693 }
694
695 /*
696 * Drop the current FPU state on the floor.
697 */
698 void
npxdrop()699 npxdrop()
700 {
701
702 stts();
703 npxproc->p_addr->u_pcb.pcb_cr0 |= CR0_TS;
704 npxproc = 0;
705 }
706
707 /*
708 * Save npxproc's FPU state.
709 *
710 * The FNSAVE instruction clears the FPU state. Rather than reloading the FPU
711 * immediately, we clear npxproc and turn on CR0_TS to force a DNA and a reload
712 * of the FPU state the next time we try to use it. This routine is only
713 * called when forking or core dump, so this algorithm at worst forces us to
714 * trap once per fork(), and at best saves us a reload once per fork().
715 */
716 void
npxsave()717 npxsave()
718 {
719
720 #ifdef DIAGNOSTIC
721 if (cpl != IPL_NONE || npx_nointr != 0)
722 panic("npxsave: masked");
723 #endif
724 iprintf(("Fork"));
725 clts();
726 npxsave1();
727 stts();
728 npxproc = 0;
729 }
730