]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/fpu.c
xnu-1504.7.4.tar.gz
[apple/xnu.git] / osfmk / i386 / fpu.c
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1992-1990 Carnegie Mellon University
34 * All Rights Reserved.
35 *
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.
41 *
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.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58
1c79356b
A
59#include <platforms.h>
60
61#include <mach/exception_types.h>
62#include <mach/i386/thread_status.h>
63#include <mach/i386/fp_reg.h>
64
65#include <kern/mach_param.h>
91447636 66#include <kern/processor.h>
1c79356b
A
67#include <kern/thread.h>
68#include <kern/zalloc.h>
69#include <kern/misc_protos.h>
70#include <kern/spl.h>
71#include <kern/assert.h>
72
0c530ab8 73#include <architecture/i386/pio.h>
55e303ae 74#include <i386/cpuid.h>
b0d623f7 75#include <i386/fpu.h>
0c530ab8 76#include <i386/proc_reg.h>
b0d623f7
A
77#include <i386/misc_protos.h>
78#include <i386/thread.h>
79#include <i386/trap.h>
1c79356b 80
0c530ab8 81int fp_kind = FP_NO; /* not inited */
1c79356b
A
82zone_t ifps_zone; /* zone for FPU save area */
83
b0d623f7 84#define ALIGNED(addr,size) (((uintptr_t)(addr)&((size)-1))==0)
1c79356b
A
85
86/* Forward */
87
88extern void fpinit(void);
89extern void fp_save(
91447636 90 thread_t thr_act);
1c79356b 91extern void fp_load(
91447636 92 thread_t thr_act);
1c79356b 93
0c530ab8
A
94static void configure_mxcsr_capability_mask(struct x86_fpsave_state *ifps);
95
96struct x86_fpsave_state starting_fp_state;
97
98
99/* Global MXCSR capability bitmask */
100static unsigned int mxcsr_capability_mask;
101
102/*
103 * Determine the MXCSR capability mask, which allows us to mask off any
104 * potentially unsafe "reserved" bits before restoring the FPU context.
105 * *Not* per-cpu, assumes symmetry.
106 */
107static void
108configure_mxcsr_capability_mask(struct x86_fpsave_state *ifps)
109{
110 /* FXSAVE requires a 16 byte aligned store */
111 assert(ALIGNED(ifps,16));
112 /* Clear, to prepare for the diagnostic FXSAVE */
113 bzero(ifps, sizeof(*ifps));
114 /* Disable FPU/SSE Device Not Available exceptions */
115 clear_ts();
0c530ab8
A
116 __asm__ volatile("fxsave %0" : "=m" (ifps->fx_save_state));
117 mxcsr_capability_mask = ifps->fx_save_state.fx_MXCSR_MASK;
118
119 /* Set default mask value if necessary */
120 if (mxcsr_capability_mask == 0)
121 mxcsr_capability_mask = 0xffbf;
122
123 /* Re-enable FPU/SSE DNA exceptions */
124 set_ts();
125}
126
127/*
128 * Allocate and initialize FP state for current thread.
129 * Don't load state.
130 */
131static struct x86_fpsave_state *
132fp_state_alloc(void)
133{
134 struct x86_fpsave_state *ifps;
135
136 ifps = (struct x86_fpsave_state *)zalloc(ifps_zone);
137 assert(ALIGNED(ifps,16));
138 bzero((char *)ifps, sizeof *ifps);
139
140 return ifps;
141}
142
143static inline void
144fp_state_free(struct x86_fpsave_state *ifps)
145{
146 zfree(ifps_zone, ifps);
147}
148
149
1c79356b
A
150/*
151 * Look for FPU and initialize it.
152 * Called on each CPU.
153 */
154void
155init_fpu(void)
156{
157 unsigned short status, control;
158
159 /*
160 * Check for FPU by initializing it,
161 * then trying to read the correct bit patterns from
162 * the control and status registers.
163 */
91447636 164 set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | CR0_NE); /* allow use of FPU */
1c79356b
A
165
166 fninit();
167 status = fnstsw();
168 fnstcw(&control);
169
170 if ((status & 0xff) == 0 &&
55e303ae
A
171 (control & 0x103f) == 0x3f)
172 {
55e303ae 173 /* Use FPU save/restore instructions if available */
0c530ab8 174 if (cpuid_features() & CPUID_FEATURE_FXSR) {
2d21ac55 175 fp_kind = FP_FXSR;
0c530ab8 176 set_cr4(get_cr4() | CR4_FXS);
0c530ab8
A
177 /* And allow SIMD instructions if present */
178 if (cpuid_features() & CPUID_FEATURE_SSE) {
0c530ab8
A
179 set_cr4(get_cr4() | CR4_XMM);
180 }
0c530ab8
A
181 } else
182 panic("fpu is not FP_FXSR");
55e303ae 183
1c79356b 184 /*
0c530ab8
A
185 * initialze FPU to normal starting
186 * position so that we can take a snapshot
187 * of that state and store it for future use
188 * when we're asked for the FPU state of a
189 * thread, and it hasn't initiated any yet
1c79356b 190 */
0c530ab8
A
191 fpinit();
192 fxsave(&starting_fp_state.fx_save_state);
193
194 /*
195 * Trap wait instructions. Turn off FPU for now.
196 */
197 set_cr0(get_cr0() | CR0_TS | CR0_MP);
1c79356b
A
198 }
199 else
200 {
201 /*
202 * NO FPU.
203 */
0c530ab8 204 panic("fpu is not FP_FXSR");
1c79356b
A
205 }
206}
207
208/*
209 * Initialize FP handling.
210 */
211void
212fpu_module_init(void)
213{
0c530ab8
A
214 struct x86_fpsave_state *new_ifps;
215
216 ifps_zone = zinit(sizeof(struct x86_fpsave_state),
b0d623f7 217 thread_max * sizeof(struct x86_fpsave_state),
0c530ab8
A
218 THREAD_CHUNK * sizeof(struct x86_fpsave_state),
219 "x86 fpsave state");
220 new_ifps = fp_state_alloc();
221 /* Determine MXCSR reserved bits */
222 configure_mxcsr_capability_mask(new_ifps);
223 fp_state_free(new_ifps);
1c79356b
A
224}
225
226/*
227 * Free a FPU save area.
228 * Called only when thread terminating - no locking necessary.
229 */
230void
2d21ac55 231fpu_free(struct x86_fpsave_state *fps)
1c79356b 232{
0c530ab8 233 fp_state_free(fps);
1c79356b
A
234}
235
55e303ae
A
236/*
237 * Set the floating-point state for a thread based
238 * on the FXSave formatted data. This is basically
239 * the same as fpu_set_state except it uses the
240 * expanded data structure.
241 * If the thread is not the current thread, it is
242 * not running (held). Locking needed against
243 * concurrent fpu_set_state or fpu_get_state.
244 */
245kern_return_t
246fpu_set_fxstate(
2d21ac55 247 thread_t thr_act,
0c530ab8 248 thread_state_t tstate)
55e303ae 249{
0c530ab8
A
250 struct x86_fpsave_state *ifps;
251 struct x86_fpsave_state *new_ifps;
252 x86_float_state64_t *state;
253 pcb_t pcb;
55e303ae 254
55e303ae 255 if (fp_kind == FP_NO)
2d21ac55 256 return KERN_FAILURE;
0c530ab8
A
257
258 state = (x86_float_state64_t *)tstate;
55e303ae 259
91447636
A
260 assert(thr_act != THREAD_NULL);
261 pcb = thr_act->machine.pcb;
55e303ae 262
0c530ab8 263 if (state == NULL) {
2d21ac55
A
264 /*
265 * new FPU state is 'invalid'.
266 * Deallocate the fp state if it exists.
267 */
268 simple_lock(&pcb->lock);
0c530ab8
A
269
270 ifps = pcb->ifps;
271 pcb->ifps = 0;
4452a7af 272
2d21ac55 273 simple_unlock(&pcb->lock);
0c530ab8
A
274
275 if (ifps != 0)
2d21ac55 276 fp_state_free(ifps);
0c530ab8 277 } else {
2d21ac55
A
278 /*
279 * Valid state. Allocate the fp state if there is none.
280 */
281 new_ifps = 0;
55e303ae 282 Retry:
2d21ac55 283 simple_lock(&pcb->lock);
0c530ab8
A
284
285 ifps = pcb->ifps;
2d21ac55
A
286 if (ifps == 0) {
287 if (new_ifps == 0) {
288 simple_unlock(&pcb->lock);
289 new_ifps = fp_state_alloc();
290 goto Retry;
55e303ae 291 }
2d21ac55
A
292 ifps = new_ifps;
293 new_ifps = 0;
294 pcb->ifps = ifps;
295 }
296 /*
297 * now copy over the new data.
298 */
299 bcopy((char *)&state->fpu_fcw,
0c530ab8 300 (char *)&ifps->fx_save_state, sizeof(struct x86_fx_save));
4452a7af 301
0c530ab8
A
302 /* XXX The layout of the state set from user-space may need to be
303 * validated for consistency.
304 */
305 ifps->fp_save_layout = thread_is_64bit(thr_act) ? FXSAVE64 : FXSAVE32;
2d21ac55
A
306 /* Mark the thread's floating point status as non-live. */
307 /* Temporarily disabled: radar 4647827
308 * ifps->fp_valid = TRUE;
309 */
310
0c530ab8
A
311 /*
312 * Clear any reserved bits in the MXCSR to prevent a GPF
313 * when issuing an FXRSTOR.
314 */
2d21ac55 315 ifps->fx_save_state.fx_MXCSR &= mxcsr_capability_mask;
6601e61a 316
2d21ac55 317 simple_unlock(&pcb->lock);
0c530ab8 318
2d21ac55
A
319 if (new_ifps != 0)
320 fp_state_free(new_ifps);
0c530ab8 321 }
55e303ae
A
322 return KERN_SUCCESS;
323}
324
325/*
326 * Get the floating-point state for a thread.
327 * If the thread is not the current thread, it is
328 * not running (held). Locking needed against
329 * concurrent fpu_set_state or fpu_get_state.
330 */
331kern_return_t
332fpu_get_fxstate(
2d21ac55 333 thread_t thr_act,
0c530ab8 334 thread_state_t tstate)
55e303ae 335{
0c530ab8
A
336 struct x86_fpsave_state *ifps;
337 x86_float_state64_t *state;
338 kern_return_t ret = KERN_FAILURE;
339 pcb_t pcb;
55e303ae 340
0c530ab8 341 if (fp_kind == FP_NO)
2d21ac55 342 return KERN_FAILURE;
0c530ab8
A
343
344 state = (x86_float_state64_t *)tstate;
55e303ae 345
91447636
A
346 assert(thr_act != THREAD_NULL);
347 pcb = thr_act->machine.pcb;
55e303ae
A
348
349 simple_lock(&pcb->lock);
0c530ab8
A
350
351 ifps = pcb->ifps;
55e303ae 352 if (ifps == 0) {
2d21ac55 353 /*
0c530ab8
A
354 * No valid floating-point state.
355 */
2d21ac55 356 bcopy((char *)&starting_fp_state.fx_save_state,
0c530ab8
A
357 (char *)&state->fpu_fcw, sizeof(struct x86_fx_save));
358
359 simple_unlock(&pcb->lock);
6601e61a 360
0c530ab8
A
361 return KERN_SUCCESS;
362 }
363 /*
364 * Make sure we`ve got the latest fp state info
365 * If the live fpu state belongs to our target
366 */
2d21ac55
A
367 if (thr_act == current_thread()) {
368 boolean_t intr;
8f6c56a5 369
0c530ab8 370 intr = ml_set_interrupts_enabled(FALSE);
89b3af67 371
0c530ab8
A
372 clear_ts();
373 fp_save(thr_act);
374 clear_fpu();
6601e61a 375
0c530ab8 376 (void)ml_set_interrupts_enabled(intr);
6601e61a 377 }
0c530ab8
A
378 if (ifps->fp_valid) {
379 bcopy((char *)&ifps->fx_save_state,
380 (char *)&state->fpu_fcw, sizeof(struct x86_fx_save));
381 ret = KERN_SUCCESS;
6601e61a 382 }
0c530ab8 383 simple_unlock(&pcb->lock);
21362eb3 384
0c530ab8 385 return ret;
6601e61a 386}
21362eb3 387
0c530ab8 388
2d21ac55 389
6601e61a 390/*
0c530ab8
A
391 * the child thread is 'stopped' with the thread
392 * mutex held and is currently not known by anyone
393 * so no way for fpu state to get manipulated by an
394 * outside agency -> no need for pcb lock
6601e61a 395 */
0c530ab8
A
396
397void
398fpu_dup_fxstate(
399 thread_t parent,
400 thread_t child)
6601e61a 401{
0c530ab8
A
402 struct x86_fpsave_state *new_ifps = NULL;
403 boolean_t intr;
404 pcb_t ppcb;
21362eb3 405
0c530ab8 406 ppcb = parent->machine.pcb;
21362eb3 407
0c530ab8
A
408 if (ppcb->ifps == NULL)
409 return;
4452a7af 410
0c530ab8
A
411 if (child->machine.pcb->ifps)
412 panic("fpu_dup_fxstate: child's ifps non-null");
4452a7af 413
0c530ab8 414 new_ifps = fp_state_alloc();
5d5c5d0d 415
0c530ab8 416 simple_lock(&ppcb->lock);
6601e61a 417
0c530ab8
A
418 if (ppcb->ifps != NULL) {
419 /*
420 * Make sure we`ve got the latest fp state info
421 */
422 intr = ml_set_interrupts_enabled(FALSE);
6601e61a 423
0c530ab8
A
424 clear_ts();
425 fp_save(parent);
426 clear_fpu();
6601e61a 427
0c530ab8 428 (void)ml_set_interrupts_enabled(intr);
6601e61a 429
0c530ab8
A
430 if (ppcb->ifps->fp_valid) {
431 child->machine.pcb->ifps = new_ifps;
432
433 bcopy((char *)&(ppcb->ifps->fx_save_state),
434 (char *)&(child->machine.pcb->ifps->fx_save_state), sizeof(struct x86_fx_save));
435
436 new_ifps->fp_save_layout = ppcb->ifps->fp_save_layout;
2d21ac55
A
437 /* Mark the new fp saved state as non-live. */
438 /* Temporarily disabled: radar 4647827
439 * new_ifps->fp_valid = TRUE;
440 */
0c530ab8
A
441 /*
442 * Clear any reserved bits in the MXCSR to prevent a GPF
443 * when issuing an FXRSTOR.
444 */
445 new_ifps->fx_save_state.fx_MXCSR &= mxcsr_capability_mask;
446 new_ifps = NULL;
447 }
6601e61a 448 }
0c530ab8 449 simple_unlock(&ppcb->lock);
89b3af67 450
0c530ab8
A
451 if (new_ifps != NULL)
452 fp_state_free(new_ifps);
6601e61a 453}
4452a7af 454
0c530ab8 455
1c79356b
A
456/*
457 * Initialize FPU.
458 *
1c79356b
A
459 */
460void
461fpinit(void)
462{
463 unsigned short control;
464
1c79356b
A
465 clear_ts();
466 fninit();
467 fnstcw(&control);
468 control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
0c530ab8 469 control |= (FPC_PC_64 | /* Set precision */
1c79356b
A
470 FPC_RC_RN | /* round-to-nearest */
471 FPC_ZE | /* Suppress zero-divide */
472 FPC_OE | /* and overflow */
473 FPC_UE | /* underflow */
474 FPC_IE | /* Allow NaNQs and +-INF */
475 FPC_DE | /* Allow denorms as operands */
476 FPC_PE); /* No trap for precision loss */
477 fldcw(control);
0c530ab8
A
478
479 /* Initialize SSE/SSE2 */
2d21ac55 480 __builtin_ia32_ldmxcsr(0x1f80);
b0d623f7 481}
1c79356b
A
482
483/*
484 * Coprocessor not present.
485 */
486
487void
488fpnoextflt(void)
489{
0c530ab8 490 boolean_t intr;
2d21ac55
A
491 thread_t thr_act;
492 pcb_t pcb;
493 struct x86_fpsave_state *ifps = 0;
494
495 thr_act = current_thread();
496 pcb = thr_act->machine.pcb;
497
498 if (pcb->ifps == 0 && !get_interrupt_level())
499 ifps = fp_state_alloc();
4452a7af 500
0c530ab8
A
501 intr = ml_set_interrupts_enabled(FALSE);
502
503 clear_ts(); /* Enable FPU use */
504
505 if (get_interrupt_level()) {
506 /*
507 * Save current coprocessor context if valid
508 * Initialize coprocessor live context
509 */
2d21ac55 510 fp_save(thr_act);
0c530ab8
A
511 fpinit();
512 } else {
2d21ac55
A
513 if (pcb->ifps == 0) {
514 pcb->ifps = ifps;
515 ifps = 0;
516 }
0c530ab8
A
517 /*
518 * Load this thread`s state into coprocessor live context.
519 */
2d21ac55 520 fp_load(thr_act);
0c530ab8 521 }
0c530ab8 522 (void)ml_set_interrupts_enabled(intr);
2d21ac55
A
523
524 if (ifps)
525 fp_state_free(ifps);
1c79356b
A
526}
527
528/*
529 * FPU overran end of segment.
530 * Re-initialize FPU. Floating point state is not valid.
531 */
532
533void
534fpextovrflt(void)
535{
0c530ab8
A
536 thread_t thr_act = current_thread();
537 pcb_t pcb;
538 struct x86_fpsave_state *ifps;
539 boolean_t intr;
540
541 intr = ml_set_interrupts_enabled(FALSE);
542
543 if (get_interrupt_level())
2d21ac55 544 panic("FPU segment overrun exception at interrupt context\n");
0c530ab8
A
545 if (current_task() == kernel_task)
546 panic("FPU segment overrun exception in kernel thread context\n");
1c79356b 547
1c79356b
A
548 /*
549 * This is a non-recoverable error.
550 * Invalidate the thread`s FPU state.
551 */
91447636 552 pcb = thr_act->machine.pcb;
1c79356b 553 simple_lock(&pcb->lock);
0c530ab8
A
554 ifps = pcb->ifps;
555 pcb->ifps = 0;
1c79356b
A
556 simple_unlock(&pcb->lock);
557
558 /*
559 * Re-initialize the FPU.
560 */
561 clear_ts();
562 fninit();
563
564 /*
565 * And disable access.
566 */
567 clear_fpu();
568
0c530ab8
A
569 (void)ml_set_interrupts_enabled(intr);
570
1c79356b 571 if (ifps)
91447636 572 zfree(ifps_zone, ifps);
1c79356b
A
573
574 /*
575 * Raise exception.
576 */
577 i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
578 /*NOTREACHED*/
579}
580
581/*
582 * FPU error. Called by AST.
583 */
584
585void
586fpexterrflt(void)
587{
0c530ab8
A
588 thread_t thr_act = current_thread();
589 struct x86_fpsave_state *ifps = thr_act->machine.pcb->ifps;
590 boolean_t intr;
591
592 intr = ml_set_interrupts_enabled(FALSE);
593
594 if (get_interrupt_level())
595 panic("FPU error exception at interrupt context\n");
596 if (current_task() == kernel_task)
597 panic("FPU error exception in kernel thread context\n");
1c79356b 598
1c79356b
A
599 /*
600 * Save the FPU state and turn off the FPU.
601 */
602 fp_save(thr_act);
1c79356b 603
0c530ab8
A
604 (void)ml_set_interrupts_enabled(intr);
605
1c79356b
A
606 /*
607 * Raise FPU exception.
0c530ab8 608 * Locking not needed on pcb->ifps,
1c79356b
A
609 * since thread is running.
610 */
611 i386_exception(EXC_ARITHMETIC,
612 EXC_I386_EXTERR,
0c530ab8
A
613 ifps->fx_save_state.fx_status);
614
1c79356b
A
615 /*NOTREACHED*/
616}
617
618/*
619 * Save FPU state.
620 *
621 * Locking not needed:
622 * . if called from fpu_get_state, pcb already locked.
623 * . if called from fpnoextflt or fp_intr, we are single-cpu
624 * . otherwise, thread is running.
0c530ab8 625 * N.B.: Must be called with interrupts disabled
1c79356b 626 */
0c530ab8 627
1c79356b
A
628void
629fp_save(
91447636 630 thread_t thr_act)
1c79356b 631{
0c530ab8
A
632 pcb_t pcb = thr_act->machine.pcb;
633 struct x86_fpsave_state *ifps = pcb->ifps;
634
1c79356b 635 if (ifps != 0 && !ifps->fp_valid) {
0c530ab8
A
636 assert((get_cr0() & CR0_TS) == 0);
637 /* registers are in FPU */
638 ifps->fp_valid = TRUE;
639
b0d623f7 640#if defined(__i386__)
0c530ab8
A
641 if (!thread_is_64bit(thr_act)) {
642 /* save the compatibility/legacy mode XMM+x87 state */
643 fxsave(&ifps->fx_save_state);
644 ifps->fp_save_layout = FXSAVE32;
645 }
646 else {
647 fxsave64(&ifps->fx_save_state);
648 ifps->fp_save_layout = FXSAVE64;
649 }
b0d623f7
A
650#elif defined(__x86_64__)
651 fxsave(&ifps->fx_save_state);
652 ifps->fp_save_layout = thread_is_64bit(thr_act) ? FXSAVE64 : FXSAVE32;
653#endif
1c79356b
A
654 }
655}
656
657/*
658 * Restore FPU state from PCB.
659 *
660 * Locking not needed; always called on the current thread.
661 */
662
663void
664fp_load(
91447636 665 thread_t thr_act)
1c79356b 666{
0c530ab8
A
667 pcb_t pcb = thr_act->machine.pcb;
668 struct x86_fpsave_state *ifps;
669
670 ifps = pcb->ifps;
671 if (ifps == 0 || ifps->fp_valid == FALSE) {
672 if (ifps == 0) {
673 /* FIXME: This allocation mechanism should be revised
674 * for scenarios where interrupts are disabled.
675 */
676 ifps = fp_state_alloc();
677 pcb->ifps = ifps;
678 }
679 fpinit();
1c79356b 680 } else {
0c530ab8 681 assert(ifps->fp_save_layout == FXSAVE32 || ifps->fp_save_layout == FXSAVE64);
b0d623f7 682#if defined(__i386__)
0c530ab8
A
683 if (ifps->fp_save_layout == FXSAVE32) {
684 /* Restore the compatibility/legacy mode XMM+x87 state */
685 fxrstor(&ifps->fx_save_state);
686 }
687 else if (ifps->fp_save_layout == FXSAVE64) {
688 fxrstor64(&ifps->fx_save_state);
689 }
b0d623f7
A
690#elif defined(__x86_64__)
691 fxrstor(&ifps->fx_save_state);
692#endif
1c79356b
A
693 }
694 ifps->fp_valid = FALSE; /* in FPU */
695}
696
55e303ae 697
1c79356b
A
698
699/*
91447636 700 * fpflush(thread_t)
1c79356b
A
701 * Flush the current act's state, if needed
702 * (used by thread_terminate_self to ensure fp faults
703 * aren't satisfied by overly general trap code in the
704 * context of the reaper thread)
705 */
706void
91447636 707fpflush(__unused thread_t thr_act)
1c79356b 708{
1c79356b 709 /* not needed on MP x86s; fp not lazily evaluated */
1c79356b
A
710}
711
1c79356b 712/*
0c530ab8
A
713 * SSE arithmetic exception handling code.
714 * Basically the same as the x87 exception handler with a different subtype
1c79356b
A
715 */
716
717void
0c530ab8 718fpSSEexterrflt(void)
1c79356b 719{
0c530ab8
A
720 thread_t thr_act = current_thread();
721 struct x86_fpsave_state *ifps = thr_act->machine.pcb->ifps;
722 boolean_t intr;
4452a7af 723
0c530ab8
A
724 intr = ml_set_interrupts_enabled(FALSE);
725
726 if (get_interrupt_level())
727 panic("SSE exception at interrupt context\n");
728 if (current_task() == kernel_task)
729 panic("SSE exception in kernel thread context\n");
1c79356b
A
730
731 /*
0c530ab8 732 * Save the FPU state and turn off the FPU.
1c79356b 733 */
1c79356b 734 fp_save(thr_act);
1c79356b 735
0c530ab8 736 (void)ml_set_interrupts_enabled(intr);
1c79356b 737 /*
0c530ab8
A
738 * Raise FPU exception.
739 * Locking not needed on pcb->ifps,
740 * since thread is running.
1c79356b 741 */
0c530ab8
A
742 assert(ifps->fp_save_layout == FXSAVE32 || ifps->fp_save_layout == FXSAVE64);
743 i386_exception(EXC_ARITHMETIC,
744 EXC_I386_SSEEXTERR,
b0d623f7 745 ifps->fx_save_state.fx_MXCSR);
0c530ab8
A
746 /*NOTREACHED*/
747}
748
749
750void
751fp_setvalid(boolean_t value) {
752 thread_t thr_act = current_thread();
753 struct x86_fpsave_state *ifps = thr_act->machine.pcb->ifps;
754
755 if (ifps) {
756 ifps->fp_valid = value;
757
758 if (value == TRUE)
759 clear_fpu();
760 }
1c79356b 761}