2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Mach Operating System
33 * Copyright (c) 1992-1990 Carnegie Mellon University
34 * All Rights Reserved.
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
59 #include <platforms.h>
61 #include <mach/exception_types.h>
62 #include <mach/i386/thread_status.h>
63 #include <mach/i386/fp_reg.h>
65 #include <kern/mach_param.h>
66 #include <kern/processor.h>
67 #include <kern/thread.h>
68 #include <kern/zalloc.h>
69 #include <kern/misc_protos.h>
71 #include <kern/assert.h>
73 #include <i386/thread.h>
75 #include <i386/trap.h>
77 #include <i386/cpuid.h>
78 #include <i386/misc_protos.h>
83 #define ASSERT_IPL(L) \
85 if (curr_ipl != L) { \
86 printf("IPL is %d, expected %d\n", curr_ipl, L); \
87 panic("fpu: wrong ipl"); \
94 int fp_kind
= FP_387
; /* 80387 present */
95 zone_t ifps_zone
; /* zone for FPU save area */
102 #define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
106 extern void fpinit(void);
113 * Look for FPU and initialize it.
114 * Called on each CPU.
119 unsigned short status
, control
;
122 * Check for FPU by initializing it,
123 * then trying to read the correct bit patterns from
124 * the control and status registers.
126 set_cr0((get_cr0() & ~(CR0_EM
|CR0_TS
)) | CR0_NE
); /* allow use of FPU */
132 if ((status
& 0xff) == 0 &&
133 (control
& 0x103f) == 0x3f)
135 fp_kind
= FP_387
; /* assume we have a 387 compatible instruction set */
136 /* Use FPU save/restore instructions if available */
137 if (cpuid_features() & CPUID_FEATURE_FXSR
) {
139 set_cr4(get_cr4() | CR4_FXS
);
140 printf("Enabling XMM register save/restore");
141 /* And allow SIMD instructions if present */
142 if (cpuid_features() & CPUID_FEATURE_SSE
) {
143 printf(" and SSE/SSE2");
144 set_cr4(get_cr4() | CR4_XMM
);
146 printf(" opcodes\n");
150 * Trap wait instructions. Turn off FPU for now.
152 set_cr0(get_cr0() | CR0_TS
| CR0_MP
);
160 set_cr0(get_cr0() | CR0_EM
);
165 * Initialize FP handling.
168 fpu_module_init(void)
170 ifps_zone
= zinit(sizeof(struct i386_fpsave_state
),
171 THREAD_MAX
* sizeof(struct i386_fpsave_state
),
172 THREAD_CHUNK
* sizeof(struct i386_fpsave_state
),
173 "i386 fpsave state");
177 * Free a FPU save area.
178 * Called only when thread terminating - no locking necessary.
182 struct i386_fpsave_state
*fps
;
185 zfree(ifps_zone
, fps
);
189 * Set the floating-point state for a thread based
190 * on the FXSave formatted data. This is basically
191 * the same as fpu_set_state except it uses the
192 * expanded data structure.
193 * If the thread is not the current thread, it is
194 * not running (held). Locking needed against
195 * concurrent fpu_set_state or fpu_get_state.
200 struct i386_float_state
*state
)
203 register struct i386_fpsave_state
*ifps
;
204 register struct i386_fpsave_state
*new_ifps
;
207 if (fp_kind
== FP_NO
)
210 if (state
->fpkind
!= FP_FXSR
) {
211 /* strange if this happens, but in case someone builds one of these manually... */
212 return fpu_set_state(thr_act
, state
);
215 assert(thr_act
!= THREAD_NULL
);
216 pcb
= thr_act
->machine
.pcb
;
218 if (state
->initialized
== 0) {
220 * new FPU state is 'invalid'.
221 * Deallocate the fp state if it exists.
223 simple_lock(&pcb
->lock
);
224 ifps
= pcb
->ims
.ifps
;
226 simple_unlock(&pcb
->lock
);
229 zfree(ifps_zone
, ifps
);
234 * Valid state. Allocate the fp state if there is none.
239 simple_lock(&pcb
->lock
);
240 ifps
= pcb
->ims
.ifps
;
243 simple_unlock(&pcb
->lock
);
244 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
245 assert(ALIGNED(new_ifps
,16));
250 bzero((char *)ifps
, sizeof *ifps
);
251 pcb
->ims
.ifps
= ifps
;
255 * now copy over the new data.
257 bcopy((char *)&state
->hw_state
[0], (char *)&ifps
->fx_save_state
, sizeof(struct i386_fx_save
));
258 ifps
->fp_save_flavor
= FP_FXSR
;
259 simple_unlock(&pcb
->lock
);
261 zfree(ifps_zone
, ifps
);
268 * Get the floating-point state for a thread.
269 * If the thread is not the current thread, it is
270 * not running (held). Locking needed against
271 * concurrent fpu_set_state or fpu_get_state.
276 register struct i386_float_state
*state
)
279 register struct i386_fpsave_state
*ifps
;
282 if (fp_kind
== FP_NO
) {
284 } else if (fp_kind
== FP_387
) {
285 return fpu_get_state(thr_act
, state
);
288 assert(thr_act
!= THREAD_NULL
);
289 pcb
= thr_act
->machine
.pcb
;
291 simple_lock(&pcb
->lock
);
292 ifps
= pcb
->ims
.ifps
;
295 * No valid floating-point state.
297 simple_unlock(&pcb
->lock
);
298 bzero((char *)state
, sizeof(struct i386_float_state
));
302 /* Make sure we`ve got the latest fp state info */
303 /* If the live fpu state belongs to our target */
304 if (thr_act
== current_thread())
311 state
->fpkind
= fp_kind
;
312 state
->exc_status
= 0;
313 state
->initialized
= ifps
->fp_valid
;
314 bcopy( (char *)&ifps
->fx_save_state
, (char *)&state
->hw_state
[0], sizeof(struct i386_fx_save
));
316 simple_unlock(&pcb
->lock
);
322 * Set the floating-point state for a thread.
323 * If the thread is not the current thread, it is
324 * not running (held). Locking needed against
325 * concurrent fpu_set_state or fpu_get_state.
330 struct i386_float_state
*state
)
333 register struct i386_fpsave_state
*ifps
;
334 register struct i386_fpsave_state
*new_ifps
;
337 if (fp_kind
== FP_NO
)
340 assert(thr_act
!= THREAD_NULL
);
341 pcb
= thr_act
->machine
.pcb
;
343 if (state
->initialized
== 0) {
345 * new FPU state is 'invalid'.
346 * Deallocate the fp state if it exists.
348 simple_lock(&pcb
->lock
);
349 ifps
= pcb
->ims
.ifps
;
351 simple_unlock(&pcb
->lock
);
354 zfree(ifps_zone
, ifps
);
359 * Valid state. Allocate the fp state if there is none.
361 register struct i386_fp_save
*user_fp_state
;
362 register struct i386_fp_regs
*user_fp_regs
;
364 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
365 user_fp_regs
= (struct i386_fp_regs
*)
366 &state
->hw_state
[sizeof(struct i386_fp_save
)];
370 simple_lock(&pcb
->lock
);
371 ifps
= pcb
->ims
.ifps
;
374 simple_unlock(&pcb
->lock
);
375 new_ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
376 assert(ALIGNED(new_ifps
,16));
381 bzero((char *)ifps
, sizeof *ifps
); // zero ALL fields first
382 pcb
->ims
.ifps
= ifps
;
386 * Ensure that reserved parts of the environment are 0.
388 bzero((char *)&ifps
->fp_save_state
, sizeof(struct i386_fp_save
));
390 ifps
->fp_save_state
.fp_control
= user_fp_state
->fp_control
;
391 ifps
->fp_save_state
.fp_status
= user_fp_state
->fp_status
;
392 ifps
->fp_save_state
.fp_tag
= user_fp_state
->fp_tag
;
393 ifps
->fp_save_state
.fp_eip
= user_fp_state
->fp_eip
;
394 ifps
->fp_save_state
.fp_cs
= user_fp_state
->fp_cs
;
395 ifps
->fp_save_state
.fp_opcode
= user_fp_state
->fp_opcode
;
396 ifps
->fp_save_state
.fp_dp
= user_fp_state
->fp_dp
;
397 ifps
->fp_save_state
.fp_ds
= user_fp_state
->fp_ds
;
398 ifps
->fp_regs
= *user_fp_regs
;
399 ifps
->fp_save_flavor
= FP_387
;
400 simple_unlock(&pcb
->lock
);
402 zfree(ifps_zone
, ifps
);
409 * Get the floating-point state for a thread.
410 * If the thread is not the current thread, it is
411 * not running (held). Locking needed against
412 * concurrent fpu_set_state or fpu_get_state.
417 register struct i386_float_state
*state
)
420 register struct i386_fpsave_state
*ifps
;
423 if (fp_kind
== FP_NO
)
426 assert(thr_act
!= THREAD_NULL
);
427 pcb
= thr_act
->machine
.pcb
;
429 simple_lock(&pcb
->lock
);
430 ifps
= pcb
->ims
.ifps
;
433 * No valid floating-point state.
435 simple_unlock(&pcb
->lock
);
436 bzero((char *)state
, sizeof(struct i386_float_state
));
440 /* Make sure we`ve got the latest fp state info */
441 /* If the live fpu state belongs to our target */
442 if (thr_act
== current_thread())
449 state
->fpkind
= fp_kind
;
450 state
->exc_status
= 0;
453 register struct i386_fp_save
*user_fp_state
;
454 register struct i386_fp_regs
*user_fp_regs
;
456 state
->initialized
= ifps
->fp_valid
;
458 user_fp_state
= (struct i386_fp_save
*) &state
->hw_state
[0];
459 user_fp_regs
= (struct i386_fp_regs
*)
460 &state
->hw_state
[sizeof(struct i386_fp_save
)];
463 * Ensure that reserved parts of the environment are 0.
465 bzero((char *)user_fp_state
, sizeof(struct i386_fp_save
));
467 user_fp_state
->fp_control
= ifps
->fp_save_state
.fp_control
;
468 user_fp_state
->fp_status
= ifps
->fp_save_state
.fp_status
;
469 user_fp_state
->fp_tag
= ifps
->fp_save_state
.fp_tag
;
470 user_fp_state
->fp_eip
= ifps
->fp_save_state
.fp_eip
;
471 user_fp_state
->fp_cs
= ifps
->fp_save_state
.fp_cs
;
472 user_fp_state
->fp_opcode
= ifps
->fp_save_state
.fp_opcode
;
473 user_fp_state
->fp_dp
= ifps
->fp_save_state
.fp_dp
;
474 user_fp_state
->fp_ds
= ifps
->fp_save_state
.fp_ds
;
475 *user_fp_regs
= ifps
->fp_regs
;
477 simple_unlock(&pcb
->lock
);
485 * Raise exceptions for:
490 * Use 53-bit precision.
495 unsigned short control
;
501 control
&= ~(FPC_PC
|FPC_RC
); /* Clear precision & rounding control */
502 control
|= (FPC_PC_53
| /* Set precision */
503 FPC_RC_RN
| /* round-to-nearest */
504 FPC_ZE
| /* Suppress zero-divide */
505 FPC_OE
| /* and overflow */
506 FPC_UE
| /* underflow */
507 FPC_IE
| /* Allow NaNQs and +-INF */
508 FPC_DE
| /* Allow denorms as operands */
509 FPC_PE
); /* No trap for precision loss */
514 * Coprocessor not present.
527 * Load this thread`s state into the FPU.
529 fp_load(current_thread());
533 * FPU overran end of segment.
534 * Re-initialize FPU. Floating point state is not valid.
540 register thread_t thr_act
= current_thread();
542 register struct i386_fpsave_state
*ifps
;
545 * This is a non-recoverable error.
546 * Invalidate the thread`s FPU state.
548 pcb
= thr_act
->machine
.pcb
;
549 simple_lock(&pcb
->lock
);
550 ifps
= pcb
->ims
.ifps
;
552 simple_unlock(&pcb
->lock
);
555 * Re-initialize the FPU.
561 * And disable access.
566 zfree(ifps_zone
, ifps
);
571 i386_exception(EXC_BAD_ACCESS
, VM_PROT_READ
|VM_PROT_EXECUTE
, 0);
576 * FPU error. Called by AST.
582 register thread_t thr_act
= current_thread();
586 * Save the FPU state and turn off the FPU.
591 * Raise FPU exception.
592 * Locking not needed on pcb->ims.ifps,
593 * since thread is running.
595 i386_exception(EXC_ARITHMETIC
,
597 thr_act
->machine
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
604 * Locking not needed:
605 * . if called from fpu_get_state, pcb already locked.
606 * . if called from fpnoextflt or fp_intr, we are single-cpu
607 * . otherwise, thread is running.
613 register pcb_t pcb
= thr_act
->machine
.pcb
;
614 register struct i386_fpsave_state
*ifps
= pcb
->ims
.ifps
;
615 if (ifps
!= 0 && !ifps
->fp_valid
) {
616 /* registers are in FPU */
617 ifps
->fp_valid
= TRUE
;
618 ifps
->fp_save_flavor
= FP_387
;
620 fxsave(&ifps
->fx_save_state
); // save the SSE2/Fp state in addition is enabled
621 ifps
->fp_save_flavor
= FP_FXSR
;
623 fnsave(&ifps
->fp_save_state
); // also update the old save area for now...
628 * Restore FPU state from PCB.
630 * Locking not needed; always called on the current thread.
637 register pcb_t pcb
= thr_act
->machine
.pcb
;
638 register struct i386_fpsave_state
*ifps
;
641 ifps
= pcb
->ims
.ifps
;
643 ifps
= (struct i386_fpsave_state
*) zalloc(ifps_zone
);
644 assert(ALIGNED(ifps
,16));
645 bzero((char *)ifps
, sizeof *ifps
);
646 pcb
->ims
.ifps
= ifps
;
650 * I'm not sure this is needed. Does the fpu regenerate the interrupt in
651 * frstor or not? Without this code we may miss some exceptions, with it
652 * we might send too many exceptions.
654 } else if (ifps
->fp_valid
== 2) {
655 /* delayed exception pending */
657 ifps
->fp_valid
= TRUE
;
660 * Raise FPU exception.
661 * Locking not needed on pcb->ims.ifps,
662 * since thread is running.
664 i386_exception(EXC_ARITHMETIC
,
666 thr_act
->machine
.pcb
->ims
.ifps
->fp_save_state
.fp_status
);
670 if (ifps
->fp_save_flavor
== FP_FXSR
) fxrstor(&ifps
->fx_save_state
);
671 else frstor(ifps
->fp_save_state
);
673 ifps
->fp_valid
= FALSE
; /* in FPU */
678 * Allocate and initialize FP state for current thread.
681 * Locking not needed; always called on the current thread.
686 pcb_t pcb
= current_thread()->machine
.pcb
;
687 struct i386_fpsave_state
*ifps
;
689 ifps
= (struct i386_fpsave_state
*)zalloc(ifps_zone
);
690 assert(ALIGNED(ifps
,16));
691 bzero((char *)ifps
, sizeof *ifps
);
692 pcb
->ims
.ifps
= ifps
;
694 ifps
->fp_valid
= TRUE
;
695 ifps
->fp_save_state
.fp_control
= (0x037f
696 & ~(FPC_IM
|FPC_ZM
|FPC_OM
|FPC_PC
))
697 | (FPC_PC_53
|FPC_IC_AFF
);
698 ifps
->fp_save_state
.fp_status
= 0;
699 ifps
->fp_save_state
.fp_tag
= 0xffff; /* all empty */
700 ifps
->fx_save_state
.fx_control
= ifps
->fp_save_state
.fp_control
;
701 ifps
->fx_save_state
.fx_status
= ifps
->fp_save_state
.fp_status
;
702 ifps
->fx_save_state
.fx_tag
= 0x00;
703 ifps
->fx_save_state
.fx_MXCSR
= 0x1f80;
710 * Flush the current act's state, if needed
711 * (used by thread_terminate_self to ensure fp faults
712 * aren't satisfied by overly general trap code in the
713 * context of the reaper thread)
716 fpflush(__unused thread_t thr_act
)
718 /* not needed on MP x86s; fp not lazily evaluated */
723 * Handle a coprocessor error interrupt on the AT386.
724 * This comes in on line 5 of the slave PIC at SPL1.
731 thread_t thr_act
= current_thread();
735 * Turn off the extended 'busy' line.
740 * Save the FPU context to the thread using it.
748 * Since we are running on the interrupt stack, we must
749 * signal the thread to take the exception when we return
750 * to user mode. Use an AST to do this.
752 * Don`t set the thread`s AST field. If the thread is
753 * descheduled before it takes the AST, it will notice
754 * the FPU error when it reloads its FPU state.
757 mp_disable_preemption();
759 mp_enable_preemption();