]>
git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/fpu.c
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
29 * Mach Operating System
30 * Copyright (c) 1992-1990 Carnegie Mellon University
31 * All Rights Reserved.
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
43 * Carnegie Mellon requests users of this software to return to
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
57 #include <platforms.h>
59 #include <mach/exception_types.h>
60 #include <mach/i386/thread_status.h>
61 #include <mach/i386/fp_reg.h>
63 #include <kern/mach_param.h>
64 #include <kern/thread.h>
65 #include <kern/zalloc.h>
66 #include <kern/misc_protos.h>
68 #include <kern/assert.h>
70 #include <i386/thread.h>
72 #include <i386/trap.h>
74 #include <i386/cpuid.h>
75 #include <i386/misc_protos.h>
80 #define ASSERT_IPL(L) \
82 if (curr_ipl != L) { \
83 printf("IPL is %d, expected %d\n", curr_ipl, L); \
84 panic("fpu: wrong ipl"); \
91 int fp_kind
= FP_387
; /* 80387 present */
92 zone_t ifps_zone
; /* zone for FPU save area */
95 volatile thread_act_t fp_act
= THR_ACT_NULL
;
96 /* thread whose state is in FPU */
97 /* always THR_ACT_NULL if emulating FPU */
98 volatile thread_act_t fp_intr_act
= THR_ACT_NULL
;
101 #define clear_fpu() \
104 fp_act = THR_ACT_NULL; \
107 #else /* NCPUS > 1 */
108 #define clear_fpu() \
115 #define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
119 extern void fpinit(void);
121 thread_act_t thr_act
);
123 thread_act_t thr_act
);
126 * Look for FPU and initialize it.
127 * Called on each CPU.
132 unsigned short status
, control
;
135 * Check for FPU by initializing it,
136 * then trying to read the correct bit patterns from
137 * the control and status registers.
139 set_cr0(get_cr0() & ~(CR0_EM
|CR0_TS
)); /* allow use of FPU */
145 if ((status
& 0xff) == 0 &&
146 (control
& 0x103f) == 0x3f)
148 fp_kind
= FP_387
; /* assume we have a 387 compatible instruction set */
149 /* Use FPU save/restore instructions if available */
150 if (cpuid_features() & CPUID_FEATURE_FXSR
) {
152 set_cr4(get_cr4() | CR4_FXS
);
153 printf("Enabling XMM register save/restore");
154 /* And allow SIMD instructions if present */
155 if (cpuid_features() & CPUID_FEATURE_SSE
) {
156 printf(" and SSE/SSE2");
157 set_cr4(get_cr4() | CR4_XMM
);
159 printf(" opcodes\n");
163 * Trap wait instructions. Turn off FPU for now.
165 set_cr0(get_cr0() | CR0_TS
| CR0_MP
);
173 set_cr0(get_cr0() | CR0_EM
);
178 * Initialize FP handling.
181 fpu_module_init(void)
183 ifps_zone
= zinit(sizeof(struct i386_fpsave_state
),
184 THREAD_MAX
* sizeof(struct i386_fpsave_state
),
185 THREAD_CHUNK
* sizeof(struct i386_fpsave_state
),
186 "i386 fpsave state");
190 * Free a FPU save area.
191 * Called only when thread terminating - no locking necessary.
195 struct i386_fpsave_state
*fps
;
199 if ((fp_act
!= THR_ACT_NULL
) && (fp_act
->mact
.pcb
->ims
.ifps
== fps
)) {
201 * Make sure we don't get FPU interrupts later for
206 /* Mark it free and disable access */
209 #endif /* NCPUS == 1 */
210 zfree(ifps_zone
, (vm_offset_t
) fps
);
214 * Set the floating-point state for a thread based
215 * on the FXSave formatted data. This is basically
216 * the same as fpu_set_state except it uses the
217 * expanded data structure.
218 * If the thread is not the current thread, it is
219 * not running (held). Locking needed against
220 * concurrent fpu_set_state or fpu_get_state.
224 thread_act_t thr_act
,
225 struct i386_float_state
*state
)
228 register struct i386_fpsave_state
*ifps
;
229 register struct i386_fpsave_state
*new_ifps
;
232 if (fp_kind
== FP_NO
)
235 if (state
->fpkind
!= FP_FXSR
) {
236 /* strange if this happens, but in case someone builds one of these manually... */
237 return fpu_set_state(thr_act
, state
);
240 assert(thr_act
!= THR_ACT_NULL
);
241 pcb
= thr_act
->mact
.pcb
;
246 * If this thread`s state is in the FPU,
247 * discard it; we are replacing the entire
250 if (fp_act
== thr_act
) {
251 fwait(); /* wait for possible interrupt */
252 clear_fpu(); /* no state in FPU */
256 if (state
->initialized
== 0) {
258 * new FPU state is 'invalid'.
259 * Deallocate the fp state if it exists.
261 simple_lock(&pcb
->lock
);
262 ifps
= pcb
->ims
.ifps
;
264 simple_unlock(&pcb
->lock
);
267 zfree(ifps_zone
, (vm_offset_t
) ifps
);
272 * Valid state. Allocate the fp state if there is none.
277 simple_lock(&pcb
->lock
);
278 ifps
= pcb
->ims
.ifps
;
281 simple_unlock(&pcb
->lock
);
282 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
283 assert(ALIGNED(new_ifps
,16));
288 bzero((char *)ifps
, sizeof *ifps
);
289 pcb
->ims
.ifps
= ifps
;
293 * now copy over the new data.
295 bcopy((char *)&state
->hw_state
[0], (char *)&ifps
->fx_save_state
, sizeof(struct i386_fx_save
));
296 ifps
->fp_save_flavor
= FP_FXSR
;
297 simple_unlock(&pcb
->lock
);
299 zfree(ifps_zone
, (vm_offset_t
) ifps
);
306 * Get the floating-point state for a thread.
307 * If the thread is not the current thread, it is
308 * not running (held). Locking needed against
309 * concurrent fpu_set_state or fpu_get_state.
313 thread_act_t thr_act
,
314 register struct i386_float_state
*state
)
317 register struct i386_fpsave_state
*ifps
;
320 if (fp_kind
== FP_NO
)
323 assert(thr_act
!= THR_ACT_NULL
);
324 pcb
= thr_act
->mact
.pcb
;
326 simple_lock(&pcb
->lock
);
327 ifps
= pcb
->ims
.ifps
;
330 * No valid floating-point state.
332 simple_unlock(&pcb
->lock
);
333 bzero((char *)state
, sizeof(struct i386_float_state
));
337 /* Make sure we`ve got the latest fp state info */
338 /* If the live fpu state belongs to our target */
340 if (thr_act
== fp_act
)
342 if (thr_act
== current_act())
350 state
->fpkind
= fp_kind
;
351 state
->exc_status
= 0;
352 state
->initialized
= ifps
->fp_valid
;
353 bcopy( (char *)&ifps
->fx_save_state
, (char *)&state
->hw_state
[0], sizeof(struct i386_fx_save
));
355 simple_unlock(&pcb
->lock
);
361 * Set the floating-point state for a thread.
362 * If the thread is not the current thread, it is
363 * not running (held). Locking needed against
364 * concurrent fpu_set_state or fpu_get_state.
368 thread_act_t thr_act
,
369 struct i386_float_state
*state
)
372 register struct i386_fpsave_state
*ifps
;
373 register struct i386_fpsave_state
*new_ifps
;
376 if (fp_kind
== FP_NO
)
379 assert(thr_act
!= THR_ACT_NULL
);
380 pcb
= thr_act
->mact
.pcb
;
385 * If this thread`s state is in the FPU,
386 * discard it; we are replacing the entire
389 if (fp_act
== thr_act
) {
390 fwait(); /* wait for possible interrupt */
391 clear_fpu(); /* no state in FPU */
395 if (state
->initialized
== 0) {
397 * new FPU state is 'invalid'.
398 * Deallocate the fp state if it exists.
400 simple_lock(&pcb
->lock
);
401 ifps
= pcb
->ims
.ifps
;
403 simple_unlock(&pcb
->lock
);
406 zfree(ifps_zone
, (vm_offset_t
) ifps
);
411 * Valid state. Allocate the fp state if there is none.
413 register struct i386_fp_save
*user_fp_state
;
414 register struct i386_fp_regs
*user_fp_regs
;
416 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
417 user_fp_regs
= (struct i386_fp_regs
*)
418 &state
->hw_state
[sizeof(struct i386_fp_save
)];
422 simple_lock(&pcb
->lock
);
423 ifps
= pcb
->ims
.ifps
;
426 simple_unlock(&pcb
->lock
);
427 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
428 assert(ALIGNED(new_ifps
,16));
433 bzero((char *)ifps
, sizeof *ifps
); // zero ALL fields first
434 pcb
->ims
.ifps
= ifps
;
438 * Ensure that reserved parts of the environment are 0.
440 bzero((char *)&ifps
->fp_save_state
, sizeof(struct i386_fp_save
));
442 ifps
->fp_save_state
.fp_control
= user_fp_state
->fp_control
;
443 ifps
->fp_save_state
.fp_status
= user_fp_state
->fp_status
;
444 ifps
->fp_save_state
.fp_tag
= user_fp_state
->fp_tag
;
445 ifps
->fp_save_state
.fp_eip
= user_fp_state
->fp_eip
;
446 ifps
->fp_save_state
.fp_cs
= user_fp_state
->fp_cs
;
447 ifps
->fp_save_state
.fp_opcode
= user_fp_state
->fp_opcode
;
448 ifps
->fp_save_state
.fp_dp
= user_fp_state
->fp_dp
;
449 ifps
->fp_save_state
.fp_ds
= user_fp_state
->fp_ds
;
450 ifps
->fp_regs
= *user_fp_regs
;
451 ifps
->fp_save_flavor
= FP_387
;
452 simple_unlock(&pcb
->lock
);
454 zfree(ifps_zone
, (vm_offset_t
) ifps
);
461 * Get the floating-point state for a thread.
462 * If the thread is not the current thread, it is
463 * not running (held). Locking needed against
464 * concurrent fpu_set_state or fpu_get_state.
468 thread_act_t thr_act
,
469 register struct i386_float_state
*state
)
472 register struct i386_fpsave_state
*ifps
;
475 if (fp_kind
== FP_NO
)
478 assert(thr_act
!= THR_ACT_NULL
);
479 pcb
= thr_act
->mact
.pcb
;
481 simple_lock(&pcb
->lock
);
482 ifps
= pcb
->ims
.ifps
;
485 * No valid floating-point state.
487 simple_unlock(&pcb
->lock
);
488 bzero((char *)state
, sizeof(struct i386_float_state
));
492 /* Make sure we`ve got the latest fp state info */
493 /* If the live fpu state belongs to our target */
495 if (thr_act
== fp_act
)
497 if (thr_act
== current_act())
505 state
->fpkind
= fp_kind
;
506 state
->exc_status
= 0;
509 register struct i386_fp_save
*user_fp_state
;
510 register struct i386_fp_regs
*user_fp_regs
;
512 state
->initialized
= ifps
->fp_valid
;
514 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
515 user_fp_regs
= (struct i386_fp_regs
*)
516 &state
->hw_state
[sizeof(struct i386_fp_save
)];
519 * Ensure that reserved parts of the environment are 0.
521 bzero((char *)user_fp_state
, sizeof(struct i386_fp_save
));
523 user_fp_state
->fp_control
= ifps
->fp_save_state
.fp_control
;
524 user_fp_state
->fp_status
= ifps
->fp_save_state
.fp_status
;
525 user_fp_state
->fp_tag
= ifps
->fp_save_state
.fp_tag
;
526 user_fp_state
->fp_eip
= ifps
->fp_save_state
.fp_eip
;
527 user_fp_state
->fp_cs
= ifps
->fp_save_state
.fp_cs
;
528 user_fp_state
->fp_opcode
= ifps
->fp_save_state
.fp_opcode
;
529 user_fp_state
->fp_dp
= ifps
->fp_save_state
.fp_dp
;
530 user_fp_state
->fp_ds
= ifps
->fp_save_state
.fp_ds
;
531 *user_fp_regs
= ifps
->fp_regs
;
533 simple_unlock(&pcb
->lock
);
541 * Raise exceptions for:
546 * Use 53-bit precision.
551 unsigned short control
;
557 control
&= ~(FPC_PC
|FPC_RC
); /* Clear precision & rounding control */
558 control
|= (FPC_PC_53
| /* Set precision */
559 FPC_RC_RN
| /* round-to-nearest */
560 FPC_ZE
| /* Suppress zero-divide */
561 FPC_OE
| /* and overflow */
562 FPC_UE
| /* underflow */
563 FPC_IE
| /* Allow NaNQs and +-INF */
564 FPC_DE
| /* Allow denorms as operands */
565 FPC_PE
); /* No trap for precision loss */
570 * Coprocessor not present.
584 * If this thread`s state is in the FPU, we are done.
586 if (fp_act
== current_act())
589 /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
590 * here if the current fpu instruction generates an error.
594 * If another thread`s state is in the FPU, save it.
596 if (fp_act
!= THR_ACT_NULL
) {
601 * Give this thread the FPU.
603 fp_act
= current_act();
605 #endif /* NCPUS == 1 */
608 * Load this thread`s state into the FPU.
610 fp_load(current_act());
614 * FPU overran end of segment.
615 * Re-initialize FPU. Floating point state is not valid.
621 register thread_act_t thr_act
= current_act();
623 register struct i386_fpsave_state
*ifps
;
628 * Is exception for the currently running thread?
630 if (fp_act
!= thr_act
) {
632 panic("fpextovrflt");
637 * This is a non-recoverable error.
638 * Invalidate the thread`s FPU state.
640 pcb
= thr_act
->mact
.pcb
;
641 simple_lock(&pcb
->lock
);
642 ifps
= pcb
->ims
.ifps
;
644 simple_unlock(&pcb
->lock
);
647 * Re-initialize the FPU.
653 * And disable access.
658 zfree(ifps_zone
, (vm_offset_t
) ifps
);
663 i386_exception(EXC_BAD_ACCESS
, VM_PROT_READ
|VM_PROT_EXECUTE
, 0);
668 * FPU error. Called by AST.
674 register thread_act_t thr_act
= current_act();
679 * Since FPU errors only occur on ESC or WAIT instructions,
680 * the current thread should own the FPU. If it didn`t,
681 * we should have gotten the task-switched interrupt first.
683 if (fp_act
!= THR_ACT_NULL
) {
684 panic("fpexterrflt");
689 * Check if we got a context switch between the interrupt and the AST
690 * This can happen if the interrupt arrived after the FPU AST was
691 * checked. In this case, raise the exception in fp_load when this
692 * thread next time uses the FPU. Remember exception condition in
693 * fp_valid (extended boolean 2).
695 if (fp_intr_act
!= thr_act
) {
696 if (fp_intr_act
== THR_ACT_NULL
) {
697 panic("fpexterrflt: fp_intr_act == THR_ACT_NULL");
700 fp_intr_act
->mact
.pcb
->ims
.ifps
->fp_valid
= 2;
701 fp_intr_act
= THR_ACT_NULL
;
704 fp_intr_act
= THR_ACT_NULL
;
705 #else /* NCPUS == 1 */
707 * Save the FPU state and turn off the FPU.
710 #endif /* NCPUS == 1 */
713 * Raise FPU exception.
714 * Locking not needed on pcb->ims.ifps,
715 * since thread is running.
717 i386_exception(EXC_ARITHMETIC
,
719 thr_act
->mact
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
726 * Locking not needed:
727 * . if called from fpu_get_state, pcb already locked.
728 * . if called from fpnoextflt or fp_intr, we are single-cpu
729 * . otherwise, thread is running.
733 thread_act_t thr_act
)
735 register pcb_t pcb
= thr_act
->mact
.pcb
;
736 register struct i386_fpsave_state
*ifps
= pcb
->ims
.ifps
;
737 if (ifps
!= 0 && !ifps
->fp_valid
) {
738 /* registers are in FPU */
739 ifps
->fp_valid
= TRUE
;
740 ifps
->fp_save_flavor
= FP_387
;
742 fxsave(&ifps
->fx_save_state
); // save the SSE2/Fp state in addition is enabled
743 ifps
->fp_save_flavor
= FP_FXSR
;
745 fnsave(&ifps
->fp_save_state
); // also update the old save area for now...
750 * Restore FPU state from PCB.
752 * Locking not needed; always called on the current thread.
757 thread_act_t thr_act
)
759 register pcb_t pcb
= thr_act
->mact
.pcb
;
760 register struct i386_fpsave_state
*ifps
;
763 ifps
= pcb
->ims
.ifps
;
765 ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
766 assert(ALIGNED(ifps
,16));
767 bzero((char *)ifps
, sizeof *ifps
);
768 pcb
->ims
.ifps
= ifps
;
772 * I'm not sure this is needed. Does the fpu regenerate the interrupt in
773 * frstor or not? Without this code we may miss some exceptions, with it
774 * we might send too many exceptions.
776 } else if (ifps
->fp_valid
== 2) {
777 /* delayed exception pending */
779 ifps
->fp_valid
= TRUE
;
782 * Raise FPU exception.
783 * Locking not needed on pcb->ims.ifps,
784 * since thread is running.
786 i386_exception(EXC_ARITHMETIC
,
788 thr_act
->mact
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
792 if (ifps
->fp_save_flavor
== FP_FXSR
) fxrstor(&ifps
->fx_save_state
);
793 else frstor(ifps
->fp_save_state
);
795 ifps
->fp_valid
= FALSE
; /* in FPU */
800 * Allocate and initialize FP state for current thread.
803 * Locking not needed; always called on the current thread.
808 pcb_t pcb
= current_act()->mact
.pcb
;
809 struct i386_fpsave_state
*ifps
;
811 ifps
= (struct i386_fpsave_state
*)zalloc(ifps_zone
);
812 assert(ALIGNED(ifps
,16));
813 bzero((char *)ifps
, sizeof *ifps
);
814 pcb
->ims
.ifps
= ifps
;
816 ifps
->fp_valid
= TRUE
;
817 ifps
->fp_save_state
.fp_control
= (0x037f
818 & ~(FPC_IM
|FPC_ZM
|FPC_OM
|FPC_PC
))
819 | (FPC_PC_53
|FPC_IC_AFF
);
820 ifps
->fp_save_state
.fp_status
= 0;
821 ifps
->fp_save_state
.fp_tag
= 0xffff; /* all empty */
822 ifps
->fx_save_state
.fx_control
= ifps
->fp_save_state
.fp_control
;
823 ifps
->fx_save_state
.fx_status
= ifps
->fp_save_state
.fp_status
;
824 ifps
->fx_save_state
.fx_tag
= 0x00;
825 ifps
->fx_save_state
.fx_MXCSR
= 0x1f80;
831 * fpflush(thread_act_t)
832 * Flush the current act's state, if needed
833 * (used by thread_terminate_self to ensure fp faults
834 * aren't satisfied by overly general trap code in the
835 * context of the reaper thread)
838 fpflush(thread_act_t thr_act
)
841 if (fp_act
&& thr_act
== fp_act
) {
847 /* not needed on MP x86s; fp not lazily evaluated */
853 * Handle a coprocessor error interrupt on the AT386.
854 * This comes in on line 5 of the slave PIC at SPL1.
861 thread_act_t thr_act
= current_act();
865 * Turn off the extended 'busy' line.
870 * Save the FPU context to the thread using it.
873 if (fp_act
== THR_ACT_NULL
) {
874 printf("fpintr: FPU not belonging to anyone!\n");
881 if (fp_act
!= thr_act
) {
883 * FPU exception is for a different thread.
884 * When that thread again uses the FPU an exception will be
885 * raised in fp_load. Remember the condition in fp_valid (== 2).
889 fp_act
->mact
.pcb
->ims
.ifps
->fp_valid
= 2;
892 /* leave fp_intr_act THR_ACT_NULL */
895 if (fp_intr_act
!= THR_ACT_NULL
)
896 panic("fp_intr: already caught intr");
897 fp_intr_act
= thr_act
;
898 #endif /* NCPUS == 1 */
906 * Since we are running on the interrupt stack, we must
907 * signal the thread to take the exception when we return
908 * to user mode. Use an AST to do this.
910 * Don`t set the thread`s AST field. If the thread is
911 * descheduled before it takes the AST, it will notice
912 * the FPU error when it reloads its FPU state.
915 mp_disable_preemption();
917 mp_enable_preemption();