]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/fpu.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / osfmk / i386 / fpu.c
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
8f6c56a5 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
8f6c56a5
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.
14 *
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
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
8ad349bb 24 * limitations under the License.
8f6c56a5
A
25 *
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
73#include <i386/thread.h>
74#include <i386/fpu.h>
75#include <i386/trap.h>
8f6c56a5 76#include <i386/pio.h>
55e303ae 77#include <i386/cpuid.h>
1c79356b
A
78#include <i386/misc_protos.h>
79
8f6c56a5
A
80#if 0
81#include <i386/ipl.h>
82extern int curr_ipl;
83#define ASSERT_IPL(L) \
84{ \
85 if (curr_ipl != L) { \
86 printf("IPL is %d, expected %d\n", curr_ipl, L); \
87 panic("fpu: wrong ipl"); \
88 } \
89}
90#else
91#define ASSERT_IPL(L)
92#endif
93
94int fp_kind = FP_387; /* 80387 present */
1c79356b
A
95zone_t ifps_zone; /* zone for FPU save area */
96
8f6c56a5
A
97#define clear_fpu() \
98 { \
99 set_ts(); \
100 }
101
55e303ae 102#define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
1c79356b
A
103
104/* Forward */
105
106extern void fpinit(void);
107extern void fp_save(
91447636 108 thread_t thr_act);
1c79356b 109extern void fp_load(
91447636 110 thread_t thr_act);
1c79356b
A
111
112/*
113 * Look for FPU and initialize it.
114 * Called on each CPU.
115 */
116void
117init_fpu(void)
118{
119 unsigned short status, control;
120
121 /*
122 * Check for FPU by initializing it,
123 * then trying to read the correct bit patterns from
124 * the control and status registers.
125 */
91447636 126 set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | CR0_NE); /* allow use of FPU */
1c79356b
A
127
128 fninit();
129 status = fnstsw();
130 fnstcw(&control);
131
132 if ((status & 0xff) == 0 &&
55e303ae
A
133 (control & 0x103f) == 0x3f)
134 {
8f6c56a5 135 fp_kind = FP_387; /* assume we have a 387 compatible instruction set */
55e303ae 136 /* Use FPU save/restore instructions if available */
8f6c56a5
A
137 if (cpuid_features() & CPUID_FEATURE_FXSR) {
138 fp_kind = FP_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);
145 }
146 printf(" opcodes\n");
147 }
55e303ae 148
1c79356b 149 /*
8f6c56a5 150 * Trap wait instructions. Turn off FPU for now.
1c79356b 151 */
8f6c56a5 152 set_cr0(get_cr0() | CR0_TS | CR0_MP);
1c79356b
A
153 }
154 else
155 {
156 /*
157 * NO FPU.
158 */
8f6c56a5
A
159 fp_kind = FP_NO;
160 set_cr0(get_cr0() | CR0_EM);
1c79356b
A
161 }
162}
163
164/*
165 * Initialize FP handling.
166 */
167void
168fpu_module_init(void)
169{
8f6c56a5
A
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");
1c79356b
A
174}
175
176/*
177 * Free a FPU save area.
178 * Called only when thread terminating - no locking necessary.
179 */
180void
91447636 181fpu_free(fps)
8f6c56a5 182 struct i386_fpsave_state *fps;
1c79356b 183{
8f6c56a5
A
184ASSERT_IPL(SPL0);
185 zfree(ifps_zone, fps);
1c79356b
A
186}
187
55e303ae
A
188/*
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.
196 */
197kern_return_t
198fpu_set_fxstate(
8f6c56a5
A
199 thread_t thr_act,
200 struct i386_float_state *state)
55e303ae 201{
8f6c56a5
A
202 register pcb_t pcb;
203 register struct i386_fpsave_state *ifps;
204 register struct i386_fpsave_state *new_ifps;
55e303ae 205
8f6c56a5 206ASSERT_IPL(SPL0);
55e303ae 207 if (fp_kind == FP_NO)
8f6c56a5 208 return KERN_FAILURE;
55e303ae 209
8f6c56a5
A
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);
213 }
214
91447636
A
215 assert(thr_act != THREAD_NULL);
216 pcb = thr_act->machine.pcb;
55e303ae 217
8f6c56a5
A
218 if (state->initialized == 0) {
219 /*
220 * new FPU state is 'invalid'.
221 * Deallocate the fp state if it exists.
222 */
223 simple_lock(&pcb->lock);
224 ifps = pcb->ims.ifps;
225 pcb->ims.ifps = 0;
226 simple_unlock(&pcb->lock);
227
228 if (ifps != 0) {
229 zfree(ifps_zone, ifps);
230 }
231 }
232 else {
233 /*
234 * Valid state. Allocate the fp state if there is none.
235 */
5d5c5d0d 236
8f6c56a5 237 new_ifps = 0;
55e303ae 238 Retry:
8f6c56a5
A
239 simple_lock(&pcb->lock);
240 ifps = pcb->ims.ifps;
241 if (ifps == 0) {
242 if (new_ifps == 0) {
243 simple_unlock(&pcb->lock);
244 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
245 assert(ALIGNED(new_ifps,16));
246 goto Retry;
55e303ae 247 }
8f6c56a5
A
248 ifps = new_ifps;
249 new_ifps = 0;
250 bzero((char *)ifps, sizeof *ifps);
251 pcb->ims.ifps = ifps;
252 }
55e303ae 253
8f6c56a5
A
254 /*
255 * now copy over the new data.
256 */
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);
260 if (new_ifps != 0)
261 zfree(ifps_zone, ifps);
5d5c5d0d 262 }
8f6c56a5 263
55e303ae
A
264 return KERN_SUCCESS;
265}
266
267/*
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.
272 */
273kern_return_t
274fpu_get_fxstate(
8f6c56a5
A
275 thread_t thr_act,
276 register struct i386_float_state *state)
55e303ae 277{
8f6c56a5
A
278 register pcb_t pcb;
279 register struct i386_fpsave_state *ifps;
55e303ae 280
8f6c56a5
A
281ASSERT_IPL(SPL0);
282 if (fp_kind == FP_NO) {
55e303ae 283 return KERN_FAILURE;
8f6c56a5
A
284 } else if (fp_kind == FP_387) {
285 return fpu_get_state(thr_act, state);
286 }
55e303ae 287
91447636
A
288 assert(thr_act != THREAD_NULL);
289 pcb = thr_act->machine.pcb;
55e303ae
A
290
291 simple_lock(&pcb->lock);
8f6c56a5 292 ifps = pcb->ims.ifps;
55e303ae 293 if (ifps == 0) {
8f6c56a5
A
294 /*
295 * No valid floating-point state.
296 */
297 simple_unlock(&pcb->lock);
298 bzero((char *)state, sizeof(struct i386_float_state));
299 return KERN_SUCCESS;
5d5c5d0d 300 }
8f6c56a5
A
301
302 /* Make sure we`ve got the latest fp state info */
303 /* If the live fpu state belongs to our target */
91447636 304 if (thr_act == current_thread())
55e303ae 305 {
8f6c56a5
A
306 clear_ts();
307 fp_save(thr_act);
308 clear_fpu();
309 }
1c79356b 310
8f6c56a5
A
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));
1c79356b 315
5d5c5d0d 316 simple_unlock(&pcb->lock);
1c79356b 317
8f6c56a5 318 return KERN_SUCCESS;
8ad349bb 319}
1c79356b 320
8ad349bb 321/*
8f6c56a5
A
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.
8ad349bb 326 */
8f6c56a5
A
327kern_return_t
328fpu_set_state(
329 thread_t thr_act,
330 struct i386_float_state *state)
8ad349bb 331{
8f6c56a5
A
332 register pcb_t pcb;
333 register struct i386_fpsave_state *ifps;
334 register struct i386_fpsave_state *new_ifps;
1c79356b 335
8f6c56a5
A
336ASSERT_IPL(SPL0);
337 if (fp_kind == FP_NO)
338 return KERN_FAILURE;
c0fea474 339
8f6c56a5
A
340 assert(thr_act != THREAD_NULL);
341 pcb = thr_act->machine.pcb;
c0fea474 342
8f6c56a5
A
343 if (state->initialized == 0) {
344 /*
345 * new FPU state is 'invalid'.
346 * Deallocate the fp state if it exists.
347 */
348 simple_lock(&pcb->lock);
349 ifps = pcb->ims.ifps;
350 pcb->ims.ifps = 0;
351 simple_unlock(&pcb->lock);
352
353 if (ifps != 0) {
354 zfree(ifps_zone, ifps);
355 }
356 }
357 else {
358 /*
359 * Valid state. Allocate the fp state if there is none.
360 */
361 register struct i386_fp_save *user_fp_state;
362 register struct i386_fp_regs *user_fp_regs;
1c79356b 363
8f6c56a5
A
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)];
8ad349bb 367
8f6c56a5
A
368 new_ifps = 0;
369 Retry:
370 simple_lock(&pcb->lock);
371 ifps = pcb->ims.ifps;
372 if (ifps == 0) {
373 if (new_ifps == 0) {
374 simple_unlock(&pcb->lock);
375 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
376 assert(ALIGNED(new_ifps,16));
377 goto Retry;
378 }
379 ifps = new_ifps;
380 new_ifps = 0;
381 bzero((char *)ifps, sizeof *ifps); // zero ALL fields first
382 pcb->ims.ifps = ifps;
383 }
1c79356b 384
8f6c56a5
A
385 /*
386 * Ensure that reserved parts of the environment are 0.
387 */
388 bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
389
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);
401 if (new_ifps != 0)
402 zfree(ifps_zone, ifps);
403 }
8ad349bb 404
8f6c56a5
A
405 return KERN_SUCCESS;
406}
8ad349bb 407
8f6c56a5
A
408/*
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.
413 */
414kern_return_t
415fpu_get_state(
416 thread_t thr_act,
417 register struct i386_float_state *state)
418{
419 register pcb_t pcb;
420 register struct i386_fpsave_state *ifps;
8ad349bb 421
8f6c56a5
A
422ASSERT_IPL(SPL0);
423 if (fp_kind == FP_NO)
424 return KERN_FAILURE;
5d5c5d0d 425
8f6c56a5
A
426 assert(thr_act != THREAD_NULL);
427 pcb = thr_act->machine.pcb;
5d5c5d0d 428
8f6c56a5
A
429 simple_lock(&pcb->lock);
430 ifps = pcb->ims.ifps;
431 if (ifps == 0) {
432 /*
433 * No valid floating-point state.
434 */
435 simple_unlock(&pcb->lock);
436 bzero((char *)state, sizeof(struct i386_float_state));
437 return KERN_SUCCESS;
8ad349bb 438 }
8ad349bb 439
8f6c56a5
A
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())
443 {
444 clear_ts();
445 fp_save(thr_act);
446 clear_fpu();
447 }
448
449 state->fpkind = fp_kind;
450 state->exc_status = 0;
c0fea474 451
8f6c56a5
A
452 {
453 register struct i386_fp_save *user_fp_state;
454 register struct i386_fp_regs *user_fp_regs;
455
456 state->initialized = ifps->fp_valid;
457
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)];
461
462 /*
463 * Ensure that reserved parts of the environment are 0.
464 */
465 bzero((char *)user_fp_state, sizeof(struct i386_fp_save));
466
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;
476 }
477 simple_unlock(&pcb->lock);
478
479 return KERN_SUCCESS;
480}
5d5c5d0d 481
1c79356b
A
482/*
483 * Initialize FPU.
484 *
8f6c56a5
A
485 * Raise exceptions for:
486 * invalid operation
487 * divide by zero
488 * overflow
489 *
490 * Use 53-bit precision.
1c79356b
A
491 */
492void
493fpinit(void)
494{
495 unsigned short control;
496
8f6c56a5 497ASSERT_IPL(SPL0);
1c79356b
A
498 clear_ts();
499 fninit();
500 fnstcw(&control);
501 control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
8f6c56a5 502 control |= (FPC_PC_53 | /* Set precision */
1c79356b
A
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 */
510 fldcw(control);
511}
512
513/*
514 * Coprocessor not present.
515 */
516
517void
518fpnoextflt(void)
519{
8f6c56a5
A
520 /*
521 * Enable FPU use.
522 */
523ASSERT_IPL(SPL0);
524 clear_ts();
5d5c5d0d 525
8f6c56a5
A
526 /*
527 * Load this thread`s state into the FPU.
528 */
529 fp_load(current_thread());
1c79356b
A
530}
531
532/*
533 * FPU overran end of segment.
534 * Re-initialize FPU. Floating point state is not valid.
535 */
536
537void
538fpextovrflt(void)
539{
8f6c56a5
A
540 register thread_t thr_act = current_thread();
541 register pcb_t pcb;
542 register struct i386_fpsave_state *ifps;
1c79356b 543
1c79356b
A
544 /*
545 * This is a non-recoverable error.
546 * Invalidate the thread`s FPU state.
547 */
91447636 548 pcb = thr_act->machine.pcb;
1c79356b 549 simple_lock(&pcb->lock);
8f6c56a5
A
550 ifps = pcb->ims.ifps;
551 pcb->ims.ifps = 0;
1c79356b
A
552 simple_unlock(&pcb->lock);
553
554 /*
555 * Re-initialize the FPU.
556 */
557 clear_ts();
558 fninit();
559
560 /*
561 * And disable access.
562 */
563 clear_fpu();
564
565 if (ifps)
91447636 566 zfree(ifps_zone, ifps);
1c79356b
A
567
568 /*
569 * Raise exception.
570 */
571 i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
572 /*NOTREACHED*/
573}
574
575/*
576 * FPU error. Called by AST.
577 */
578
579void
580fpexterrflt(void)
581{
8f6c56a5 582 register thread_t thr_act = current_thread();
1c79356b 583
8f6c56a5 584ASSERT_IPL(SPL0);
1c79356b
A
585 /*
586 * Save the FPU state and turn off the FPU.
587 */
588 fp_save(thr_act);
1c79356b
A
589
590 /*
591 * Raise FPU exception.
8f6c56a5 592 * Locking not needed on pcb->ims.ifps,
1c79356b
A
593 * since thread is running.
594 */
595 i386_exception(EXC_ARITHMETIC,
596 EXC_I386_EXTERR,
8f6c56a5 597 thr_act->machine.pcb->ims.ifps->fp_save_state.fp_status);
1c79356b
A
598 /*NOTREACHED*/
599}
600
601/*
602 * Save FPU state.
603 *
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.
608 */
1c79356b
A
609void
610fp_save(
91447636 611 thread_t thr_act)
1c79356b 612{
8f6c56a5
A
613 register pcb_t pcb = thr_act->machine.pcb;
614 register struct i386_fpsave_state *ifps = pcb->ims.ifps;
1c79356b 615 if (ifps != 0 && !ifps->fp_valid) {
8f6c56a5
A
616 /* registers are in FPU */
617 ifps->fp_valid = TRUE;
618 ifps->fp_save_flavor = FP_387;
619 if (FXSAFE()) {
620 fxsave(&ifps->fx_save_state); // save the SSE2/Fp state in addition is enabled
621 ifps->fp_save_flavor = FP_FXSR;
622 }
623 fnsave(&ifps->fp_save_state); // also update the old save area for now...
1c79356b
A
624 }
625}
626
627/*
628 * Restore FPU state from PCB.
629 *
630 * Locking not needed; always called on the current thread.
631 */
632
633void
634fp_load(
91447636 635 thread_t thr_act)
1c79356b 636{
8f6c56a5
A
637 register pcb_t pcb = thr_act->machine.pcb;
638 register struct i386_fpsave_state *ifps;
639
640ASSERT_IPL(SPL0);
641 ifps = pcb->ims.ifps;
642 if (ifps == 0) {
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;
647 fpinit();
648#if 1
649/*
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.
653 */
654 } else if (ifps->fp_valid == 2) {
655 /* delayed exception pending */
656
657 ifps->fp_valid = TRUE;
658 clear_fpu();
659 /*
660 * Raise FPU exception.
661 * Locking not needed on pcb->ims.ifps,
662 * since thread is running.
663 */
664 i386_exception(EXC_ARITHMETIC,
665 EXC_I386_EXTERR,
666 thr_act->machine.pcb->ims.ifps->fp_save_state.fp_status);
667 /*NOTREACHED*/
668#endif
1c79356b 669 } else {
8f6c56a5
A
670 if (ifps->fp_save_flavor == FP_FXSR) fxrstor(&ifps->fx_save_state);
671 else frstor(ifps->fp_save_state);
1c79356b
A
672 }
673 ifps->fp_valid = FALSE; /* in FPU */
674}
675
55e303ae 676
8f6c56a5
A
677/*
678 * Allocate and initialize FP state for current thread.
679 * Don't load state.
680 *
681 * Locking not needed; always called on the current thread.
682 */
683void
684fp_state_alloc(void)
685{
686 pcb_t pcb = current_thread()->machine.pcb;
687 struct i386_fpsave_state *ifps;
688
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;
693
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;
704
705}
706
1c79356b
A
707
708/*
91447636 709 * fpflush(thread_t)
1c79356b
A
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)
714 */
715void
91447636 716fpflush(__unused thread_t thr_act)
1c79356b 717{
1c79356b 718 /* not needed on MP x86s; fp not lazily evaluated */
1c79356b
A
719}
720
8f6c56a5 721
1c79356b 722/*
8f6c56a5
A
723 * Handle a coprocessor error interrupt on the AT386.
724 * This comes in on line 5 of the slave PIC at SPL1.
1c79356b
A
725 */
726
727void
8f6c56a5 728fpintr(void)
1c79356b 729{
8f6c56a5
A
730 spl_t s;
731 thread_t thr_act = current_thread();
5d5c5d0d 732
8f6c56a5
A
733ASSERT_IPL(SPL1);
734 /*
735 * Turn off the extended 'busy' line.
736 */
737 outb(0xf0, 0);
1c79356b
A
738
739 /*
8f6c56a5 740 * Save the FPU context to the thread using it.
1c79356b 741 */
8f6c56a5 742 clear_ts();
1c79356b 743 fp_save(thr_act);
8f6c56a5
A
744 fninit();
745 clear_fpu();
1c79356b
A
746
747 /*
8f6c56a5
A
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.
751 *
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.
1c79356b 755 */
8f6c56a5
A
756 s = splsched();
757 mp_disable_preemption();
758 ast_on(AST_I386_FP);
759 mp_enable_preemption();
760 splx(s);
1c79356b 761}