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