FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/npx.c
1 /*-
2 * Copyright (c) 1990 William Jolitz.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * from: @(#)npx.c 7.2 (Berkeley) 5/12/91
35 * $FreeBSD: src/sys/i386/isa/npx.c,v 1.31.2.9 1999/09/05 08:13:15 peter Exp $
36 */
37
38 #include "npx.h"
39 #if NNPX > 0
40
41 #include "opt_cpu.h"
42 #include "opt_math_emulate.h"
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/sysctl.h>
48 #include <sys/conf.h>
49 #include <sys/file.h>
50 #include <sys/proc.h>
51 #include <sys/ioctl.h>
52 #ifdef NPX_DEBUG
53 #include <sys/syslog.h>
54 #endif
55 #include <sys/signalvar.h>
56
57 #include <machine/cpu.h>
58 #include <machine/pcb.h>
59 #include <machine/md_var.h>
60 #include <machine/trap.h>
61 #include <machine/clock.h>
62 #include <machine/specialreg.h>
63
64 #include <i386/isa/icu.h>
65 #include <i386/isa/isa_device.h>
66 #include <i386/isa/isa.h>
67
68 /*
69 * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
70 */
71
72 /* Configuration flags. */
73 #define NPX_DISABLE_I586_OPTIMIZED_BCOPY (1 << 0)
74 #define NPX_DISABLE_I586_OPTIMIZED_BZERO (1 << 1)
75 #define NPX_DISABLE_I586_OPTIMIZED_COPYIO (1 << 2)
76
77 /* XXX - should be in header file. */
78 extern void (*bcopy_vector) __P((const void *from, void *to, size_t len));
79 extern void (*ovbcopy_vector) __P((const void *from, void *to, size_t len));
80 extern int (*copyin_vector) __P((const void *udaddr, void *kaddr, size_t len));
81 extern int (*copyout_vector) __P((const void *kaddr, void *udaddr, size_t len));
82
83 void i586_bcopy __P((const void *from, void *to, size_t len));
84 void i586_bzero __P((void *buf, size_t len));
85 int i586_copyin __P((const void *udaddr, void *kaddr, size_t len));
86 int i586_copyout __P((const void *kaddr, void *udaddr, size_t len));
87
88 #ifdef __GNUC__
89
90 #define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr)))
91 #define fnclex() __asm("fnclex")
92 #define fninit() __asm("fninit")
93 #define fnop() __asm("fnop")
94 #define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr)))
95 #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr)))
96 #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr)))
97 #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop")
98 #define frstor(addr) __asm("frstor %0" : : "m" (*(addr)))
99 #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
100 : : "n" (CR0_TS) : "ax")
101 #define stop_emulating() __asm("clts")
102
103 #else /* not __GNUC__ */
104
105 void fldcw __P((caddr_t addr));
106 void fnclex __P((void));
107 void fninit __P((void));
108 void fnop __P((void));
109 void fnsave __P((caddr_t addr));
110 void fnstcw __P((caddr_t addr));
111 void fnstsw __P((caddr_t addr));
112 void fp_divide_by_0 __P((void));
113 void frstor __P((caddr_t addr));
114 void start_emulating __P((void));
115 void stop_emulating __P((void));
116
117 #endif /* __GNUC__ */
118
119 typedef u_char bool_t;
120
121 static int npxattach __P((struct isa_device *dvp));
122 static int npxprobe __P((struct isa_device *dvp));
123 static int npxprobe1 __P((struct isa_device *dvp));
124
125 struct isa_driver npxdriver = {
126 npxprobe, npxattach, "npx",
127 };
128
129 int hw_float; /* XXX currently just alias for npx_exists */
130
131 SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint,
132 CTLFLAG_RD, &hw_float, 0,
133 "Floatingpoint instructions executed in hardware");
134
135 static u_int npx0_imask = SWI_CLOCK_MASK;
136 struct proc *npxproc;
137
138 static bool_t npx_ex16;
139 static bool_t npx_exists;
140 static struct gate_descriptor npx_idt_probeintr;
141 static int npx_intrno;
142 static volatile u_int npx_intrs_while_probing;
143 static bool_t npx_irq13;
144 static volatile u_int npx_traps_while_probing;
145
146 /*
147 * Special interrupt handlers. Someday intr0-intr15 will be used to count
148 * interrupts. We'll still need a special exception 16 handler. The busy
149 * latch stuff in probeintr() can be moved to npxprobe().
150 */
151 inthand_t probeintr;
152 __asm
153 ("
154 .text
155 _probeintr:
156 ss
157 incl _npx_intrs_while_probing
158 pushl %eax
159 movb $0x20,%al # EOI (asm in strings loses cpp features)
160 outb %al,$0xa0 # IO_ICU2
161 outb %al,$0x20 # IO_ICU1
162 movb $0,%al
163 outb %al,$0xf0 # clear BUSY# latch
164 popl %eax
165 iret
166 ");
167
168 inthand_t probetrap;
169 __asm
170 ("
171 .text
172 _probetrap:
173 ss
174 incl _npx_traps_while_probing
175 fnclex
176 iret
177 ");
178
179 /*
180 * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
181 * whether the device exists or not (XXX should be elsewhere). Set flags
182 * to tell npxattach() what to do. Modify device struct if npx doesn't
183 * need to use interrupts. Return 1 if device exists.
184 */
185 static int
186 npxprobe(dvp)
187 struct isa_device *dvp;
188 {
189 int result;
190 u_long save_eflags;
191 u_char save_icu1_mask;
192 u_char save_icu2_mask;
193 struct gate_descriptor save_idt_npxintr;
194 struct gate_descriptor save_idt_npxtrap;
195 /*
196 * This routine is now just a wrapper for npxprobe1(), to install
197 * special npx interrupt and trap handlers, to enable npx interrupts
198 * and to disable other interrupts. Someday isa_configure() will
199 * install suitable handlers and run with interrupts enabled so we
200 * won't need to do so much here.
201 */
202 npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
203 save_eflags = read_eflags();
204 disable_intr();
205 save_icu1_mask = inb(IO_ICU1 + 1);
206 save_icu2_mask = inb(IO_ICU2 + 1);
207 save_idt_npxintr = idt[npx_intrno];
208 save_idt_npxtrap = idt[16];
209 outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
210 outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
211 setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
212 setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
213 npx_idt_probeintr = idt[npx_intrno];
214 enable_intr();
215 result = npxprobe1(dvp);
216 disable_intr();
217 outb(IO_ICU1 + 1, save_icu1_mask);
218 outb(IO_ICU2 + 1, save_icu2_mask);
219 idt[npx_intrno] = save_idt_npxintr;
220 idt[16] = save_idt_npxtrap;
221 write_eflags(save_eflags);
222 return (result);
223 }
224
225 static int
226 npxprobe1(dvp)
227 struct isa_device *dvp;
228 {
229 u_short control;
230 u_short status;
231
232 /*
233 * Partially reset the coprocessor, if any. Some BIOS's don't reset
234 * it after a warm boot.
235 */
236 outb(0xf1, 0); /* full reset on some systems, NOP on others */
237 outb(0xf0, 0); /* clear BUSY# latch */
238 /*
239 * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
240 * instructions. We must set the CR0_MP bit and use the CR0_TS
241 * bit to control the trap, because setting the CR0_EM bit does
242 * not cause WAIT instructions to trap. It's important to trap
243 * WAIT instructions - otherwise the "wait" variants of no-wait
244 * control instructions would degenerate to the "no-wait" variants
245 * after FP context switches but work correctly otherwise. It's
246 * particularly important to trap WAITs when there is no NPX -
247 * otherwise the "wait" variants would always degenerate.
248 *
249 * Try setting CR0_NE to get correct error reporting on 486DX's.
250 * Setting it should fail or do nothing on lesser processors.
251 */
252 load_cr0(rcr0() | CR0_MP | CR0_NE);
253 /*
254 * But don't trap while we're probing.
255 */
256 stop_emulating();
257 /*
258 * Finish resetting the coprocessor, if any. If there is an error
259 * pending, then we may get a bogus IRQ13, but probeintr() will handle
260 * it OK. Bogus halts have never been observed, but we enabled
261 * IRQ13 and cleared the BUSY# latch early to handle them anyway.
262 */
263 fninit();
264 /*
265 * Don't use fwait here because it might hang.
266 * Don't use fnop here because it usually hangs if there is no FPU.
267 */
268 DELAY(1000); /* wait for any IRQ13 */
269 #ifdef DIAGNOSTIC
270 if (npx_intrs_while_probing != 0)
271 printf("fninit caused %u bogus npx interrupt(s)\n",
272 npx_intrs_while_probing);
273 if (npx_traps_while_probing != 0)
274 printf("fninit caused %u bogus npx trap(s)\n",
275 npx_traps_while_probing);
276 #endif
277 /*
278 * Check for a status of mostly zero.
279 */
280 status = 0x5a5a;
281 fnstsw(&status);
282 if ((status & 0xb8ff) == 0) {
283 /*
284 * Good, now check for a proper control word.
285 */
286 control = 0x5a5a;
287 fnstcw(&control);
288 if ((control & 0x1f3f) == 0x033f) {
289 hw_float = npx_exists = 1;
290 /*
291 * We have an npx, now divide by 0 to see if exception
292 * 16 works.
293 */
294 control &= ~(1 << 2); /* enable divide by 0 trap */
295 fldcw(&control);
296 npx_traps_while_probing = npx_intrs_while_probing = 0;
297 fp_divide_by_0();
298 if (npx_traps_while_probing != 0) {
299 /*
300 * Good, exception 16 works.
301 */
302 npx_ex16 = 1;
303 dvp->id_irq = 0; /* zap the interrupt */
304 /*
305 * special return value to flag that we do not
306 * actually use any I/O registers
307 */
308 return (-1);
309 }
310 if (npx_intrs_while_probing != 0) {
311 /*
312 * Bad, we are stuck with IRQ13.
313 */
314 npx_irq13 = 1;
315 /*
316 * npxattach would be too late to set npx0_imask.
317 */
318 npx0_imask |= dvp->id_irq;
319 return (IO_NPXSIZE);
320 }
321 /*
322 * Worse, even IRQ13 is broken. Use emulator.
323 */
324 }
325 }
326 /*
327 * Probe failed, but we want to get to npxattach to initialize the
328 * emulator and say that it has been installed. XXX handle devices
329 * that aren't really devices better.
330 */
331 dvp->id_irq = 0;
332 /*
333 * special return value to flag that we do not
334 * actually use any I/O registers
335 */
336 return (-1);
337 }
338
339 /*
340 * Attach routine - announce which it is, and wire into system
341 */
342 int
343 npxattach(dvp)
344 struct isa_device *dvp;
345 {
346 /* The caller has printed "irq 13" for the npx_irq13 case. */
347 if (!npx_irq13) {
348 printf("npx%d: ", dvp->id_unit);
349 if (npx_ex16)
350 printf("INT 16 interface\n");
351 #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE)
352 else if (npx_exists) {
353 printf("error reporting broken; using 387 emulator\n");
354 hw_float = npx_exists = 0;
355 } else
356 printf("387 emulator\n");
357 #else
358 else
359 printf("no 387 emulator in kernel!\n");
360 #endif
361 }
362 npxinit(__INITIAL_NPXCW__);
363
364 #ifdef I586_CPU
365 if (cpu_class == CPUCLASS_586 && npx_ex16) {
366 if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) {
367 bcopy_vector = i586_bcopy;
368 ovbcopy_vector = i586_bcopy;
369 }
370 if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BZERO))
371 bzero = i586_bzero;
372 if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) {
373 copyin_vector = i586_copyin;
374 copyout_vector = i586_copyout;
375 }
376 }
377 #endif
378
379 return (1); /* XXX unused */
380 }
381
382 /*
383 * Initialize floating point unit.
384 */
385 void
386 npxinit(control)
387 u_short control;
388 {
389 struct save87 dummy;
390
391 if (!npx_exists)
392 return;
393 /*
394 * fninit has the same h/w bugs as fnsave. Use the detoxified
395 * fnsave to throw away any junk in the fpu. npxsave() initializes
396 * the fpu and sets npxproc = NULL as important side effects.
397 */
398 npxsave(&dummy);
399 stop_emulating();
400 fldcw(&control);
401 if (curpcb != NULL)
402 fnsave(&curpcb->pcb_savefpu);
403 start_emulating();
404 }
405
406 /*
407 * Free coprocessor (if we have it).
408 */
409 void
410 npxexit(p)
411 struct proc *p;
412 {
413
414 if (p == npxproc)
415 npxsave(&curpcb->pcb_savefpu);
416 #ifdef NPX_DEBUG
417 if (npx_exists) {
418 u_int masked_exceptions;
419
420 masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw
421 & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f;
422 /*
423 * Log exceptions that would have trapped with the old
424 * control word (overflow, divide by 0, and invalid operand).
425 */
426 if (masked_exceptions & 0x0d)
427 log(LOG_ERR,
428 "pid %d (%s) exited with masked floating point exceptions 0x%02x\n",
429 p->p_pid, p->p_comm, masked_exceptions);
430 }
431 #endif
432 }
433
434 /*
435 * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE.
436 *
437 * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now
438 * depend on longjmp() restoring a usable state. Restoring the state
439 * or examining it might fail if we didn't clear exceptions.
440 *
441 * XXX there is no standard way to tell SIGFPE handlers about the error
442 * state. The old interface:
443 *
444 * void handler(int sig, int code, struct sigcontext *scp);
445 *
446 * is broken because it is non-ANSI and because the FP state is not in
447 * struct sigcontext.
448 *
449 * XXX the FP state is not preserved across signal handlers. So signal
450 * handlers cannot afford to do FP unless they preserve the state or
451 * longjmp() out. Both preserving the state and longjmp()ing may be
452 * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable
453 * solution for signals other than SIGFPE.
454 */
455 void
456 npxintr(unit)
457 int unit;
458 {
459 int code;
460 struct intrframe *frame;
461
462 if (npxproc == NULL || !npx_exists) {
463 printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n",
464 npxproc, curproc, npx_exists);
465 panic("npxintr from nowhere");
466 }
467 if (npxproc != curproc) {
468 printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n",
469 npxproc, curproc, npx_exists);
470 panic("npxintr from non-current process");
471 }
472
473 outb(0xf0, 0);
474 fnstsw(&curpcb->pcb_savefpu.sv_ex_sw);
475 fnclex();
476
477 /*
478 * Pass exception to process.
479 */
480 frame = (struct intrframe *)&unit; /* XXX */
481 if (ISPL(frame->if_cs) == SEL_UPL) {
482 /*
483 * Interrupt is essentially a trap, so we can afford to call
484 * the SIGFPE handler (if any) as soon as the interrupt
485 * returns.
486 *
487 * XXX little or nothing is gained from this, and plenty is
488 * lost - the interrupt frame has to contain the trap frame
489 * (this is otherwise only necessary for the rescheduling trap
490 * in doreti, and the frame for that could easily be set up
491 * just before it is used).
492 */
493 curproc->p_md.md_regs = &frame->if_es;
494 #ifdef notyet
495 /*
496 * Encode the appropriate code for detailed information on
497 * this exception.
498 */
499 code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
500 #else
501 code = 0; /* XXX */
502 #endif
503 trapsignal(curproc, SIGFPE, code);
504 } else {
505 /*
506 * Nested interrupt. These losers occur when:
507 * o an IRQ13 is bogusly generated at a bogus time, e.g.:
508 * o immediately after an fnsave or frstor of an
509 * error state.
510 * o a couple of 386 instructions after
511 * "fstpl _memvar" causes a stack overflow.
512 * These are especially nasty when combined with a
513 * trace trap.
514 * o an IRQ13 occurs at the same time as another higher-
515 * priority interrupt.
516 *
517 * Treat them like a true async interrupt.
518 */
519 psignal(curproc, SIGFPE);
520 }
521 }
522
523 /*
524 * Implement device not available (DNA) exception
525 *
526 * It would be better to switch FP context here (if curproc != npxproc)
527 * and not necessarily for every context switch, but it is too hard to
528 * access foreign pcb's.
529 */
530 int
531 npxdna()
532 {
533 if (!npx_exists)
534 return (0);
535 if (npxproc != NULL) {
536 printf("npxdna: npxproc = %p, curproc = %p\n",
537 npxproc, curproc);
538 panic("npxdna");
539 }
540 stop_emulating();
541 /*
542 * Record new context early in case frstor causes an IRQ13.
543 */
544 npxproc = curproc;
545 curpcb->pcb_savefpu.sv_ex_sw = 0;
546 /*
547 * The following frstor may cause an IRQ13 when the state being
548 * restored has a pending error. The error will appear to have been
549 * triggered by the current (npx) user instruction even when that
550 * instruction is a no-wait instruction that should not trigger an
551 * error (e.g., fnclex). On at least one 486 system all of the
552 * no-wait instructions are broken the same as frstor, so our
553 * treatment does not amplify the breakage. On at least one
554 * 386/Cyrix 387 system, fnclex works correctly while frstor and
555 * fnsave are broken, so our treatment breaks fnclex if it is the
556 * first FPU instruction after a context switch.
557 */
558 frstor(&curpcb->pcb_savefpu);
559
560 return (1);
561 }
562
563 /*
564 * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
565 * pending, then fnsave generates a bogus IRQ13 on some systems. Force
566 * any IRQ13 to be handled immediately, and then ignore it. This routine is
567 * often called at splhigh so it must not use many system services. In
568 * particular, it's much easier to install a special handler than to
569 * guarantee that it's safe to use npxintr() and its supporting code.
570 */
571 void
572 npxsave(addr)
573 struct save87 *addr;
574 {
575 u_char icu1_mask;
576 u_char icu2_mask;
577 u_char old_icu1_mask;
578 u_char old_icu2_mask;
579 struct gate_descriptor save_idt_npxintr;
580
581 disable_intr();
582 old_icu1_mask = inb(IO_ICU1 + 1);
583 old_icu2_mask = inb(IO_ICU2 + 1);
584 save_idt_npxintr = idt[npx_intrno];
585 outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
586 outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
587 idt[npx_intrno] = npx_idt_probeintr;
588 enable_intr();
589 stop_emulating();
590 fnsave(addr);
591 fnop();
592 start_emulating();
593 npxproc = NULL;
594 disable_intr();
595 icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
596 icu2_mask = inb(IO_ICU2 + 1);
597 outb(IO_ICU1 + 1,
598 (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
599 outb(IO_ICU2 + 1,
600 (icu2_mask & ~(npx0_imask >> 8))
601 | (old_icu2_mask & (npx0_imask >> 8)));
602 idt[npx_intrno] = save_idt_npxintr;
603 enable_intr(); /* back to usual state */
604 }
605
606 #endif /* NNPX > 0 */
Cache object: d06d677d56ddb5a351d06861866681ec
|