2 * Copyright (c) 2000-2004 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.
53 #include <platforms.h>
55 #include <mach/exception_types.h>
56 #include <mach/i386/thread_status.h>
57 #include <mach/i386/fp_reg.h>
59 #include <kern/mach_param.h>
60 #include <kern/processor.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 */
96 #define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
100 extern void fpinit(void);
107 * Look for FPU and initialize it.
108 * Called on each CPU.
113 unsigned short status
, control
;
116 * Check for FPU by initializing it,
117 * then trying to read the correct bit patterns from
118 * the control and status registers.
120 set_cr0((get_cr0() & ~(CR0_EM
|CR0_TS
)) | CR0_NE
); /* allow use of FPU */
126 if ((status
& 0xff) == 0 &&
127 (control
& 0x103f) == 0x3f)
129 fp_kind
= FP_387
; /* assume we have a 387 compatible instruction set */
130 /* Use FPU save/restore instructions if available */
131 if (cpuid_features() & CPUID_FEATURE_FXSR
) {
133 set_cr4(get_cr4() | CR4_FXS
);
134 printf("Enabling XMM register save/restore");
135 /* And allow SIMD instructions if present */
136 if (cpuid_features() & CPUID_FEATURE_SSE
) {
137 printf(" and SSE/SSE2");
138 set_cr4(get_cr4() | CR4_XMM
);
140 printf(" opcodes\n");
144 * Trap wait instructions. Turn off FPU for now.
146 set_cr0(get_cr0() | CR0_TS
| CR0_MP
);
154 set_cr0(get_cr0() | CR0_EM
);
159 * Initialize FP handling.
162 fpu_module_init(void)
164 ifps_zone
= zinit(sizeof(struct i386_fpsave_state
),
165 THREAD_MAX
* sizeof(struct i386_fpsave_state
),
166 THREAD_CHUNK
* sizeof(struct i386_fpsave_state
),
167 "i386 fpsave state");
171 * Free a FPU save area.
172 * Called only when thread terminating - no locking necessary.
176 struct i386_fpsave_state
*fps
;
179 zfree(ifps_zone
, fps
);
183 * Set the floating-point state for a thread based
184 * on the FXSave formatted data. This is basically
185 * the same as fpu_set_state except it uses the
186 * expanded data structure.
187 * If the thread is not the current thread, it is
188 * not running (held). Locking needed against
189 * concurrent fpu_set_state or fpu_get_state.
194 struct i386_float_state
*state
)
197 register struct i386_fpsave_state
*ifps
;
198 register struct i386_fpsave_state
*new_ifps
;
201 if (fp_kind
== FP_NO
)
204 if (state
->fpkind
!= FP_FXSR
) {
205 /* strange if this happens, but in case someone builds one of these manually... */
206 return fpu_set_state(thr_act
, state
);
209 assert(thr_act
!= THREAD_NULL
);
210 pcb
= thr_act
->machine
.pcb
;
212 if (state
->initialized
== 0) {
214 * new FPU state is 'invalid'.
215 * Deallocate the fp state if it exists.
217 simple_lock(&pcb
->lock
);
218 ifps
= pcb
->ims
.ifps
;
220 simple_unlock(&pcb
->lock
);
223 zfree(ifps_zone
, ifps
);
228 * Valid state. Allocate the fp state if there is none.
233 simple_lock(&pcb
->lock
);
234 ifps
= pcb
->ims
.ifps
;
237 simple_unlock(&pcb
->lock
);
238 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
239 assert(ALIGNED(new_ifps
,16));
244 bzero((char *)ifps
, sizeof *ifps
);
245 pcb
->ims
.ifps
= ifps
;
249 * now copy over the new data.
251 bcopy((char *)&state
->hw_state
[0], (char *)&ifps
->fx_save_state
, sizeof(struct i386_fx_save
));
252 ifps
->fp_save_flavor
= FP_FXSR
;
253 simple_unlock(&pcb
->lock
);
255 zfree(ifps_zone
, ifps
);
262 * Get the floating-point state for a thread.
263 * If the thread is not the current thread, it is
264 * not running (held). Locking needed against
265 * concurrent fpu_set_state or fpu_get_state.
270 register struct i386_float_state
*state
)
273 register struct i386_fpsave_state
*ifps
;
276 if (fp_kind
== FP_NO
) {
278 } else if (fp_kind
== FP_387
) {
279 return fpu_get_state(thr_act
, state
);
282 assert(thr_act
!= THREAD_NULL
);
283 pcb
= thr_act
->machine
.pcb
;
285 simple_lock(&pcb
->lock
);
286 ifps
= pcb
->ims
.ifps
;
289 * No valid floating-point state.
291 simple_unlock(&pcb
->lock
);
292 bzero((char *)state
, sizeof(struct i386_float_state
));
296 /* Make sure we`ve got the latest fp state info */
297 /* If the live fpu state belongs to our target */
298 if (thr_act
== current_thread())
305 state
->fpkind
= fp_kind
;
306 state
->exc_status
= 0;
307 state
->initialized
= ifps
->fp_valid
;
308 bcopy( (char *)&ifps
->fx_save_state
, (char *)&state
->hw_state
[0], sizeof(struct i386_fx_save
));
310 simple_unlock(&pcb
->lock
);
316 * Set the floating-point state for a thread.
317 * If the thread is not the current thread, it is
318 * not running (held). Locking needed against
319 * concurrent fpu_set_state or fpu_get_state.
324 struct i386_float_state
*state
)
327 register struct i386_fpsave_state
*ifps
;
328 register struct i386_fpsave_state
*new_ifps
;
331 if (fp_kind
== FP_NO
)
334 assert(thr_act
!= THREAD_NULL
);
335 pcb
= thr_act
->machine
.pcb
;
337 if (state
->initialized
== 0) {
339 * new FPU state is 'invalid'.
340 * Deallocate the fp state if it exists.
342 simple_lock(&pcb
->lock
);
343 ifps
= pcb
->ims
.ifps
;
345 simple_unlock(&pcb
->lock
);
348 zfree(ifps_zone
, ifps
);
353 * Valid state. Allocate the fp state if there is none.
355 register struct i386_fp_save
*user_fp_state
;
356 register struct i386_fp_regs
*user_fp_regs
;
358 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
359 user_fp_regs
= (struct i386_fp_regs
*)
360 &state
->hw_state
[sizeof(struct i386_fp_save
)];
364 simple_lock(&pcb
->lock
);
365 ifps
= pcb
->ims
.ifps
;
368 simple_unlock(&pcb
->lock
);
369 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
370 assert(ALIGNED(new_ifps
,16));
375 bzero((char *)ifps
, sizeof *ifps
); // zero ALL fields first
376 pcb
->ims
.ifps
= ifps
;
380 * Ensure that reserved parts of the environment are 0.
382 bzero((char *)&ifps
->fp_save_state
, sizeof(struct i386_fp_save
));
384 ifps
->fp_save_state
.fp_control
= user_fp_state
->fp_control
;
385 ifps
->fp_save_state
.fp_status
= user_fp_state
->fp_status
;
386 ifps
->fp_save_state
.fp_tag
= user_fp_state
->fp_tag
;
387 ifps
->fp_save_state
.fp_eip
= user_fp_state
->fp_eip
;
388 ifps
->fp_save_state
.fp_cs
= user_fp_state
->fp_cs
;
389 ifps
->fp_save_state
.fp_opcode
= user_fp_state
->fp_opcode
;
390 ifps
->fp_save_state
.fp_dp
= user_fp_state
->fp_dp
;
391 ifps
->fp_save_state
.fp_ds
= user_fp_state
->fp_ds
;
392 ifps
->fp_regs
= *user_fp_regs
;
393 ifps
->fp_save_flavor
= FP_387
;
394 simple_unlock(&pcb
->lock
);
396 zfree(ifps_zone
, ifps
);
403 * Get the floating-point state for a thread.
404 * If the thread is not the current thread, it is
405 * not running (held). Locking needed against
406 * concurrent fpu_set_state or fpu_get_state.
411 register struct i386_float_state
*state
)
414 register struct i386_fpsave_state
*ifps
;
417 if (fp_kind
== FP_NO
)
420 assert(thr_act
!= THREAD_NULL
);
421 pcb
= thr_act
->machine
.pcb
;
423 simple_lock(&pcb
->lock
);
424 ifps
= pcb
->ims
.ifps
;
427 * No valid floating-point state.
429 simple_unlock(&pcb
->lock
);
430 bzero((char *)state
, sizeof(struct i386_float_state
));
434 /* Make sure we`ve got the latest fp state info */
435 /* If the live fpu state belongs to our target */
436 if (thr_act
== current_thread())
443 state
->fpkind
= fp_kind
;
444 state
->exc_status
= 0;
447 register struct i386_fp_save
*user_fp_state
;
448 register struct i386_fp_regs
*user_fp_regs
;
450 state
->initialized
= ifps
->fp_valid
;
452 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
453 user_fp_regs
= (struct i386_fp_regs
*)
454 &state
->hw_state
[sizeof(struct i386_fp_save
)];
457 * Ensure that reserved parts of the environment are 0.
459 bzero((char *)user_fp_state
, sizeof(struct i386_fp_save
));
461 user_fp_state
->fp_control
= ifps
->fp_save_state
.fp_control
;
462 user_fp_state
->fp_status
= ifps
->fp_save_state
.fp_status
;
463 user_fp_state
->fp_tag
= ifps
->fp_save_state
.fp_tag
;
464 user_fp_state
->fp_eip
= ifps
->fp_save_state
.fp_eip
;
465 user_fp_state
->fp_cs
= ifps
->fp_save_state
.fp_cs
;
466 user_fp_state
->fp_opcode
= ifps
->fp_save_state
.fp_opcode
;
467 user_fp_state
->fp_dp
= ifps
->fp_save_state
.fp_dp
;
468 user_fp_state
->fp_ds
= ifps
->fp_save_state
.fp_ds
;
469 *user_fp_regs
= ifps
->fp_regs
;
471 simple_unlock(&pcb
->lock
);
479 * Raise exceptions for:
484 * Use 53-bit precision.
489 unsigned short control
;
495 control
&= ~(FPC_PC
|FPC_RC
); /* Clear precision & rounding control */
496 control
|= (FPC_PC_53
| /* Set precision */
497 FPC_RC_RN
| /* round-to-nearest */
498 FPC_ZE
| /* Suppress zero-divide */
499 FPC_OE
| /* and overflow */
500 FPC_UE
| /* underflow */
501 FPC_IE
| /* Allow NaNQs and +-INF */
502 FPC_DE
| /* Allow denorms as operands */
503 FPC_PE
); /* No trap for precision loss */
508 * Coprocessor not present.
521 * Load this thread`s state into the FPU.
523 fp_load(current_thread());
527 * FPU overran end of segment.
528 * Re-initialize FPU. Floating point state is not valid.
534 register thread_t thr_act
= current_thread();
536 register struct i386_fpsave_state
*ifps
;
539 * This is a non-recoverable error.
540 * Invalidate the thread`s FPU state.
542 pcb
= thr_act
->machine
.pcb
;
543 simple_lock(&pcb
->lock
);
544 ifps
= pcb
->ims
.ifps
;
546 simple_unlock(&pcb
->lock
);
549 * Re-initialize the FPU.
555 * And disable access.
560 zfree(ifps_zone
, ifps
);
565 i386_exception(EXC_BAD_ACCESS
, VM_PROT_READ
|VM_PROT_EXECUTE
, 0);
570 * FPU error. Called by AST.
576 register thread_t thr_act
= current_thread();
580 * Save the FPU state and turn off the FPU.
585 * Raise FPU exception.
586 * Locking not needed on pcb->ims.ifps,
587 * since thread is running.
589 i386_exception(EXC_ARITHMETIC
,
591 thr_act
->machine
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
598 * Locking not needed:
599 * . if called from fpu_get_state, pcb already locked.
600 * . if called from fpnoextflt or fp_intr, we are single-cpu
601 * . otherwise, thread is running.
607 register pcb_t pcb
= thr_act
->machine
.pcb
;
608 register struct i386_fpsave_state
*ifps
= pcb
->ims
.ifps
;
609 if (ifps
!= 0 && !ifps
->fp_valid
) {
610 /* registers are in FPU */
611 ifps
->fp_valid
= TRUE
;
612 ifps
->fp_save_flavor
= FP_387
;
614 fxsave(&ifps
->fx_save_state
); // save the SSE2/Fp state in addition is enabled
615 ifps
->fp_save_flavor
= FP_FXSR
;
617 fnsave(&ifps
->fp_save_state
); // also update the old save area for now...
622 * Restore FPU state from PCB.
624 * Locking not needed; always called on the current thread.
631 register pcb_t pcb
= thr_act
->machine
.pcb
;
632 register struct i386_fpsave_state
*ifps
;
635 ifps
= pcb
->ims
.ifps
;
637 ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
638 assert(ALIGNED(ifps
,16));
639 bzero((char *)ifps
, sizeof *ifps
);
640 pcb
->ims
.ifps
= ifps
;
644 * I'm not sure this is needed. Does the fpu regenerate the interrupt in
645 * frstor or not? Without this code we may miss some exceptions, with it
646 * we might send too many exceptions.
648 } else if (ifps
->fp_valid
== 2) {
649 /* delayed exception pending */
651 ifps
->fp_valid
= TRUE
;
654 * Raise FPU exception.
655 * Locking not needed on pcb->ims.ifps,
656 * since thread is running.
658 i386_exception(EXC_ARITHMETIC
,
660 thr_act
->machine
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
664 if (ifps
->fp_save_flavor
== FP_FXSR
) fxrstor(&ifps
->fx_save_state
);
665 else frstor(ifps
->fp_save_state
);
667 ifps
->fp_valid
= FALSE
; /* in FPU */
672 * Allocate and initialize FP state for current thread.
675 * Locking not needed; always called on the current thread.
680 pcb_t pcb
= current_thread()->machine
.pcb
;
681 struct i386_fpsave_state
*ifps
;
683 ifps
= (struct i386_fpsave_state
*)zalloc(ifps_zone
);
684 assert(ALIGNED(ifps
,16));
685 bzero((char *)ifps
, sizeof *ifps
);
686 pcb
->ims
.ifps
= ifps
;
688 ifps
->fp_valid
= TRUE
;
689 ifps
->fp_save_state
.fp_control
= (0x037f
690 & ~(FPC_IM
|FPC_ZM
|FPC_OM
|FPC_PC
))
691 | (FPC_PC_53
|FPC_IC_AFF
);
692 ifps
->fp_save_state
.fp_status
= 0;
693 ifps
->fp_save_state
.fp_tag
= 0xffff; /* all empty */
694 ifps
->fx_save_state
.fx_control
= ifps
->fp_save_state
.fp_control
;
695 ifps
->fx_save_state
.fx_status
= ifps
->fp_save_state
.fp_status
;
696 ifps
->fx_save_state
.fx_tag
= 0x00;
697 ifps
->fx_save_state
.fx_MXCSR
= 0x1f80;
704 * Flush the current act's state, if needed
705 * (used by thread_terminate_self to ensure fp faults
706 * aren't satisfied by overly general trap code in the
707 * context of the reaper thread)
710 fpflush(__unused thread_t thr_act
)
712 /* not needed on MP x86s; fp not lazily evaluated */
717 * Handle a coprocessor error interrupt on the AT386.
718 * This comes in on line 5 of the slave PIC at SPL1.
725 thread_t thr_act
= current_thread();
729 * Turn off the extended 'busy' line.
734 * Save the FPU context to the thread using it.
742 * Since we are running on the interrupt stack, we must
743 * signal the thread to take the exception when we return
744 * to user mode. Use an AST to do this.
746 * Don`t set the thread`s AST field. If the thread is
747 * descheduled before it takes the AST, it will notice
748 * the FPU error when it reloads its FPU state.
751 mp_disable_preemption();
753 mp_enable_preemption();