]>
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 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
26 * Mach Operating System
27 * Copyright (c) 1992-1990 Carnegie Mellon University
28 * All Rights Reserved.
30 * Permission to use, copy, modify and distribute this software and its
31 * documentation is hereby granted, provided that both the copyright
32 * notice and this permission notice appear in all copies of the
33 * software, derivative works or modified versions, and any portions
34 * thereof, and that both notices appear in supporting documentation.
36 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
37 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
38 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
40 * Carnegie Mellon requests users of this software to return to
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
54 #include <platforms.h>
56 #include <mach/exception_types.h>
57 #include <mach/i386/thread_status.h>
58 #include <mach/i386/fp_reg.h>
60 #include <kern/mach_param.h>
61 #include <kern/thread.h>
62 #include <kern/zalloc.h>
63 #include <kern/misc_protos.h>
65 #include <kern/assert.h>
67 #include <i386/thread.h>
69 #include <i386/trap.h>
71 #include <i386/cpuid.h>
72 #include <i386/misc_protos.h>
77 #define ASSERT_IPL(L) \
79 if (curr_ipl != L) { \
80 printf("IPL is %d, expected %d\n", curr_ipl, L); \
81 panic("fpu: wrong ipl"); \
88 int fp_kind
= FP_387
; /* 80387 present */
89 zone_t ifps_zone
; /* zone for FPU save area */
92 volatile thread_act_t fp_act
= THR_ACT_NULL
;
93 /* thread whose state is in FPU */
94 /* always THR_ACT_NULL if emulating FPU */
95 volatile thread_act_t fp_intr_act
= THR_ACT_NULL
;
101 fp_act = THR_ACT_NULL; \
104 #else /* NCPUS > 1 */
105 #define clear_fpu() \
112 #define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
116 extern void fpinit(void);
118 thread_act_t thr_act
);
120 thread_act_t thr_act
);
123 * Look for FPU and initialize it.
124 * Called on each CPU.
129 unsigned short status
, control
;
132 * Check for FPU by initializing it,
133 * then trying to read the correct bit patterns from
134 * the control and status registers.
136 set_cr0(get_cr0() & ~(CR0_EM
|CR0_TS
)); /* allow use of FPU */
142 if ((status
& 0xff) == 0 &&
143 (control
& 0x103f) == 0x3f)
145 fp_kind
= FP_387
; /* assume we have a 387 compatible instruction set */
146 /* Use FPU save/restore instructions if available */
147 if (cpuid_features() & CPUID_FEATURE_FXSR
) {
149 set_cr4(get_cr4() | CR4_FXS
);
150 printf("Enabling XMM register save/restore");
151 /* And allow SIMD instructions if present */
152 if (cpuid_features() & CPUID_FEATURE_SSE
) {
153 printf(" and SSE/SSE2");
154 set_cr4(get_cr4() | CR4_XMM
);
156 printf(" opcodes\n");
160 * Trap wait instructions. Turn off FPU for now.
162 set_cr0(get_cr0() | CR0_TS
| CR0_MP
);
170 set_cr0(get_cr0() | CR0_EM
);
175 * Initialize FP handling.
178 fpu_module_init(void)
180 ifps_zone
= zinit(sizeof(struct i386_fpsave_state
),
181 THREAD_MAX
* sizeof(struct i386_fpsave_state
),
182 THREAD_CHUNK
* sizeof(struct i386_fpsave_state
),
183 "i386 fpsave state");
187 * Free a FPU save area.
188 * Called only when thread terminating - no locking necessary.
192 struct i386_fpsave_state
*fps
;
196 if ((fp_act
!= THR_ACT_NULL
) && (fp_act
->mact
.pcb
->ims
.ifps
== fps
)) {
198 * Make sure we don't get FPU interrupts later for
203 /* Mark it free and disable access */
206 #endif /* NCPUS == 1 */
207 zfree(ifps_zone
, (vm_offset_t
) fps
);
211 * Set the floating-point state for a thread based
212 * on the FXSave formatted data. This is basically
213 * the same as fpu_set_state except it uses the
214 * expanded data structure.
215 * If the thread is not the current thread, it is
216 * not running (held). Locking needed against
217 * concurrent fpu_set_state or fpu_get_state.
221 thread_act_t thr_act
,
222 struct i386_float_state
*state
)
225 register struct i386_fpsave_state
*ifps
;
226 register struct i386_fpsave_state
*new_ifps
;
229 if (fp_kind
== FP_NO
)
232 if (state
->fpkind
!= FP_FXSR
) {
233 /* strange if this happens, but in case someone builds one of these manually... */
234 return fpu_set_state(thr_act
, state
);
237 assert(thr_act
!= THR_ACT_NULL
);
238 pcb
= thr_act
->mact
.pcb
;
243 * If this thread`s state is in the FPU,
244 * discard it; we are replacing the entire
247 if (fp_act
== thr_act
) {
248 fwait(); /* wait for possible interrupt */
249 clear_fpu(); /* no state in FPU */
253 if (state
->initialized
== 0) {
255 * new FPU state is 'invalid'.
256 * Deallocate the fp state if it exists.
258 simple_lock(&pcb
->lock
);
259 ifps
= pcb
->ims
.ifps
;
261 simple_unlock(&pcb
->lock
);
264 zfree(ifps_zone
, (vm_offset_t
) ifps
);
269 * Valid state. Allocate the fp state if there is none.
274 simple_lock(&pcb
->lock
);
275 ifps
= pcb
->ims
.ifps
;
278 simple_unlock(&pcb
->lock
);
279 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
280 assert(ALIGNED(new_ifps
,16));
285 bzero((char *)ifps
, sizeof *ifps
);
286 pcb
->ims
.ifps
= ifps
;
290 * now copy over the new data.
292 bcopy((char *)&state
->hw_state
[0], (char *)&ifps
->fx_save_state
, sizeof(struct i386_fx_save
));
293 ifps
->fp_save_flavor
= FP_FXSR
;
294 simple_unlock(&pcb
->lock
);
296 zfree(ifps_zone
, (vm_offset_t
) ifps
);
303 * Get the floating-point state for a thread.
304 * If the thread is not the current thread, it is
305 * not running (held). Locking needed against
306 * concurrent fpu_set_state or fpu_get_state.
310 thread_act_t thr_act
,
311 register struct i386_float_state
*state
)
314 register struct i386_fpsave_state
*ifps
;
317 if (fp_kind
== FP_NO
)
320 assert(thr_act
!= THR_ACT_NULL
);
321 pcb
= thr_act
->mact
.pcb
;
323 simple_lock(&pcb
->lock
);
324 ifps
= pcb
->ims
.ifps
;
327 * No valid floating-point state.
329 simple_unlock(&pcb
->lock
);
330 bzero((char *)state
, sizeof(struct i386_float_state
));
334 /* Make sure we`ve got the latest fp state info */
335 /* If the live fpu state belongs to our target */
337 if (thr_act
== fp_act
)
339 if (thr_act
== current_act())
347 state
->fpkind
= fp_kind
;
348 state
->exc_status
= 0;
349 state
->initialized
= ifps
->fp_valid
;
350 bcopy( (char *)&ifps
->fx_save_state
, (char *)&state
->hw_state
[0], sizeof(struct i386_fx_save
));
352 simple_unlock(&pcb
->lock
);
358 * Set the floating-point state for a thread.
359 * If the thread is not the current thread, it is
360 * not running (held). Locking needed against
361 * concurrent fpu_set_state or fpu_get_state.
365 thread_act_t thr_act
,
366 struct i386_float_state
*state
)
369 register struct i386_fpsave_state
*ifps
;
370 register struct i386_fpsave_state
*new_ifps
;
373 if (fp_kind
== FP_NO
)
376 assert(thr_act
!= THR_ACT_NULL
);
377 pcb
= thr_act
->mact
.pcb
;
382 * If this thread`s state is in the FPU,
383 * discard it; we are replacing the entire
386 if (fp_act
== thr_act
) {
387 fwait(); /* wait for possible interrupt */
388 clear_fpu(); /* no state in FPU */
392 if (state
->initialized
== 0) {
394 * new FPU state is 'invalid'.
395 * Deallocate the fp state if it exists.
397 simple_lock(&pcb
->lock
);
398 ifps
= pcb
->ims
.ifps
;
400 simple_unlock(&pcb
->lock
);
403 zfree(ifps_zone
, (vm_offset_t
) ifps
);
408 * Valid state. Allocate the fp state if there is none.
410 register struct i386_fp_save
*user_fp_state
;
411 register struct i386_fp_regs
*user_fp_regs
;
413 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
414 user_fp_regs
= (struct i386_fp_regs
*)
415 &state
->hw_state
[sizeof(struct i386_fp_save
)];
419 simple_lock(&pcb
->lock
);
420 ifps
= pcb
->ims
.ifps
;
423 simple_unlock(&pcb
->lock
);
424 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
425 assert(ALIGNED(new_ifps
,16));
430 bzero((char *)ifps
, sizeof *ifps
); // zero ALL fields first
431 pcb
->ims
.ifps
= ifps
;
435 * Ensure that reserved parts of the environment are 0.
437 bzero((char *)&ifps
->fp_save_state
, sizeof(struct i386_fp_save
));
439 ifps
->fp_save_state
.fp_control
= user_fp_state
->fp_control
;
440 ifps
->fp_save_state
.fp_status
= user_fp_state
->fp_status
;
441 ifps
->fp_save_state
.fp_tag
= user_fp_state
->fp_tag
;
442 ifps
->fp_save_state
.fp_eip
= user_fp_state
->fp_eip
;
443 ifps
->fp_save_state
.fp_cs
= user_fp_state
->fp_cs
;
444 ifps
->fp_save_state
.fp_opcode
= user_fp_state
->fp_opcode
;
445 ifps
->fp_save_state
.fp_dp
= user_fp_state
->fp_dp
;
446 ifps
->fp_save_state
.fp_ds
= user_fp_state
->fp_ds
;
447 ifps
->fp_regs
= *user_fp_regs
;
448 ifps
->fp_save_flavor
= FP_387
;
449 simple_unlock(&pcb
->lock
);
451 zfree(ifps_zone
, (vm_offset_t
) ifps
);
458 * Get the floating-point state for a thread.
459 * If the thread is not the current thread, it is
460 * not running (held). Locking needed against
461 * concurrent fpu_set_state or fpu_get_state.
465 thread_act_t thr_act
,
466 register struct i386_float_state
*state
)
469 register struct i386_fpsave_state
*ifps
;
472 if (fp_kind
== FP_NO
)
475 assert(thr_act
!= THR_ACT_NULL
);
476 pcb
= thr_act
->mact
.pcb
;
478 simple_lock(&pcb
->lock
);
479 ifps
= pcb
->ims
.ifps
;
482 * No valid floating-point state.
484 simple_unlock(&pcb
->lock
);
485 bzero((char *)state
, sizeof(struct i386_float_state
));
489 /* Make sure we`ve got the latest fp state info */
490 /* If the live fpu state belongs to our target */
492 if (thr_act
== fp_act
)
494 if (thr_act
== current_act())
502 state
->fpkind
= fp_kind
;
503 state
->exc_status
= 0;
506 register struct i386_fp_save
*user_fp_state
;
507 register struct i386_fp_regs
*user_fp_regs
;
509 state
->initialized
= ifps
->fp_valid
;
511 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
512 user_fp_regs
= (struct i386_fp_regs
*)
513 &state
->hw_state
[sizeof(struct i386_fp_save
)];
516 * Ensure that reserved parts of the environment are 0.
518 bzero((char *)user_fp_state
, sizeof(struct i386_fp_save
));
520 user_fp_state
->fp_control
= ifps
->fp_save_state
.fp_control
;
521 user_fp_state
->fp_status
= ifps
->fp_save_state
.fp_status
;
522 user_fp_state
->fp_tag
= ifps
->fp_save_state
.fp_tag
;
523 user_fp_state
->fp_eip
= ifps
->fp_save_state
.fp_eip
;
524 user_fp_state
->fp_cs
= ifps
->fp_save_state
.fp_cs
;
525 user_fp_state
->fp_opcode
= ifps
->fp_save_state
.fp_opcode
;
526 user_fp_state
->fp_dp
= ifps
->fp_save_state
.fp_dp
;
527 user_fp_state
->fp_ds
= ifps
->fp_save_state
.fp_ds
;
528 *user_fp_regs
= ifps
->fp_regs
;
530 simple_unlock(&pcb
->lock
);
538 * Raise exceptions for:
543 * Use 53-bit precision.
548 unsigned short control
;
554 control
&= ~(FPC_PC
|FPC_RC
); /* Clear precision & rounding control */
555 control
|= (FPC_PC_53
| /* Set precision */
556 FPC_RC_RN
| /* round-to-nearest */
557 FPC_ZE
| /* Suppress zero-divide */
558 FPC_OE
| /* and overflow */
559 FPC_UE
| /* underflow */
560 FPC_IE
| /* Allow NaNQs and +-INF */
561 FPC_DE
| /* Allow denorms as operands */
562 FPC_PE
); /* No trap for precision loss */
567 * Coprocessor not present.
581 * If this thread`s state is in the FPU, we are done.
583 if (fp_act
== current_act())
586 /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
587 * here if the current fpu instruction generates an error.
591 * If another thread`s state is in the FPU, save it.
593 if (fp_act
!= THR_ACT_NULL
) {
598 * Give this thread the FPU.
600 fp_act
= current_act();
602 #endif /* NCPUS == 1 */
605 * Load this thread`s state into the FPU.
607 fp_load(current_act());
611 * FPU overran end of segment.
612 * Re-initialize FPU. Floating point state is not valid.
618 register thread_act_t thr_act
= current_act();
620 register struct i386_fpsave_state
*ifps
;
625 * Is exception for the currently running thread?
627 if (fp_act
!= thr_act
) {
629 panic("fpextovrflt");
634 * This is a non-recoverable error.
635 * Invalidate the thread`s FPU state.
637 pcb
= thr_act
->mact
.pcb
;
638 simple_lock(&pcb
->lock
);
639 ifps
= pcb
->ims
.ifps
;
641 simple_unlock(&pcb
->lock
);
644 * Re-initialize the FPU.
650 * And disable access.
655 zfree(ifps_zone
, (vm_offset_t
) ifps
);
660 i386_exception(EXC_BAD_ACCESS
, VM_PROT_READ
|VM_PROT_EXECUTE
, 0);
665 * FPU error. Called by AST.
671 register thread_act_t thr_act
= current_act();
676 * Since FPU errors only occur on ESC or WAIT instructions,
677 * the current thread should own the FPU. If it didn`t,
678 * we should have gotten the task-switched interrupt first.
680 if (fp_act
!= THR_ACT_NULL
) {
681 panic("fpexterrflt");
686 * Check if we got a context switch between the interrupt and the AST
687 * This can happen if the interrupt arrived after the FPU AST was
688 * checked. In this case, raise the exception in fp_load when this
689 * thread next time uses the FPU. Remember exception condition in
690 * fp_valid (extended boolean 2).
692 if (fp_intr_act
!= thr_act
) {
693 if (fp_intr_act
== THR_ACT_NULL
) {
694 panic("fpexterrflt: fp_intr_act == THR_ACT_NULL");
697 fp_intr_act
->mact
.pcb
->ims
.ifps
->fp_valid
= 2;
698 fp_intr_act
= THR_ACT_NULL
;
701 fp_intr_act
= THR_ACT_NULL
;
702 #else /* NCPUS == 1 */
704 * Save the FPU state and turn off the FPU.
707 #endif /* NCPUS == 1 */
710 * Raise FPU exception.
711 * Locking not needed on pcb->ims.ifps,
712 * since thread is running.
714 i386_exception(EXC_ARITHMETIC
,
716 thr_act
->mact
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
723 * Locking not needed:
724 * . if called from fpu_get_state, pcb already locked.
725 * . if called from fpnoextflt or fp_intr, we are single-cpu
726 * . otherwise, thread is running.
730 thread_act_t thr_act
)
732 register pcb_t pcb
= thr_act
->mact
.pcb
;
733 register struct i386_fpsave_state
*ifps
= pcb
->ims
.ifps
;
734 if (ifps
!= 0 && !ifps
->fp_valid
) {
735 /* registers are in FPU */
736 ifps
->fp_valid
= TRUE
;
737 ifps
->fp_save_flavor
= FP_387
;
739 fxsave(&ifps
->fx_save_state
); // save the SSE2/Fp state in addition is enabled
740 ifps
->fp_save_flavor
= FP_FXSR
;
742 fnsave(&ifps
->fp_save_state
); // also update the old save area for now...
747 * Restore FPU state from PCB.
749 * Locking not needed; always called on the current thread.
754 thread_act_t thr_act
)
756 register pcb_t pcb
= thr_act
->mact
.pcb
;
757 register struct i386_fpsave_state
*ifps
;
760 ifps
= pcb
->ims
.ifps
;
762 ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
763 assert(ALIGNED(ifps
,16));
764 bzero((char *)ifps
, sizeof *ifps
);
765 pcb
->ims
.ifps
= ifps
;
769 * I'm not sure this is needed. Does the fpu regenerate the interrupt in
770 * frstor or not? Without this code we may miss some exceptions, with it
771 * we might send too many exceptions.
773 } else if (ifps
->fp_valid
== 2) {
774 /* delayed exception pending */
776 ifps
->fp_valid
= TRUE
;
779 * Raise FPU exception.
780 * Locking not needed on pcb->ims.ifps,
781 * since thread is running.
783 i386_exception(EXC_ARITHMETIC
,
785 thr_act
->mact
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
789 if (ifps
->fp_save_flavor
== FP_FXSR
) fxrstor(&ifps
->fx_save_state
);
790 else frstor(ifps
->fp_save_state
);
792 ifps
->fp_valid
= FALSE
; /* in FPU */
797 * Allocate and initialize FP state for current thread.
800 * Locking not needed; always called on the current thread.
805 pcb_t pcb
= current_act()->mact
.pcb
;
806 struct i386_fpsave_state
*ifps
;
808 ifps
= (struct i386_fpsave_state
*)zalloc(ifps_zone
);
809 assert(ALIGNED(ifps
,16));
810 bzero((char *)ifps
, sizeof *ifps
);
811 pcb
->ims
.ifps
= ifps
;
813 ifps
->fp_valid
= TRUE
;
814 ifps
->fp_save_state
.fp_control
= (0x037f
815 & ~(FPC_IM
|FPC_ZM
|FPC_OM
|FPC_PC
))
816 | (FPC_PC_53
|FPC_IC_AFF
);
817 ifps
->fp_save_state
.fp_status
= 0;
818 ifps
->fp_save_state
.fp_tag
= 0xffff; /* all empty */
819 ifps
->fx_save_state
.fx_control
= ifps
->fp_save_state
.fp_control
;
820 ifps
->fx_save_state
.fx_status
= ifps
->fp_save_state
.fp_status
;
821 ifps
->fx_save_state
.fx_tag
= 0x00;
822 ifps
->fx_save_state
.fx_MXCSR
= 0x1f80;
828 * fpflush(thread_act_t)
829 * Flush the current act's state, if needed
830 * (used by thread_terminate_self to ensure fp faults
831 * aren't satisfied by overly general trap code in the
832 * context of the reaper thread)
835 fpflush(thread_act_t thr_act
)
838 if (fp_act
&& thr_act
== fp_act
) {
844 /* not needed on MP x86s; fp not lazily evaluated */
850 * Handle a coprocessor error interrupt on the AT386.
851 * This comes in on line 5 of the slave PIC at SPL1.
858 thread_act_t thr_act
= current_act();
862 * Turn off the extended 'busy' line.
867 * Save the FPU context to the thread using it.
870 if (fp_act
== THR_ACT_NULL
) {
871 printf("fpintr: FPU not belonging to anyone!\n");
878 if (fp_act
!= thr_act
) {
880 * FPU exception is for a different thread.
881 * When that thread again uses the FPU an exception will be
882 * raised in fp_load. Remember the condition in fp_valid (== 2).
886 fp_act
->mact
.pcb
->ims
.ifps
->fp_valid
= 2;
889 /* leave fp_intr_act THR_ACT_NULL */
892 if (fp_intr_act
!= THR_ACT_NULL
)
893 panic("fp_intr: already caught intr");
894 fp_intr_act
= thr_act
;
895 #endif /* NCPUS == 1 */
903 * Since we are running on the interrupt stack, we must
904 * signal the thread to take the exception when we return
905 * to user mode. Use an AST to do this.
907 * Don`t set the thread`s AST field. If the thread is
908 * descheduled before it takes the AST, it will notice
909 * the FPU error when it reloads its FPU state.
912 mp_disable_preemption();
914 mp_enable_preemption();