]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/fpu.c
d2a966ffab79c7f675ba6d9ce42e93f7c844058f
[apple/xnu.git] / osfmk / i386 / fpu.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*
31 * @OSF_COPYRIGHT@
32 */
33 /*
34 * Mach Operating System
35 * Copyright (c) 1992-1990 Carnegie Mellon University
36 * All Rights Reserved.
37 *
38 * Permission to use, copy, modify and distribute this software and its
39 * documentation is hereby granted, provided that both the copyright
40 * notice and this permission notice appear in all copies of the
41 * software, derivative works or modified versions, and any portions
42 * thereof, and that both notices appear in supporting documentation.
43 *
44 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
45 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
46 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
47 *
48 * Carnegie Mellon requests users of this software to return to
49 *
50 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
51 * School of Computer Science
52 * Carnegie Mellon University
53 * Pittsburgh PA 15213-3890
54 *
55 * any improvements or extensions that they make and grant Carnegie Mellon
56 * the rights to redistribute these changes.
57 */
58 /*
59 */
60
61 #include <platforms.h>
62
63 #include <mach/exception_types.h>
64 #include <mach/i386/thread_status.h>
65 #include <mach/i386/fp_reg.h>
66
67 #include <kern/mach_param.h>
68 #include <kern/processor.h>
69 #include <kern/thread.h>
70 #include <kern/zalloc.h>
71 #include <kern/misc_protos.h>
72 #include <kern/spl.h>
73 #include <kern/assert.h>
74
75 #include <i386/thread.h>
76 #include <i386/fpu.h>
77 #include <i386/trap.h>
78 #include <i386/pio.h>
79 #include <i386/cpuid.h>
80 #include <i386/misc_protos.h>
81
82 #if 0
83 #include <i386/ipl.h>
84 extern int curr_ipl;
85 #define ASSERT_IPL(L) \
86 { \
87 if (curr_ipl != L) { \
88 printf("IPL is %d, expected %d\n", curr_ipl, L); \
89 panic("fpu: wrong ipl"); \
90 } \
91 }
92 #else
93 #define ASSERT_IPL(L)
94 #endif
95
96 int fp_kind = FP_387; /* 80387 present */
97 zone_t ifps_zone; /* zone for FPU save area */
98
99 #define clear_fpu() \
100 { \
101 set_ts(); \
102 }
103
104 #define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
105
106 /* Forward */
107
108 extern void fpinit(void);
109 extern void fp_save(
110 thread_t thr_act);
111 extern void fp_load(
112 thread_t thr_act);
113
114 /*
115 * Look for FPU and initialize it.
116 * Called on each CPU.
117 */
118 void
119 init_fpu(void)
120 {
121 unsigned short status, control;
122
123 /*
124 * Check for FPU by initializing it,
125 * then trying to read the correct bit patterns from
126 * the control and status registers.
127 */
128 set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | CR0_NE); /* allow use of FPU */
129
130 fninit();
131 status = fnstsw();
132 fnstcw(&control);
133
134 if ((status & 0xff) == 0 &&
135 (control & 0x103f) == 0x3f)
136 {
137 fp_kind = FP_387; /* assume we have a 387 compatible instruction set */
138 /* Use FPU save/restore instructions if available */
139 if (cpuid_features() & CPUID_FEATURE_FXSR) {
140 fp_kind = FP_FXSR;
141 set_cr4(get_cr4() | CR4_FXS);
142 printf("Enabling XMM register save/restore");
143 /* And allow SIMD instructions if present */
144 if (cpuid_features() & CPUID_FEATURE_SSE) {
145 printf(" and SSE/SSE2");
146 set_cr4(get_cr4() | CR4_XMM);
147 }
148 printf(" opcodes\n");
149 }
150
151 /*
152 * Trap wait instructions. Turn off FPU for now.
153 */
154 set_cr0(get_cr0() | CR0_TS | CR0_MP);
155 }
156 else
157 {
158 /*
159 * NO FPU.
160 */
161 fp_kind = FP_NO;
162 set_cr0(get_cr0() | CR0_EM);
163 }
164 }
165
166 /*
167 * Initialize FP handling.
168 */
169 void
170 fpu_module_init(void)
171 {
172 ifps_zone = zinit(sizeof(struct i386_fpsave_state),
173 THREAD_MAX * sizeof(struct i386_fpsave_state),
174 THREAD_CHUNK * sizeof(struct i386_fpsave_state),
175 "i386 fpsave state");
176 }
177
178 /*
179 * Free a FPU save area.
180 * Called only when thread terminating - no locking necessary.
181 */
182 void
183 fpu_free(fps)
184 struct i386_fpsave_state *fps;
185 {
186 ASSERT_IPL(SPL0);
187 zfree(ifps_zone, fps);
188 }
189
190 /*
191 * Set the floating-point state for a thread based
192 * on the FXSave formatted data. This is basically
193 * the same as fpu_set_state except it uses the
194 * expanded data structure.
195 * If the thread is not the current thread, it is
196 * not running (held). Locking needed against
197 * concurrent fpu_set_state or fpu_get_state.
198 */
199 kern_return_t
200 fpu_set_fxstate(
201 thread_t thr_act,
202 struct i386_float_state *state)
203 {
204 register pcb_t pcb;
205 register struct i386_fpsave_state *ifps;
206 register struct i386_fpsave_state *new_ifps;
207
208 ASSERT_IPL(SPL0);
209 if (fp_kind == FP_NO)
210 return KERN_FAILURE;
211
212 if (state->fpkind != FP_FXSR) {
213 /* strange if this happens, but in case someone builds one of these manually... */
214 return fpu_set_state(thr_act, state);
215 }
216
217 assert(thr_act != THREAD_NULL);
218 pcb = thr_act->machine.pcb;
219
220 if (state->initialized == 0) {
221 /*
222 * new FPU state is 'invalid'.
223 * Deallocate the fp state if it exists.
224 */
225 simple_lock(&pcb->lock);
226 ifps = pcb->ims.ifps;
227 pcb->ims.ifps = 0;
228 simple_unlock(&pcb->lock);
229
230 if (ifps != 0) {
231 zfree(ifps_zone, ifps);
232 }
233 }
234 else {
235 /*
236 * Valid state. Allocate the fp state if there is none.
237 */
238
239 new_ifps = 0;
240 Retry:
241 simple_lock(&pcb->lock);
242 ifps = pcb->ims.ifps;
243 if (ifps == 0) {
244 if (new_ifps == 0) {
245 simple_unlock(&pcb->lock);
246 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
247 assert(ALIGNED(new_ifps,16));
248 goto Retry;
249 }
250 ifps = new_ifps;
251 new_ifps = 0;
252 bzero((char *)ifps, sizeof *ifps);
253 pcb->ims.ifps = ifps;
254 }
255
256 /*
257 * now copy over the new data.
258 */
259 bcopy((char *)&state->hw_state[0], (char *)&ifps->fx_save_state, sizeof(struct i386_fx_save));
260 ifps->fp_save_flavor = FP_FXSR;
261 simple_unlock(&pcb->lock);
262 if (new_ifps != 0)
263 zfree(ifps_zone, ifps);
264 }
265
266 return KERN_SUCCESS;
267 }
268
269 /*
270 * Get the floating-point state for a thread.
271 * If the thread is not the current thread, it is
272 * not running (held). Locking needed against
273 * concurrent fpu_set_state or fpu_get_state.
274 */
275 kern_return_t
276 fpu_get_fxstate(
277 thread_t thr_act,
278 register struct i386_float_state *state)
279 {
280 register pcb_t pcb;
281 register struct i386_fpsave_state *ifps;
282
283 ASSERT_IPL(SPL0);
284 if (fp_kind == FP_NO) {
285 return KERN_FAILURE;
286 } else if (fp_kind == FP_387) {
287 return fpu_get_state(thr_act, state);
288 }
289
290 assert(thr_act != THREAD_NULL);
291 pcb = thr_act->machine.pcb;
292
293 simple_lock(&pcb->lock);
294 ifps = pcb->ims.ifps;
295 if (ifps == 0) {
296 /*
297 * No valid floating-point state.
298 */
299 simple_unlock(&pcb->lock);
300 bzero((char *)state, sizeof(struct i386_float_state));
301 return KERN_SUCCESS;
302 }
303
304 /* Make sure we`ve got the latest fp state info */
305 /* If the live fpu state belongs to our target */
306 if (thr_act == current_thread())
307 {
308 clear_ts();
309 fp_save(thr_act);
310 clear_fpu();
311 }
312
313 state->fpkind = fp_kind;
314 state->exc_status = 0;
315 state->initialized = ifps->fp_valid;
316 bcopy( (char *)&ifps->fx_save_state, (char *)&state->hw_state[0], sizeof(struct i386_fx_save));
317
318 simple_unlock(&pcb->lock);
319
320 return KERN_SUCCESS;
321 }
322
323 /*
324 * Set the floating-point state for a thread.
325 * If the thread is not the current thread, it is
326 * not running (held). Locking needed against
327 * concurrent fpu_set_state or fpu_get_state.
328 */
329 kern_return_t
330 fpu_set_state(
331 thread_t thr_act,
332 struct i386_float_state *state)
333 {
334 register pcb_t pcb;
335 register struct i386_fpsave_state *ifps;
336 register struct i386_fpsave_state *new_ifps;
337
338 ASSERT_IPL(SPL0);
339 if (fp_kind == FP_NO)
340 return KERN_FAILURE;
341
342 assert(thr_act != THREAD_NULL);
343 pcb = thr_act->machine.pcb;
344
345 if (state->initialized == 0) {
346 /*
347 * new FPU state is 'invalid'.
348 * Deallocate the fp state if it exists.
349 */
350 simple_lock(&pcb->lock);
351 ifps = pcb->ims.ifps;
352 pcb->ims.ifps = 0;
353 simple_unlock(&pcb->lock);
354
355 if (ifps != 0) {
356 zfree(ifps_zone, ifps);
357 }
358 }
359 else {
360 /*
361 * Valid state. Allocate the fp state if there is none.
362 */
363 register struct i386_fp_save *user_fp_state;
364 register struct i386_fp_regs *user_fp_regs;
365
366 user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
367 user_fp_regs = (struct i386_fp_regs *)
368 &state->hw_state[sizeof(struct i386_fp_save)];
369
370 new_ifps = 0;
371 Retry:
372 simple_lock(&pcb->lock);
373 ifps = pcb->ims.ifps;
374 if (ifps == 0) {
375 if (new_ifps == 0) {
376 simple_unlock(&pcb->lock);
377 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
378 assert(ALIGNED(new_ifps,16));
379 goto Retry;
380 }
381 ifps = new_ifps;
382 new_ifps = 0;
383 bzero((char *)ifps, sizeof *ifps); // zero ALL fields first
384 pcb->ims.ifps = ifps;
385 }
386
387 /*
388 * Ensure that reserved parts of the environment are 0.
389 */
390 bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
391
392 ifps->fp_save_state.fp_control = user_fp_state->fp_control;
393 ifps->fp_save_state.fp_status = user_fp_state->fp_status;
394 ifps->fp_save_state.fp_tag = user_fp_state->fp_tag;
395 ifps->fp_save_state.fp_eip = user_fp_state->fp_eip;
396 ifps->fp_save_state.fp_cs = user_fp_state->fp_cs;
397 ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode;
398 ifps->fp_save_state.fp_dp = user_fp_state->fp_dp;
399 ifps->fp_save_state.fp_ds = user_fp_state->fp_ds;
400 ifps->fp_regs = *user_fp_regs;
401 ifps->fp_save_flavor = FP_387;
402 simple_unlock(&pcb->lock);
403 if (new_ifps != 0)
404 zfree(ifps_zone, ifps);
405 }
406
407 return KERN_SUCCESS;
408 }
409
410 /*
411 * Get the floating-point state for a thread.
412 * If the thread is not the current thread, it is
413 * not running (held). Locking needed against
414 * concurrent fpu_set_state or fpu_get_state.
415 */
416 kern_return_t
417 fpu_get_state(
418 thread_t thr_act,
419 register struct i386_float_state *state)
420 {
421 register pcb_t pcb;
422 register struct i386_fpsave_state *ifps;
423
424 ASSERT_IPL(SPL0);
425 if (fp_kind == FP_NO)
426 return KERN_FAILURE;
427
428 assert(thr_act != THREAD_NULL);
429 pcb = thr_act->machine.pcb;
430
431 simple_lock(&pcb->lock);
432 ifps = pcb->ims.ifps;
433 if (ifps == 0) {
434 /*
435 * No valid floating-point state.
436 */
437 simple_unlock(&pcb->lock);
438 bzero((char *)state, sizeof(struct i386_float_state));
439 return KERN_SUCCESS;
440 }
441
442 /* Make sure we`ve got the latest fp state info */
443 /* If the live fpu state belongs to our target */
444 if (thr_act == current_thread())
445 {
446 clear_ts();
447 fp_save(thr_act);
448 clear_fpu();
449 }
450
451 state->fpkind = fp_kind;
452 state->exc_status = 0;
453
454 {
455 register struct i386_fp_save *user_fp_state;
456 register struct i386_fp_regs *user_fp_regs;
457
458 state->initialized = ifps->fp_valid;
459
460 user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
461 user_fp_regs = (struct i386_fp_regs *)
462 &state->hw_state[sizeof(struct i386_fp_save)];
463
464 /*
465 * Ensure that reserved parts of the environment are 0.
466 */
467 bzero((char *)user_fp_state, sizeof(struct i386_fp_save));
468
469 user_fp_state->fp_control = ifps->fp_save_state.fp_control;
470 user_fp_state->fp_status = ifps->fp_save_state.fp_status;
471 user_fp_state->fp_tag = ifps->fp_save_state.fp_tag;
472 user_fp_state->fp_eip = ifps->fp_save_state.fp_eip;
473 user_fp_state->fp_cs = ifps->fp_save_state.fp_cs;
474 user_fp_state->fp_opcode = ifps->fp_save_state.fp_opcode;
475 user_fp_state->fp_dp = ifps->fp_save_state.fp_dp;
476 user_fp_state->fp_ds = ifps->fp_save_state.fp_ds;
477 *user_fp_regs = ifps->fp_regs;
478 }
479 simple_unlock(&pcb->lock);
480
481 return KERN_SUCCESS;
482 }
483
484 /*
485 * Initialize FPU.
486 *
487 * Raise exceptions for:
488 * invalid operation
489 * divide by zero
490 * overflow
491 *
492 * Use 53-bit precision.
493 */
494 void
495 fpinit(void)
496 {
497 unsigned short control;
498
499 ASSERT_IPL(SPL0);
500 clear_ts();
501 fninit();
502 fnstcw(&control);
503 control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
504 control |= (FPC_PC_53 | /* Set precision */
505 FPC_RC_RN | /* round-to-nearest */
506 FPC_ZE | /* Suppress zero-divide */
507 FPC_OE | /* and overflow */
508 FPC_UE | /* underflow */
509 FPC_IE | /* Allow NaNQs and +-INF */
510 FPC_DE | /* Allow denorms as operands */
511 FPC_PE); /* No trap for precision loss */
512 fldcw(control);
513 }
514
515 /*
516 * Coprocessor not present.
517 */
518
519 void
520 fpnoextflt(void)
521 {
522 /*
523 * Enable FPU use.
524 */
525 ASSERT_IPL(SPL0);
526 clear_ts();
527
528 /*
529 * Load this thread`s state into the FPU.
530 */
531 fp_load(current_thread());
532 }
533
534 /*
535 * FPU overran end of segment.
536 * Re-initialize FPU. Floating point state is not valid.
537 */
538
539 void
540 fpextovrflt(void)
541 {
542 register thread_t thr_act = current_thread();
543 register pcb_t pcb;
544 register struct i386_fpsave_state *ifps;
545
546 /*
547 * This is a non-recoverable error.
548 * Invalidate the thread`s FPU state.
549 */
550 pcb = thr_act->machine.pcb;
551 simple_lock(&pcb->lock);
552 ifps = pcb->ims.ifps;
553 pcb->ims.ifps = 0;
554 simple_unlock(&pcb->lock);
555
556 /*
557 * Re-initialize the FPU.
558 */
559 clear_ts();
560 fninit();
561
562 /*
563 * And disable access.
564 */
565 clear_fpu();
566
567 if (ifps)
568 zfree(ifps_zone, ifps);
569
570 /*
571 * Raise exception.
572 */
573 i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
574 /*NOTREACHED*/
575 }
576
577 /*
578 * FPU error. Called by AST.
579 */
580
581 void
582 fpexterrflt(void)
583 {
584 register thread_t thr_act = current_thread();
585
586 ASSERT_IPL(SPL0);
587 /*
588 * Save the FPU state and turn off the FPU.
589 */
590 fp_save(thr_act);
591
592 /*
593 * Raise FPU exception.
594 * Locking not needed on pcb->ims.ifps,
595 * since thread is running.
596 */
597 i386_exception(EXC_ARITHMETIC,
598 EXC_I386_EXTERR,
599 thr_act->machine.pcb->ims.ifps->fp_save_state.fp_status);
600 /*NOTREACHED*/
601 }
602
603 /*
604 * Save FPU state.
605 *
606 * Locking not needed:
607 * . if called from fpu_get_state, pcb already locked.
608 * . if called from fpnoextflt or fp_intr, we are single-cpu
609 * . otherwise, thread is running.
610 */
611 void
612 fp_save(
613 thread_t thr_act)
614 {
615 register pcb_t pcb = thr_act->machine.pcb;
616 register struct i386_fpsave_state *ifps = pcb->ims.ifps;
617 if (ifps != 0 && !ifps->fp_valid) {
618 /* registers are in FPU */
619 ifps->fp_valid = TRUE;
620 ifps->fp_save_flavor = FP_387;
621 if (FXSAFE()) {
622 fxsave(&ifps->fx_save_state); // save the SSE2/Fp state in addition is enabled
623 ifps->fp_save_flavor = FP_FXSR;
624 }
625 fnsave(&ifps->fp_save_state); // also update the old save area for now...
626 }
627 }
628
629 /*
630 * Restore FPU state from PCB.
631 *
632 * Locking not needed; always called on the current thread.
633 */
634
635 void
636 fp_load(
637 thread_t thr_act)
638 {
639 register pcb_t pcb = thr_act->machine.pcb;
640 register struct i386_fpsave_state *ifps;
641
642 ASSERT_IPL(SPL0);
643 ifps = pcb->ims.ifps;
644 if (ifps == 0) {
645 ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
646 assert(ALIGNED(ifps,16));
647 bzero((char *)ifps, sizeof *ifps);
648 pcb->ims.ifps = ifps;
649 fpinit();
650 #if 1
651 /*
652 * I'm not sure this is needed. Does the fpu regenerate the interrupt in
653 * frstor or not? Without this code we may miss some exceptions, with it
654 * we might send too many exceptions.
655 */
656 } else if (ifps->fp_valid == 2) {
657 /* delayed exception pending */
658
659 ifps->fp_valid = TRUE;
660 clear_fpu();
661 /*
662 * Raise FPU exception.
663 * Locking not needed on pcb->ims.ifps,
664 * since thread is running.
665 */
666 i386_exception(EXC_ARITHMETIC,
667 EXC_I386_EXTERR,
668 thr_act->machine.pcb->ims.ifps->fp_save_state.fp_status);
669 /*NOTREACHED*/
670 #endif
671 } else {
672 if (ifps->fp_save_flavor == FP_FXSR) fxrstor(&ifps->fx_save_state);
673 else frstor(ifps->fp_save_state);
674 }
675 ifps->fp_valid = FALSE; /* in FPU */
676 }
677
678
679 /*
680 * Allocate and initialize FP state for current thread.
681 * Don't load state.
682 *
683 * Locking not needed; always called on the current thread.
684 */
685 void
686 fp_state_alloc(void)
687 {
688 pcb_t pcb = current_thread()->machine.pcb;
689 struct i386_fpsave_state *ifps;
690
691 ifps = (struct i386_fpsave_state *)zalloc(ifps_zone);
692 assert(ALIGNED(ifps,16));
693 bzero((char *)ifps, sizeof *ifps);
694 pcb->ims.ifps = ifps;
695
696 ifps->fp_valid = TRUE;
697 ifps->fp_save_state.fp_control = (0x037f
698 & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC))
699 | (FPC_PC_53|FPC_IC_AFF);
700 ifps->fp_save_state.fp_status = 0;
701 ifps->fp_save_state.fp_tag = 0xffff; /* all empty */
702 ifps->fx_save_state.fx_control = ifps->fp_save_state.fp_control;
703 ifps->fx_save_state.fx_status = ifps->fp_save_state.fp_status;
704 ifps->fx_save_state.fx_tag = 0x00;
705 ifps->fx_save_state.fx_MXCSR = 0x1f80;
706
707 }
708
709
710 /*
711 * fpflush(thread_t)
712 * Flush the current act's state, if needed
713 * (used by thread_terminate_self to ensure fp faults
714 * aren't satisfied by overly general trap code in the
715 * context of the reaper thread)
716 */
717 void
718 fpflush(__unused thread_t thr_act)
719 {
720 /* not needed on MP x86s; fp not lazily evaluated */
721 }
722
723
724 /*
725 * Handle a coprocessor error interrupt on the AT386.
726 * This comes in on line 5 of the slave PIC at SPL1.
727 */
728
729 void
730 fpintr(void)
731 {
732 spl_t s;
733 thread_t thr_act = current_thread();
734
735 ASSERT_IPL(SPL1);
736 /*
737 * Turn off the extended 'busy' line.
738 */
739 outb(0xf0, 0);
740
741 /*
742 * Save the FPU context to the thread using it.
743 */
744 clear_ts();
745 fp_save(thr_act);
746 fninit();
747 clear_fpu();
748
749 /*
750 * Since we are running on the interrupt stack, we must
751 * signal the thread to take the exception when we return
752 * to user mode. Use an AST to do this.
753 *
754 * Don`t set the thread`s AST field. If the thread is
755 * descheduled before it takes the AST, it will notice
756 * the FPU error when it reloads its FPU state.
757 */
758 s = splsched();
759 mp_disable_preemption();
760 ast_on(AST_I386_FP);
761 mp_enable_preemption();
762 splx(s);
763 }