FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/npx.c
1 /*-
2 * Copyright (c) 1990 William Jolitz.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * from: @(#)npx.c 7.2 (Berkeley) 5/12/91
35 * $FreeBSD$
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 .type " __XSTRING(CNAME(probeintr)) ",@function \n\
161 " __XSTRING(CNAME(probeintr)) ": \n\
162 ss \n\
163 incl " __XSTRING(CNAME(npx_intrs_while_probing)) " \n\
164 pushl %eax \n\
165 movb $0x20,%al # EOI (asm in strings loses cpp features) \n\
166 outb %al,$0xa0 # IO_ICU2 \n\
167 outb %al,$0x20 # IO_ICU1 \n\
168 movb $0,%al \n\
169 outb %al,$0xf0 # clear BUSY# latch \n\
170 popl %eax \n\
171 iret \n\
172 ");
173
174 inthand_t probetrap;
175 __asm(" \n\
176 .text \n\
177 .p2align 2,0x90 \n\
178 .type " __XSTRING(CNAME(probetrap)) ",@function \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 */
193 static int
194 npxprobe(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
241 static int
242 npxprobe1(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 */
379 int
380 npxattach(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 */
426 void
427 npxinit(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 */
450 void
451 npxexit(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 */
496 void
497 npxintr(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 */
571 int
572 npxdna()
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 */
612 void
613 npxsave(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
660 static long
661 timezero(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 */
Cache object: 57e65cb2ed0d55794015c827c0875240
|