Deleted Added
full compact
npx.c (126) npx.c (593)
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 *
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 * @(#)npx.c 7.2 (Berkeley) 5/12/91
35 *
36 * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
37 * -------------------- ----- ----------------------
38 * CURRENT PATCH LEVEL: 1 00154
39 * -------------------- ----- ----------------------
40 *
41 * 20 Apr 93 Bruce Evans New npx-0.5 code
42 * 23 May 93 Rodney W. Grimes Return a special value of -1 from
43 * the probe code to keep isa_config from
44 * printing out the I/O address when we
45 * are using trap 16 handling.
46 *
34 * from: @(#)npx.c 7.2 (Berkeley) 5/12/91
35 * $Id$
47 */
36 */
48static char rcsid[] = "$Header: /a/cvs/386BSD/src/sys.386bsd/i386/isa/npx.c,v 1.1.1.1 1993/06/12 14:58:00 rgrimes Exp $";
49
50#include "npx.h"
51#if NNPX > 0
52
53#include "param.h"
54#include "systm.h"
55#include "conf.h"
56#include "file.h"
57#include "proc.h"
58#include "machine/cpu.h"
59#include "machine/pcb.h"
60#include "machine/trap.h"
61#include "ioctl.h"
62#include "machine/specialreg.h"
63#include "i386/isa/icu.h"
64#include "i386/isa/isa_device.h"
65#include "i386/isa/isa.h"
66
67/*
68 * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
69 */
70
71#ifdef __GNUC__
72
73#define disable_intr() __asm("cli")
74#define enable_intr() __asm("sti")
75#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
76#define fnclex() __asm("fnclex")
77#define fninit() __asm("fninit")
78#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
79#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
80#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
81#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
82#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
83#define fwait() __asm("fwait")
84#define read_eflags() ({u_long ef; \
85 __asm("pushf; popl %0" : "=a" (ef)); \
86 ef; })
87#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
88 : : "n" (CR0_TS) : "ax")
89#define stop_emulating() __asm("clts")
90#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
91
92#else /* not __GNUC__ */
93
94void disable_intr __P((void));
95void enable_intr __P((void));
96void fldcw __P((caddr_t addr));
97void fnclex __P((void));
98void fninit __P((void));
99void fnsave __P((caddr_t addr));
100void fnstcw __P((caddr_t addr));
101void fnstsw __P((caddr_t addr));
102void fp_divide_by_0 __P((void));
103void frstor __P((caddr_t addr));
104void fwait __P((void));
105u_long read_eflags __P((void));
106void start_emulating __P((void));
107void stop_emulating __P((void));
108void write_eflags __P((u_long ef));
109
110#endif /* __GNUC__ */
111
112typedef u_char bool_t;
113
114extern struct gate_descriptor idt[];
115
116int npxdna __P((void));
117void npxexit __P((struct proc *p));
118void npxinit __P((u_int control));
119void npxintr __P((struct intrframe frame));
120void npxsave __P((struct save87 *addr));
121static int npxattach __P((struct isa_device *dvp));
122static int npxprobe __P((struct isa_device *dvp));
123static int npxprobe1 __P((struct isa_device *dvp));
124
125struct isa_driver npxdriver = {
126 npxprobe, npxattach, "npx",
127};
128
129u_int npx0mask;
130struct proc *npxproc;
131
132static bool_t npx_ex16;
133static bool_t npx_exists;
134static struct gate_descriptor npx_idt_probeintr;
135static int npx_intrno;
136static volatile u_int npx_intrs_while_probing;
137static bool_t npx_irq13;
138static volatile u_int npx_traps_while_probing;
139
140/*
141 * Special interrupt handlers. Someday intr0-intr15 will be used to count
142 * interrupts. We'll still need a special exception 16 handler. The busy
143 * latch stuff in probintr() can be moved to npxprobe().
144 */
145void probeintr(void);
146asm
147("
148 .text
149_probeintr:
150 ss
151 incl _npx_intrs_while_probing
152 pushl %eax
153 movb $0x20,%al /* EOI (asm in strings loses cpp features) */
154 outb %al,$0xa0 /* IO_ICU2 */
155 outb %al,$0x20 /* IO_ICU1 */
156 movb $0,%al
157 outb %al,$0xf0 /* clear BUSY# latch */
158 popl %eax
159 iret
160");
161
162void probetrap(void);
163asm
164("
165 .text
166_probetrap:
167 ss
168 incl _npx_traps_while_probing
169 fnclex
170 iret
171");
172
173/*
174 * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
175 * whether the device exists or not (XXX should be elsewhere). Set flags
176 * to tell npxattach() what to do. Modify device struct if npx doesn't
177 * need to use interrupts. Return 1 if device exists.
178 */
179static int
180npxprobe(dvp)
181 struct isa_device *dvp;
182{
183 int result;
184 u_long save_eflags;
185 u_char save_icu1_mask;
186 u_char save_icu2_mask;
187 struct gate_descriptor save_idt_npxintr;
188 struct gate_descriptor save_idt_npxtrap;
189 /*
190 * This routine is now just a wrapper for npxprobe1(), to install
191 * special npx interrupt and trap handlers, to enable npx interrupts
192 * and to disable other interrupts. Someday isa_configure() will
193 * install suitable handlers and run with interrupts enabled so we
194 * won't need to do so much here.
195 */
196 npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
197 save_eflags = read_eflags();
198 disable_intr();
199 save_icu1_mask = inb(IO_ICU1 + 1);
200 save_icu2_mask = inb(IO_ICU2 + 1);
201 save_idt_npxintr = idt[npx_intrno];
202 save_idt_npxtrap = idt[16];
203 outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
204 outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
205 setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
206 setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
207 npx_idt_probeintr = idt[npx_intrno];
208 enable_intr();
209 result = npxprobe1(dvp);
210 disable_intr();
211 outb(IO_ICU1 + 1, save_icu1_mask);
212 outb(IO_ICU2 + 1, save_icu2_mask);
213 idt[npx_intrno] = save_idt_npxintr;
214 idt[16] = save_idt_npxtrap;
215 write_eflags(save_eflags);
216 return (result);
217}
218
219static int
220npxprobe1(dvp)
221 struct isa_device *dvp;
222{
223 int control;
224 int status;
225#ifdef lint
226 npxintr();
227#endif
228 /*
229 * Partially reset the coprocessor, if any. Some BIOS's don't reset
230 * it after a warm boot.
231 */
232 outb(0xf1, 0); /* full reset on some systems, NOP on others */
233 outb(0xf0, 0); /* clear BUSY# latch */
234 /*
235 * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
236 * instructions. We must set the CR0_MP bit and use the CR0_TS
237 * bit to control the trap, because setting the CR0_EM bit does
238 * not cause WAIT instructions to trap. It's important to trap
239 * WAIT instructions - otherwise the "wait" variants of no-wait
240 * control instructions would degenerate to the "no-wait" variants
241 * after FP context switches but work correctly otherwise. It's
242 * particularly important to trap WAITs when there is no NPX -
243 * otherwise the "wait" variants would always degenerate.
244 *
245 * Try setting CR0_NE to get correct error reporting on 486DX's.
246 * Setting it should fail or do nothing on lesser processors.
247 */
248 load_cr0(rcr0() | CR0_MP | CR0_NE);
249 /*
250 * But don't trap while we're probing.
251 */
252 stop_emulating();
253 /*
254 * Finish resetting the coprocessor, if any. If there is an error
255 * pending, then we may get a bogus IRQ13, but probeintr() will handle
256 * it OK. Bogus halts have never been observed, but we enabled
257 * IRQ13 and cleared the BUSY# latch early to handle them anyway.
258 */
259 fninit();
260 DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
261#ifdef DIAGNOSTIC
262 if (npx_intrs_while_probing != 0)
263 printf("fninit caused %u bogus npx interrupt(s)\n",
264 npx_intrs_while_probing);
265 if (npx_traps_while_probing != 0)
266 printf("fninit caused %u bogus npx trap(s)\n",
267 npx_traps_while_probing);
268#endif
269 /*
270 * Check for a status of mostly zero.
271 */
272 status = 0x5a5a;
273 fnstsw(&status);
274 if ((status & 0xb8ff) == 0) {
275 /*
276 * Good, now check for a proper control word.
277 */
278 control = 0x5a5a;
279 fnstcw(&control);
280 if ((control & 0x1f3f) == 0x033f) {
281 npx_exists = 1;
282 /*
283 * We have an npx, now divide by 0 to see if exception
284 * 16 works.
285 */
286 control &= ~(1 << 2); /* enable divide by 0 trap */
287 fldcw(&control);
288 npx_traps_while_probing = npx_intrs_while_probing = 0;
289 fp_divide_by_0();
290 if (npx_traps_while_probing != 0) {
291 /*
292 * Good, exception 16 works.
293 */
294 npx_ex16 = 1;
295 dvp->id_irq = 0; /* zap the interrupt */
296 /*
297 * special return value to flag that we do not
298 * actually use any I/O registers
299 */
300 return (-1);
301 }
302 if (npx_intrs_while_probing != 0) {
303 /*
304 * Bad, we are stuck with IRQ13.
305 */
306 npx_irq13 = 1;
307 npx0mask = dvp->id_irq; /* npxattach too late */
308 return (IO_NPXSIZE);
309 }
310 /*
311 * Worse, even IRQ13 is broken. Use emulator.
312 */
313 }
314 }
315 /*
316 * Probe failed, but we want to get to npxattach to initialize the
317 * emulator and say that it has been installed. XXX handle devices
318 * that aren't really devices better.
319 */
320 dvp->id_irq = 0;
37
38#include "npx.h"
39#if NNPX > 0
40
41#include "param.h"
42#include "systm.h"
43#include "conf.h"
44#include "file.h"
45#include "proc.h"
46#include "machine/cpu.h"
47#include "machine/pcb.h"
48#include "machine/trap.h"
49#include "ioctl.h"
50#include "machine/specialreg.h"
51#include "i386/isa/icu.h"
52#include "i386/isa/isa_device.h"
53#include "i386/isa/isa.h"
54
55/*
56 * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
57 */
58
59#ifdef __GNUC__
60
61#define disable_intr() __asm("cli")
62#define enable_intr() __asm("sti")
63#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
64#define fnclex() __asm("fnclex")
65#define fninit() __asm("fninit")
66#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
67#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
68#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
69#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
70#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
71#define fwait() __asm("fwait")
72#define read_eflags() ({u_long ef; \
73 __asm("pushf; popl %0" : "=a" (ef)); \
74 ef; })
75#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
76 : : "n" (CR0_TS) : "ax")
77#define stop_emulating() __asm("clts")
78#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
79
80#else /* not __GNUC__ */
81
82void disable_intr __P((void));
83void enable_intr __P((void));
84void fldcw __P((caddr_t addr));
85void fnclex __P((void));
86void fninit __P((void));
87void fnsave __P((caddr_t addr));
88void fnstcw __P((caddr_t addr));
89void fnstsw __P((caddr_t addr));
90void fp_divide_by_0 __P((void));
91void frstor __P((caddr_t addr));
92void fwait __P((void));
93u_long read_eflags __P((void));
94void start_emulating __P((void));
95void stop_emulating __P((void));
96void write_eflags __P((u_long ef));
97
98#endif /* __GNUC__ */
99
100typedef u_char bool_t;
101
102extern struct gate_descriptor idt[];
103
104int npxdna __P((void));
105void npxexit __P((struct proc *p));
106void npxinit __P((u_int control));
107void npxintr __P((struct intrframe frame));
108void npxsave __P((struct save87 *addr));
109static int npxattach __P((struct isa_device *dvp));
110static int npxprobe __P((struct isa_device *dvp));
111static int npxprobe1 __P((struct isa_device *dvp));
112
113struct isa_driver npxdriver = {
114 npxprobe, npxattach, "npx",
115};
116
117u_int npx0mask;
118struct proc *npxproc;
119
120static bool_t npx_ex16;
121static bool_t npx_exists;
122static struct gate_descriptor npx_idt_probeintr;
123static int npx_intrno;
124static volatile u_int npx_intrs_while_probing;
125static bool_t npx_irq13;
126static volatile u_int npx_traps_while_probing;
127
128/*
129 * Special interrupt handlers. Someday intr0-intr15 will be used to count
130 * interrupts. We'll still need a special exception 16 handler. The busy
131 * latch stuff in probintr() can be moved to npxprobe().
132 */
133void probeintr(void);
134asm
135("
136 .text
137_probeintr:
138 ss
139 incl _npx_intrs_while_probing
140 pushl %eax
141 movb $0x20,%al /* EOI (asm in strings loses cpp features) */
142 outb %al,$0xa0 /* IO_ICU2 */
143 outb %al,$0x20 /* IO_ICU1 */
144 movb $0,%al
145 outb %al,$0xf0 /* clear BUSY# latch */
146 popl %eax
147 iret
148");
149
150void probetrap(void);
151asm
152("
153 .text
154_probetrap:
155 ss
156 incl _npx_traps_while_probing
157 fnclex
158 iret
159");
160
161/*
162 * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
163 * whether the device exists or not (XXX should be elsewhere). Set flags
164 * to tell npxattach() what to do. Modify device struct if npx doesn't
165 * need to use interrupts. Return 1 if device exists.
166 */
167static int
168npxprobe(dvp)
169 struct isa_device *dvp;
170{
171 int result;
172 u_long save_eflags;
173 u_char save_icu1_mask;
174 u_char save_icu2_mask;
175 struct gate_descriptor save_idt_npxintr;
176 struct gate_descriptor save_idt_npxtrap;
177 /*
178 * This routine is now just a wrapper for npxprobe1(), to install
179 * special npx interrupt and trap handlers, to enable npx interrupts
180 * and to disable other interrupts. Someday isa_configure() will
181 * install suitable handlers and run with interrupts enabled so we
182 * won't need to do so much here.
183 */
184 npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
185 save_eflags = read_eflags();
186 disable_intr();
187 save_icu1_mask = inb(IO_ICU1 + 1);
188 save_icu2_mask = inb(IO_ICU2 + 1);
189 save_idt_npxintr = idt[npx_intrno];
190 save_idt_npxtrap = idt[16];
191 outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
192 outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
193 setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
194 setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
195 npx_idt_probeintr = idt[npx_intrno];
196 enable_intr();
197 result = npxprobe1(dvp);
198 disable_intr();
199 outb(IO_ICU1 + 1, save_icu1_mask);
200 outb(IO_ICU2 + 1, save_icu2_mask);
201 idt[npx_intrno] = save_idt_npxintr;
202 idt[16] = save_idt_npxtrap;
203 write_eflags(save_eflags);
204 return (result);
205}
206
207static int
208npxprobe1(dvp)
209 struct isa_device *dvp;
210{
211 int control;
212 int status;
213#ifdef lint
214 npxintr();
215#endif
216 /*
217 * Partially reset the coprocessor, if any. Some BIOS's don't reset
218 * it after a warm boot.
219 */
220 outb(0xf1, 0); /* full reset on some systems, NOP on others */
221 outb(0xf0, 0); /* clear BUSY# latch */
222 /*
223 * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
224 * instructions. We must set the CR0_MP bit and use the CR0_TS
225 * bit to control the trap, because setting the CR0_EM bit does
226 * not cause WAIT instructions to trap. It's important to trap
227 * WAIT instructions - otherwise the "wait" variants of no-wait
228 * control instructions would degenerate to the "no-wait" variants
229 * after FP context switches but work correctly otherwise. It's
230 * particularly important to trap WAITs when there is no NPX -
231 * otherwise the "wait" variants would always degenerate.
232 *
233 * Try setting CR0_NE to get correct error reporting on 486DX's.
234 * Setting it should fail or do nothing on lesser processors.
235 */
236 load_cr0(rcr0() | CR0_MP | CR0_NE);
237 /*
238 * But don't trap while we're probing.
239 */
240 stop_emulating();
241 /*
242 * Finish resetting the coprocessor, if any. If there is an error
243 * pending, then we may get a bogus IRQ13, but probeintr() will handle
244 * it OK. Bogus halts have never been observed, but we enabled
245 * IRQ13 and cleared the BUSY# latch early to handle them anyway.
246 */
247 fninit();
248 DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
249#ifdef DIAGNOSTIC
250 if (npx_intrs_while_probing != 0)
251 printf("fninit caused %u bogus npx interrupt(s)\n",
252 npx_intrs_while_probing);
253 if (npx_traps_while_probing != 0)
254 printf("fninit caused %u bogus npx trap(s)\n",
255 npx_traps_while_probing);
256#endif
257 /*
258 * Check for a status of mostly zero.
259 */
260 status = 0x5a5a;
261 fnstsw(&status);
262 if ((status & 0xb8ff) == 0) {
263 /*
264 * Good, now check for a proper control word.
265 */
266 control = 0x5a5a;
267 fnstcw(&control);
268 if ((control & 0x1f3f) == 0x033f) {
269 npx_exists = 1;
270 /*
271 * We have an npx, now divide by 0 to see if exception
272 * 16 works.
273 */
274 control &= ~(1 << 2); /* enable divide by 0 trap */
275 fldcw(&control);
276 npx_traps_while_probing = npx_intrs_while_probing = 0;
277 fp_divide_by_0();
278 if (npx_traps_while_probing != 0) {
279 /*
280 * Good, exception 16 works.
281 */
282 npx_ex16 = 1;
283 dvp->id_irq = 0; /* zap the interrupt */
284 /*
285 * special return value to flag that we do not
286 * actually use any I/O registers
287 */
288 return (-1);
289 }
290 if (npx_intrs_while_probing != 0) {
291 /*
292 * Bad, we are stuck with IRQ13.
293 */
294 npx_irq13 = 1;
295 npx0mask = dvp->id_irq; /* npxattach too late */
296 return (IO_NPXSIZE);
297 }
298 /*
299 * Worse, even IRQ13 is broken. Use emulator.
300 */
301 }
302 }
303 /*
304 * Probe failed, but we want to get to npxattach to initialize the
305 * emulator and say that it has been installed. XXX handle devices
306 * that aren't really devices better.
307 */
308 dvp->id_irq = 0;
321 return (IO_NPXSIZE);
309 /*
310 * special return value to flag that we do not
311 * actually use any I/O registers
312 */
313 return (-1);
322}
323
324/*
325 * Attach routine - announce which it is, and wire into system
326 */
327int
328npxattach(dvp)
329 struct isa_device *dvp;
330{
314}
315
316/*
317 * Attach routine - announce which it is, and wire into system
318 */
319int
320npxattach(dvp)
321 struct isa_device *dvp;
322{
331 if (npx_ex16)
332 printf("npx%d: Errors reported via Exception 16\n",dvp->id_unit);
333 else if (npx_irq13)
334 printf("npx%d: Errors reported via IRQ 13\n",dvp->id_unit);
335 else if (npx_exists)
336 printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit);
337 else
338 printf("npx%d: 387 Emulator\n",dvp->id_unit);
323 if (!npx_ex16 && !npx_irq13) {
324 if (npx_exists)
325 printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit);
326 else
327 printf("npx%d: 387 Emulator\n",dvp->id_unit);
328 }
339 npxinit(__INITIAL_NPXCW__);
340 return (1); /* XXX unused */
341}
342
343/*
344 * Initialize floating point unit.
345 */
346void
347npxinit(control)
348 u_int control;
349{
350 struct save87 dummy;
351
352 if (!npx_exists)
353 return;
354 /*
355 * fninit has the same h/w bugs as fnsave. Use the detoxified
356 * fnsave to throw away any junk in the fpu. fnsave initializes
357 * the fpu and sets npxproc = NULL as important side effects.
358 */
359 npxsave(&dummy);
360 stop_emulating();
361 fldcw(&control);
362 if (curpcb != NULL)
363 fnsave(&curpcb->pcb_savefpu);
364 start_emulating();
365}
366
367/*
368 * Free coprocessor (if we have it).
369 */
370void
371npxexit(p)
372 struct proc *p;
373{
374
375 if (p == npxproc) {
376 start_emulating();
377 npxproc = NULL;
378 }
379}
380
381/*
382 * Record the FPU state and reinitialize it all except for the control word.
383 * Then generate a SIGFPE.
384 *
385 * Reinitializing the state allows naive SIGFPE handlers to longjmp without
386 * doing any fixups.
387 *
388 * XXX there is currently no way to pass the full error state to signal
389 * handlers, and if this is a nested interrupt there is no way to pass even
390 * a status code! So there is no way to have a non-naive SIGFPE handler. At
391 * best a handler could do an fninit followed by an fldcw of a static value.
392 * fnclex would be of little use because it would leave junk on the FPU stack.
393 * Returning from the handler would be even less safe than usual because
394 * IRQ13 exception handling makes exceptions even less precise than usual.
395 */
396void
397npxintr(frame)
398 struct intrframe frame;
399{
400 int code;
401
402 if (npxproc == NULL || !npx_exists) {
403 /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
404 printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
405 (u_long) npxproc, (u_long) curproc, npx_exists);
406 panic("npxintr from nowhere");
407 }
408 if (npxproc != curproc) {
409 printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
410 (u_long) npxproc, (u_long) curproc, npx_exists);
411 panic("npxintr from non-current process");
412 }
413 /*
414 * Save state. This does an implied fninit. It had better not halt
415 * the cpu or we'll hang.
416 */
417 outb(0xf0, 0);
418 fnsave(&curpcb->pcb_savefpu);
419 fwait();
420 /*
421 * Restore control word (was clobbered by fnsave).
422 */
423 fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
424 fwait();
425 /*
426 * Remember the exception status word and tag word. The current
427 * (almost fninit'ed) fpu state is in the fpu and the exception
428 * state just saved will soon be junk. However, the implied fninit
429 * doesn't change the error pointers or register contents, and we
430 * preserved the control word and will copy the status and tag
431 * words, so the complete exception state can be recovered.
432 */
433 curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
434 curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
435
436 /*
437 * Pass exception to process.
438 */
439 if (ISPL(frame.if_cs) == SEL_UPL) {
440 /*
441 * Interrupt is essentially a trap, so we can afford to call
442 * the SIGFPE handler (if any) as soon as the interrupt
443 * returns.
444 *
445 * XXX little or nothing is gained from this, and plenty is
446 * lost - the interrupt frame has to contain the trap frame
447 * (this is otherwise only necessary for the rescheduling trap
448 * in doreti, and the frame for that could easily be set up
449 * just before it is used).
450 */
451 curproc->p_regs = (int *)&frame.if_es;
452 curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */
453#ifdef notyet
454 /*
455 * Encode the appropriate code for detailed information on
456 * this exception.
457 */
458 code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
459#else
460 code = 0; /* XXX */
461#endif
462 trapsignal(curproc, SIGFPE, code);
463 curpcb->pcb_flags &= ~FM_TRAP;
464 } else {
465 /*
466 * Nested interrupt. These losers occur when:
467 * o an IRQ13 is bogusly generated at a bogus time, e.g.:
468 * o immediately after an fnsave or frstor of an
469 * error state.
470 * o a couple of 386 instructions after
471 * "fstpl _memvar" causes a stack overflow.
472 * These are especially nasty when combined with a
473 * trace trap.
474 * o an IRQ13 occurs at the same time as another higher-
475 * priority interrupt.
476 *
477 * Treat them like a true async interrupt.
478 */
479 psignal(npxproc, SIGFPE);
480 }
481}
482
483/*
484 * Implement device not available (DNA) exception
485 *
486 * It would be better to switch FP context here (only). This would require
487 * saving the state in the proc table instead of in the pcb.
488 */
489int
490npxdna()
491{
492 if (!npx_exists)
493 return (0);
494 if (npxproc != NULL) {
495 printf("npxdna: npxproc = %lx, curproc = %lx\n",
496 (u_long) npxproc, (u_long) curproc);
497 panic("npxdna");
498 }
499 stop_emulating();
500 /*
501 * Record new context early in case frstor causes an IRQ13.
502 */
503 npxproc = curproc;
504 /*
505 * The following frstor may cause an IRQ13 when the state being
506 * restored has a pending error. The error will appear to have been
507 * triggered by the current (npx) user instruction even when that
508 * instruction is a no-wait instruction that should not trigger an
509 * error (e.g., fnclex). On at least one 486 system all of the
510 * no-wait instructions are broken the same as frstor, so our
511 * treatment does not amplify the breakage. On at least one
512 * 386/Cyrix 387 system, fnclex works correctly while frstor and
513 * fnsave are broken, so our treatment breaks fnclex if it is the
514 * first FPU instruction after a context switch.
515 */
516 frstor(&curpcb->pcb_savefpu);
517
518 return (1);
519}
520
521/*
522 * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
523 * pending, then fnsave generates a bogus IRQ13 on some systems. Force
524 * any IRQ13 to be handled immediately, and then ignore it. This routine is
525 * often called at splhigh so it must not use many system services. In
526 * particular, it's much easier to install a special handler than to
527 * guarantee that it's safe to use npxintr() and its supporting code.
528 */
529void
530npxsave(addr)
531 struct save87 *addr;
532{
533 u_char icu1_mask;
534 u_char icu2_mask;
535 u_char old_icu1_mask;
536 u_char old_icu2_mask;
537 struct gate_descriptor save_idt_npxintr;
538
539 disable_intr();
540 old_icu1_mask = inb(IO_ICU1 + 1);
541 old_icu2_mask = inb(IO_ICU2 + 1);
542 save_idt_npxintr = idt[npx_intrno];
543 outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask));
544 outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8));
545 idt[npx_intrno] = npx_idt_probeintr;
546 enable_intr();
547 stop_emulating();
548 fnsave(addr);
549 fwait();
550 start_emulating();
551 npxproc = NULL;
552 disable_intr();
553 icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
554 icu2_mask = inb(IO_ICU2 + 1);
555 outb(IO_ICU1 + 1,
556 (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask));
557 outb(IO_ICU2 + 1,
558 (icu2_mask & ~(npx0mask >> 8))
559 | (old_icu2_mask & (npx0mask >> 8)));
560 idt[npx_intrno] = save_idt_npxintr;
561 enable_intr(); /* back to usual state */
562}
563
564#endif /* NNPX > 0 */
329 npxinit(__INITIAL_NPXCW__);
330 return (1); /* XXX unused */
331}
332
333/*
334 * Initialize floating point unit.
335 */
336void
337npxinit(control)
338 u_int control;
339{
340 struct save87 dummy;
341
342 if (!npx_exists)
343 return;
344 /*
345 * fninit has the same h/w bugs as fnsave. Use the detoxified
346 * fnsave to throw away any junk in the fpu. fnsave initializes
347 * the fpu and sets npxproc = NULL as important side effects.
348 */
349 npxsave(&dummy);
350 stop_emulating();
351 fldcw(&control);
352 if (curpcb != NULL)
353 fnsave(&curpcb->pcb_savefpu);
354 start_emulating();
355}
356
357/*
358 * Free coprocessor (if we have it).
359 */
360void
361npxexit(p)
362 struct proc *p;
363{
364
365 if (p == npxproc) {
366 start_emulating();
367 npxproc = NULL;
368 }
369}
370
371/*
372 * Record the FPU state and reinitialize it all except for the control word.
373 * Then generate a SIGFPE.
374 *
375 * Reinitializing the state allows naive SIGFPE handlers to longjmp without
376 * doing any fixups.
377 *
378 * XXX there is currently no way to pass the full error state to signal
379 * handlers, and if this is a nested interrupt there is no way to pass even
380 * a status code! So there is no way to have a non-naive SIGFPE handler. At
381 * best a handler could do an fninit followed by an fldcw of a static value.
382 * fnclex would be of little use because it would leave junk on the FPU stack.
383 * Returning from the handler would be even less safe than usual because
384 * IRQ13 exception handling makes exceptions even less precise than usual.
385 */
386void
387npxintr(frame)
388 struct intrframe frame;
389{
390 int code;
391
392 if (npxproc == NULL || !npx_exists) {
393 /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
394 printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
395 (u_long) npxproc, (u_long) curproc, npx_exists);
396 panic("npxintr from nowhere");
397 }
398 if (npxproc != curproc) {
399 printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
400 (u_long) npxproc, (u_long) curproc, npx_exists);
401 panic("npxintr from non-current process");
402 }
403 /*
404 * Save state. This does an implied fninit. It had better not halt
405 * the cpu or we'll hang.
406 */
407 outb(0xf0, 0);
408 fnsave(&curpcb->pcb_savefpu);
409 fwait();
410 /*
411 * Restore control word (was clobbered by fnsave).
412 */
413 fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
414 fwait();
415 /*
416 * Remember the exception status word and tag word. The current
417 * (almost fninit'ed) fpu state is in the fpu and the exception
418 * state just saved will soon be junk. However, the implied fninit
419 * doesn't change the error pointers or register contents, and we
420 * preserved the control word and will copy the status and tag
421 * words, so the complete exception state can be recovered.
422 */
423 curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
424 curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
425
426 /*
427 * Pass exception to process.
428 */
429 if (ISPL(frame.if_cs) == SEL_UPL) {
430 /*
431 * Interrupt is essentially a trap, so we can afford to call
432 * the SIGFPE handler (if any) as soon as the interrupt
433 * returns.
434 *
435 * XXX little or nothing is gained from this, and plenty is
436 * lost - the interrupt frame has to contain the trap frame
437 * (this is otherwise only necessary for the rescheduling trap
438 * in doreti, and the frame for that could easily be set up
439 * just before it is used).
440 */
441 curproc->p_regs = (int *)&frame.if_es;
442 curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */
443#ifdef notyet
444 /*
445 * Encode the appropriate code for detailed information on
446 * this exception.
447 */
448 code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
449#else
450 code = 0; /* XXX */
451#endif
452 trapsignal(curproc, SIGFPE, code);
453 curpcb->pcb_flags &= ~FM_TRAP;
454 } else {
455 /*
456 * Nested interrupt. These losers occur when:
457 * o an IRQ13 is bogusly generated at a bogus time, e.g.:
458 * o immediately after an fnsave or frstor of an
459 * error state.
460 * o a couple of 386 instructions after
461 * "fstpl _memvar" causes a stack overflow.
462 * These are especially nasty when combined with a
463 * trace trap.
464 * o an IRQ13 occurs at the same time as another higher-
465 * priority interrupt.
466 *
467 * Treat them like a true async interrupt.
468 */
469 psignal(npxproc, SIGFPE);
470 }
471}
472
473/*
474 * Implement device not available (DNA) exception
475 *
476 * It would be better to switch FP context here (only). This would require
477 * saving the state in the proc table instead of in the pcb.
478 */
479int
480npxdna()
481{
482 if (!npx_exists)
483 return (0);
484 if (npxproc != NULL) {
485 printf("npxdna: npxproc = %lx, curproc = %lx\n",
486 (u_long) npxproc, (u_long) curproc);
487 panic("npxdna");
488 }
489 stop_emulating();
490 /*
491 * Record new context early in case frstor causes an IRQ13.
492 */
493 npxproc = curproc;
494 /*
495 * The following frstor may cause an IRQ13 when the state being
496 * restored has a pending error. The error will appear to have been
497 * triggered by the current (npx) user instruction even when that
498 * instruction is a no-wait instruction that should not trigger an
499 * error (e.g., fnclex). On at least one 486 system all of the
500 * no-wait instructions are broken the same as frstor, so our
501 * treatment does not amplify the breakage. On at least one
502 * 386/Cyrix 387 system, fnclex works correctly while frstor and
503 * fnsave are broken, so our treatment breaks fnclex if it is the
504 * first FPU instruction after a context switch.
505 */
506 frstor(&curpcb->pcb_savefpu);
507
508 return (1);
509}
510
511/*
512 * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
513 * pending, then fnsave generates a bogus IRQ13 on some systems. Force
514 * any IRQ13 to be handled immediately, and then ignore it. This routine is
515 * often called at splhigh so it must not use many system services. In
516 * particular, it's much easier to install a special handler than to
517 * guarantee that it's safe to use npxintr() and its supporting code.
518 */
519void
520npxsave(addr)
521 struct save87 *addr;
522{
523 u_char icu1_mask;
524 u_char icu2_mask;
525 u_char old_icu1_mask;
526 u_char old_icu2_mask;
527 struct gate_descriptor save_idt_npxintr;
528
529 disable_intr();
530 old_icu1_mask = inb(IO_ICU1 + 1);
531 old_icu2_mask = inb(IO_ICU2 + 1);
532 save_idt_npxintr = idt[npx_intrno];
533 outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask));
534 outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8));
535 idt[npx_intrno] = npx_idt_probeintr;
536 enable_intr();
537 stop_emulating();
538 fnsave(addr);
539 fwait();
540 start_emulating();
541 npxproc = NULL;
542 disable_intr();
543 icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
544 icu2_mask = inb(IO_ICU2 + 1);
545 outb(IO_ICU1 + 1,
546 (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask));
547 outb(IO_ICU2 + 1,
548 (icu2_mask & ~(npx0mask >> 8))
549 | (old_icu2_mask & (npx0mask >> 8)));
550 idt[npx_intrno] = save_idt_npxintr;
551 enable_intr(); /* back to usual state */
552}
553
554#endif /* NNPX > 0 */