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