xref: /freebsd/sys/amd64/amd64/fpu.c (revision 2ad872c5794e4c26fdf6ed219ad3f09ca0d5304a)
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  *	$Id: npx.c,v 1.64 1998/12/14 19:16:17 bde Exp $
36  */
37 
38 #include "npx.h"
39 #if NNPX > 0
40 
41 #include "opt_debug_npx.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/malloc.h>
48 #include <sys/sysctl.h>
49 #include <sys/proc.h>
50 #ifdef NPX_DEBUG
51 #include <sys/syslog.h>
52 #endif
53 #include <sys/signalvar.h>
54 
55 #ifndef SMP
56 #include <machine/asmacros.h>
57 #endif
58 #include <machine/cputypes.h>
59 #include <machine/frame.h>
60 #include <machine/ipl.h>
61 #include <machine/md_var.h>
62 #include <machine/pcb.h>
63 #include <machine/psl.h>
64 #ifndef SMP
65 #include <machine/clock.h>
66 #endif
67 #include <machine/specialreg.h>
68 #include <machine/segments.h>
69 
70 #ifndef SMP
71 #include <i386/isa/icu.h>
72 #include <i386/isa/intr_machdep.h>
73 #include <i386/isa/isa.h>
74 #endif
75 #include <i386/isa/isa_device.h>
76 
77 /*
78  * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
79  */
80 
81 /* Configuration flags. */
82 #define	NPX_DISABLE_I586_OPTIMIZED_BCOPY	(1 << 0)
83 #define	NPX_DISABLE_I586_OPTIMIZED_BZERO	(1 << 1)
84 #define	NPX_DISABLE_I586_OPTIMIZED_COPYIO	(1 << 2)
85 
86 /* XXX - should be in header file. */
87 ointhand2_t	npxintr;
88 
89 #ifdef	__GNUC__
90 
91 #define	fldcw(addr)		__asm("fldcw %0" : : "m" (*(addr)))
92 #define	fnclex()		__asm("fnclex")
93 #define	fninit()		__asm("fninit")
94 #define	fnop()			__asm("fnop")
95 #define	fnsave(addr)		__asm __volatile("fnsave %0" : "=m" (*(addr)))
96 #define	fnstcw(addr)		__asm __volatile("fnstcw %0" : "=m" (*(addr)))
97 #define	fnstsw(addr)		__asm __volatile("fnstsw %0" : "=m" (*(addr)))
98 #define	fp_divide_by_0()	__asm("fldz; fld1; fdiv %st,%st(1); fnop")
99 #define	frstor(addr)		__asm("frstor %0" : : "m" (*(addr)))
100 #define	start_emulating()	__asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
101 				      : : "n" (CR0_TS) : "ax")
102 #define	stop_emulating()	__asm("clts")
103 
104 #else	/* not __GNUC__ */
105 
106 void	fldcw		__P((caddr_t addr));
107 void	fnclex		__P((void));
108 void	fninit		__P((void));
109 void	fnop		__P((void));
110 void	fnsave		__P((caddr_t addr));
111 void	fnstcw		__P((caddr_t addr));
112 void	fnstsw		__P((caddr_t addr));
113 void	fp_divide_by_0	__P((void));
114 void	frstor		__P((caddr_t addr));
115 void	start_emulating	__P((void));
116 void	stop_emulating	__P((void));
117 
118 #endif	/* __GNUC__ */
119 
120 typedef u_char bool_t;
121 
122 static	int	npxattach	__P((struct isa_device *dvp));
123 static	int	npxprobe	__P((struct isa_device *dvp));
124 static	int	npxprobe1	__P((struct isa_device *dvp));
125 static	long	timezero	__P((const char *funcname,
126 				     void (*func)(void *buf, size_t len)));
127 
128 struct	isa_driver npxdriver = {
129 	npxprobe, npxattach, "npx",
130 };
131 
132 int	hw_float;		/* XXX currently just alias for npx_exists */
133 
134 SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint,
135 	CTLFLAG_RD, &hw_float, 0,
136 	"Floatingpoint instructions executed in hardware");
137 
138 #ifndef SMP
139 static	u_int			npx0_imask = SWI_CLOCK_MASK;
140 static	struct gate_descriptor	npx_idt_probeintr;
141 static	int			npx_intrno;
142 static	volatile u_int		npx_intrs_while_probing;
143 static	volatile u_int		npx_traps_while_probing;
144 #endif
145 
146 static	bool_t			npx_ex16;
147 static	bool_t			npx_exists;
148 static	bool_t			npx_irq13;
149 
150 #ifndef SMP
151 /*
152  * Special interrupt handlers.  Someday intr0-intr15 will be used to count
153  * interrupts.  We'll still need a special exception 16 handler.  The busy
154  * latch stuff in probeintr() can be moved to npxprobe().
155  */
156 inthand_t probeintr;
157 __asm("								\n\
158 	.text							\n\
159 	.p2align 2,0x90						\n\
160 " __XSTRING(CNAME(probeintr)) ":				\n\
161 	ss							\n\
162 	incl	" __XSTRING(CNAME(npx_intrs_while_probing)) "	\n\
163 	pushl	%eax						\n\
164 	movb	$0x20,%al	# EOI (asm in strings loses cpp features) \n\
165 	outb	%al,$0xa0	# IO_ICU2			\n\
166 	outb	%al,$0x20	# IO_ICU1			\n\
167 	movb	$0,%al						\n\
168 	outb	%al,$0xf0	# clear BUSY# latch		\n\
169 	popl	%eax						\n\
170 	iret							\n\
171 ");
172 
173 inthand_t probetrap;
174 __asm("								\n\
175 	.text							\n\
176 	.p2align 2,0x90						\n\
177 " __XSTRING(CNAME(probetrap)) ":				\n\
178 	ss							\n\
179 	incl	" __XSTRING(CNAME(npx_traps_while_probing)) "	\n\
180 	fnclex							\n\
181 	iret							\n\
182 ");
183 #endif /* SMP */
184 
185 /*
186  * Probe routine.  Initialize cr0 to give correct behaviour for [f]wait
187  * whether the device exists or not (XXX should be elsewhere).  Set flags
188  * to tell npxattach() what to do.  Modify device struct if npx doesn't
189  * need to use interrupts.  Return 1 if device exists.
190  */
191 static int
192 npxprobe(dvp)
193 	struct isa_device *dvp;
194 {
195 #ifdef SMP
196 
197 	return npxprobe1(dvp);
198 
199 #else /* SMP */
200 
201 	int	result;
202 	u_long	save_eflags;
203 	u_char	save_icu1_mask;
204 	u_char	save_icu2_mask;
205 	struct	gate_descriptor save_idt_npxintr;
206 	struct	gate_descriptor save_idt_npxtrap;
207 	/*
208 	 * This routine is now just a wrapper for npxprobe1(), to install
209 	 * special npx interrupt and trap handlers, to enable npx interrupts
210 	 * and to disable other interrupts.  Someday isa_configure() will
211 	 * install suitable handlers and run with interrupts enabled so we
212 	 * won't need to do so much here.
213 	 */
214 	npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
215 	save_eflags = read_eflags();
216 	disable_intr();
217 	save_icu1_mask = inb(IO_ICU1 + 1);
218 	save_icu2_mask = inb(IO_ICU2 + 1);
219 	save_idt_npxintr = idt[npx_intrno];
220 	save_idt_npxtrap = idt[16];
221 	outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
222 	outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
223 	setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
224 	setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
225 	npx_idt_probeintr = idt[npx_intrno];
226 	enable_intr();
227 	result = npxprobe1(dvp);
228 	disable_intr();
229 	outb(IO_ICU1 + 1, save_icu1_mask);
230 	outb(IO_ICU2 + 1, save_icu2_mask);
231 	idt[npx_intrno] = save_idt_npxintr;
232 	idt[16] = save_idt_npxtrap;
233 	write_eflags(save_eflags);
234 	return (result);
235 
236 #endif /* SMP */
237 }
238 
239 static int
240 npxprobe1(dvp)
241 	struct isa_device *dvp;
242 {
243 #ifndef SMP
244 	u_short control;
245 	u_short status;
246 #endif
247 
248 	/*
249 	 * Partially reset the coprocessor, if any.  Some BIOS's don't reset
250 	 * it after a warm boot.
251 	 */
252 	outb(0xf1, 0);		/* full reset on some systems, NOP on others */
253 	outb(0xf0, 0);		/* clear BUSY# latch */
254 	/*
255 	 * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
256 	 * instructions.  We must set the CR0_MP bit and use the CR0_TS
257 	 * bit to control the trap, because setting the CR0_EM bit does
258 	 * not cause WAIT instructions to trap.  It's important to trap
259 	 * WAIT instructions - otherwise the "wait" variants of no-wait
260 	 * control instructions would degenerate to the "no-wait" variants
261 	 * after FP context switches but work correctly otherwise.  It's
262 	 * particularly important to trap WAITs when there is no NPX -
263 	 * otherwise the "wait" variants would always degenerate.
264 	 *
265 	 * Try setting CR0_NE to get correct error reporting on 486DX's.
266 	 * Setting it should fail or do nothing on lesser processors.
267 	 */
268 	load_cr0(rcr0() | CR0_MP | CR0_NE);
269 	/*
270 	 * But don't trap while we're probing.
271 	 */
272 	stop_emulating();
273 	/*
274 	 * Finish resetting the coprocessor, if any.  If there is an error
275 	 * pending, then we may get a bogus IRQ13, but probeintr() will handle
276 	 * it OK.  Bogus halts have never been observed, but we enabled
277 	 * IRQ13 and cleared the BUSY# latch early to handle them anyway.
278 	 */
279 	fninit();
280 
281 #ifdef SMP
282 
283 	/*
284 	 * Exception 16 MUST work for SMP.
285 	 */
286 	npx_irq13 = 0;
287 	npx_ex16 = hw_float = npx_exists = 1;
288 	dvp->id_irq = 0;	/* zap the interrupt */
289 	/*
290 	 * special return value to flag that we do not
291 	 * actually use any I/O registers
292 	 */
293 	return (-1);
294 
295 #else /* SMP */
296 
297 	/*
298 	 * Don't use fwait here because it might hang.
299 	 * Don't use fnop here because it usually hangs if there is no FPU.
300 	 */
301 	DELAY(1000);		/* wait for any IRQ13 */
302 #ifdef DIAGNOSTIC
303 	if (npx_intrs_while_probing != 0)
304 		printf("fninit caused %u bogus npx interrupt(s)\n",
305 		       npx_intrs_while_probing);
306 	if (npx_traps_while_probing != 0)
307 		printf("fninit caused %u bogus npx trap(s)\n",
308 		       npx_traps_while_probing);
309 #endif
310 	/*
311 	 * Check for a status of mostly zero.
312 	 */
313 	status = 0x5a5a;
314 	fnstsw(&status);
315 	if ((status & 0xb8ff) == 0) {
316 		/*
317 		 * Good, now check for a proper control word.
318 		 */
319 		control = 0x5a5a;
320 		fnstcw(&control);
321 		if ((control & 0x1f3f) == 0x033f) {
322 			hw_float = npx_exists = 1;
323 			/*
324 			 * We have an npx, now divide by 0 to see if exception
325 			 * 16 works.
326 			 */
327 			control &= ~(1 << 2);	/* enable divide by 0 trap */
328 			fldcw(&control);
329 			npx_traps_while_probing = npx_intrs_while_probing = 0;
330 			fp_divide_by_0();
331 			if (npx_traps_while_probing != 0) {
332 				/*
333 				 * Good, exception 16 works.
334 				 */
335 				npx_ex16 = 1;
336 				dvp->id_irq = 0;	/* zap the interrupt */
337 				/*
338 				 * special return value to flag that we do not
339 				 * actually use any I/O registers
340 				 */
341 				return (-1);
342 			}
343 			if (npx_intrs_while_probing != 0) {
344 				/*
345 				 * Bad, we are stuck with IRQ13.
346 				 */
347 				npx_irq13 = 1;
348 				/*
349 				 * npxattach would be too late to set npx0_imask.
350 				 */
351 				npx0_imask |= dvp->id_irq;
352 				return (IO_NPXSIZE);
353 			}
354 			/*
355 			 * Worse, even IRQ13 is broken.  Use emulator.
356 			 */
357 		}
358 	}
359 	/*
360 	 * Probe failed, but we want to get to npxattach to initialize the
361 	 * emulator and say that it has been installed.  XXX handle devices
362 	 * that aren't really devices better.
363 	 */
364 	dvp->id_irq = 0;
365 	/*
366 	 * special return value to flag that we do not
367 	 * actually use any I/O registers
368 	 */
369 	return (-1);
370 
371 #endif /* SMP */
372 }
373 
374 /*
375  * Attach routine - announce which it is, and wire into system
376  */
377 int
378 npxattach(dvp)
379 	struct isa_device *dvp;
380 {
381 	dvp->id_ointr = npxintr;
382 
383 	/* The caller has printed "irq 13" for the npx_irq13 case. */
384 	if (!npx_irq13) {
385 		printf("npx%d: ", dvp->id_unit);
386 		if (npx_ex16)
387 			printf("INT 16 interface\n");
388 #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE)
389 		else if (npx_exists) {
390 			printf("error reporting broken; using 387 emulator\n");
391 			hw_float = npx_exists = 0;
392 		} else
393 			printf("387 emulator\n");
394 #else
395 		else
396 			printf("no 387 emulator in kernel!\n");
397 #endif
398 	}
399 	npxinit(__INITIAL_NPXCW__);
400 
401 #ifdef I586_CPU
402 	if (cpu_class == CPUCLASS_586 && npx_ex16 &&
403 	    timezero("i586_bzero()", i586_bzero) <
404 	    timezero("bzero()", bzero) * 4 / 5) {
405 		if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) {
406 			bcopy_vector = i586_bcopy;
407 			ovbcopy_vector = i586_bcopy;
408 		}
409 		if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BZERO))
410 			bzero = i586_bzero;
411 		if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) {
412 			copyin_vector = i586_copyin;
413 			copyout_vector = i586_copyout;
414 		}
415 	}
416 #endif
417 
418 	return (1);		/* XXX unused */
419 }
420 
421 /*
422  * Initialize floating point unit.
423  */
424 void
425 npxinit(control)
426 	u_short control;
427 {
428 	struct save87 dummy;
429 
430 	if (!npx_exists)
431 		return;
432 	/*
433 	 * fninit has the same h/w bugs as fnsave.  Use the detoxified
434 	 * fnsave to throw away any junk in the fpu.  npxsave() initializes
435 	 * the fpu and sets npxproc = NULL as important side effects.
436 	 */
437 	npxsave(&dummy);
438 	stop_emulating();
439 	fldcw(&control);
440 	if (curpcb != NULL)
441 		fnsave(&curpcb->pcb_savefpu);
442 	start_emulating();
443 }
444 
445 /*
446  * Free coprocessor (if we have it).
447  */
448 void
449 npxexit(p)
450 	struct proc *p;
451 {
452 
453 	if (p == npxproc)
454 		npxsave(&curpcb->pcb_savefpu);
455 #ifdef NPX_DEBUG
456 	if (npx_exists) {
457 		u_int	masked_exceptions;
458 
459 		masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw
460 				    & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f;
461 		/*
462 		 * Log exceptions that would have trapped with the old
463 		 * control word (overflow, divide by 0, and invalid operand).
464 		 */
465 		if (masked_exceptions & 0x0d)
466 			log(LOG_ERR,
467 	"pid %d (%s) exited with masked floating point exceptions 0x%02x\n",
468 			    p->p_pid, p->p_comm, masked_exceptions);
469 	}
470 #endif
471 }
472 
473 /*
474  * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE.
475  *
476  * Clearing exceptions is necessary mainly to avoid IRQ13 bugs.  We now
477  * depend on longjmp() restoring a usable state.  Restoring the state
478  * or examining it might fail if we didn't clear exceptions.
479  *
480  * XXX there is no standard way to tell SIGFPE handlers about the error
481  * state.  The old interface:
482  *
483  *	void handler(int sig, int code, struct sigcontext *scp);
484  *
485  * is broken because it is non-ANSI and because the FP state is not in
486  * struct sigcontext.
487  *
488  * XXX the FP state is not preserved across signal handlers.  So signal
489  * handlers cannot afford to do FP unless they preserve the state or
490  * longjmp() out.  Both preserving the state and longjmp()ing may be
491  * destroyed by IRQ13 bugs.  Clearing FP exceptions is not an acceptable
492  * solution for signals other than SIGFPE.
493  */
494 void
495 npxintr(unit)
496 	int unit;
497 {
498 	int code;
499 	struct intrframe *frame;
500 
501 	if (npxproc == NULL || !npx_exists) {
502 		printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n",
503 		       npxproc, curproc, npx_exists);
504 		panic("npxintr from nowhere");
505 	}
506 	if (npxproc != curproc) {
507 		printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n",
508 		       npxproc, curproc, npx_exists);
509 		panic("npxintr from non-current process");
510 	}
511 
512 	outb(0xf0, 0);
513 	fnstsw(&curpcb->pcb_savefpu.sv_ex_sw);
514 	fnclex();
515 
516 	/*
517 	 * Pass exception to process.
518 	 */
519 	frame = (struct intrframe *)&unit;	/* XXX */
520 	if ((ISPL(frame->if_cs) == SEL_UPL) || (frame->if_eflags & PSL_VM)) {
521 		/*
522 		 * Interrupt is essentially a trap, so we can afford to call
523 		 * the SIGFPE handler (if any) as soon as the interrupt
524 		 * returns.
525 		 *
526 		 * XXX little or nothing is gained from this, and plenty is
527 		 * lost - the interrupt frame has to contain the trap frame
528 		 * (this is otherwise only necessary for the rescheduling trap
529 		 * in doreti, and the frame for that could easily be set up
530 		 * just before it is used).
531 		 */
532 		curproc->p_md.md_regs = (struct trapframe *)&frame->if_es;
533 #ifdef notyet
534 		/*
535 		 * Encode the appropriate code for detailed information on
536 		 * this exception.
537 		 */
538 		code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
539 #else
540 		code = 0;	/* XXX */
541 #endif
542 		trapsignal(curproc, SIGFPE, code);
543 	} else {
544 		/*
545 		 * Nested interrupt.  These losers occur when:
546 		 *	o an IRQ13 is bogusly generated at a bogus time, e.g.:
547 		 *		o immediately after an fnsave or frstor of an
548 		 *		  error state.
549 		 *		o a couple of 386 instructions after
550 		 *		  "fstpl _memvar" causes a stack overflow.
551 		 *	  These are especially nasty when combined with a
552 		 *	  trace trap.
553 		 *	o an IRQ13 occurs at the same time as another higher-
554 		 *	  priority interrupt.
555 		 *
556 		 * Treat them like a true async interrupt.
557 		 */
558 		psignal(curproc, SIGFPE);
559 	}
560 }
561 
562 /*
563  * Implement device not available (DNA) exception
564  *
565  * It would be better to switch FP context here (if curproc != npxproc)
566  * and not necessarily for every context switch, but it is too hard to
567  * access foreign pcb's.
568  */
569 int
570 npxdna()
571 {
572 	if (!npx_exists)
573 		return (0);
574 	if (npxproc != NULL) {
575 		printf("npxdna: npxproc = %p, curproc = %p\n",
576 		       npxproc, curproc);
577 		panic("npxdna");
578 	}
579 	stop_emulating();
580 	/*
581 	 * Record new context early in case frstor causes an IRQ13.
582 	 */
583 	npxproc = curproc;
584 	curpcb->pcb_savefpu.sv_ex_sw = 0;
585 	/*
586 	 * The following frstor may cause an IRQ13 when the state being
587 	 * restored has a pending error.  The error will appear to have been
588 	 * triggered by the current (npx) user instruction even when that
589 	 * instruction is a no-wait instruction that should not trigger an
590 	 * error (e.g., fnclex).  On at least one 486 system all of the
591 	 * no-wait instructions are broken the same as frstor, so our
592 	 * treatment does not amplify the breakage.  On at least one
593 	 * 386/Cyrix 387 system, fnclex works correctly while frstor and
594 	 * fnsave are broken, so our treatment breaks fnclex if it is the
595 	 * first FPU instruction after a context switch.
596 	 */
597 	frstor(&curpcb->pcb_savefpu);
598 
599 	return (1);
600 }
601 
602 /*
603  * Wrapper for fnsave instruction to handle h/w bugs.  If there is an error
604  * pending, then fnsave generates a bogus IRQ13 on some systems.  Force
605  * any IRQ13 to be handled immediately, and then ignore it.  This routine is
606  * often called at splhigh so it must not use many system services.  In
607  * particular, it's much easier to install a special handler than to
608  * guarantee that it's safe to use npxintr() and its supporting code.
609  */
610 void
611 npxsave(addr)
612 	struct save87 *addr;
613 {
614 #ifdef SMP
615 
616 	stop_emulating();
617 	fnsave(addr);
618 	/* fnop(); */
619 	start_emulating();
620 	npxproc = NULL;
621 
622 #else /* SMP */
623 
624 	u_char	icu1_mask;
625 	u_char	icu2_mask;
626 	u_char	old_icu1_mask;
627 	u_char	old_icu2_mask;
628 	struct gate_descriptor	save_idt_npxintr;
629 
630 	disable_intr();
631 	old_icu1_mask = inb(IO_ICU1 + 1);
632 	old_icu2_mask = inb(IO_ICU2 + 1);
633 	save_idt_npxintr = idt[npx_intrno];
634 	outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
635 	outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
636 	idt[npx_intrno] = npx_idt_probeintr;
637 	enable_intr();
638 	stop_emulating();
639 	fnsave(addr);
640 	fnop();
641 	start_emulating();
642 	npxproc = NULL;
643 	disable_intr();
644 	icu1_mask = inb(IO_ICU1 + 1);	/* masks may have changed */
645 	icu2_mask = inb(IO_ICU2 + 1);
646 	outb(IO_ICU1 + 1,
647 	     (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
648 	outb(IO_ICU2 + 1,
649 	     (icu2_mask & ~(npx0_imask >> 8))
650 	     | (old_icu2_mask & (npx0_imask >> 8)));
651 	idt[npx_intrno] = save_idt_npxintr;
652 	enable_intr();		/* back to usual state */
653 
654 #endif /* SMP */
655 }
656 
657 #ifdef I586_CPU
658 static long
659 timezero(funcname, func)
660 	const char *funcname;
661 	void (*func) __P((void *buf, size_t len));
662 
663 {
664 	void *buf;
665 #define	BUFSIZE		1000000
666 	long usec;
667 	struct timeval finish, start;
668 
669 	buf = malloc(BUFSIZE, M_TEMP, M_NOWAIT);
670 	if (buf == NULL)
671 		return (BUFSIZE);
672 	microtime(&start);
673 	(*func)(buf, BUFSIZE);
674 	microtime(&finish);
675 	usec = 1000000 * (finish.tv_sec - start.tv_sec) +
676 	    finish.tv_usec - start.tv_usec;
677 	if (usec <= 0)
678 		usec = 1;
679 	if (bootverbose)
680 		printf("%s bandwidth = %ld bytes/sec\n",
681 		    funcname, (long)(BUFSIZE * (int64_t)1000000 / usec));
682 	free(buf, M_TEMP);
683 	return (usec);
684 }
685 #endif /* I586_CPU */
686 
687 #endif /* NNPX > 0 */
688