]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/fpu.c
xnu-517.9.5.tar.gz
[apple/xnu.git] / osfmk / i386 / fpu.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * @OSF_COPYRIGHT@
24 */
25 /*
26 * Mach Operating System
27 * Copyright (c) 1992-1990 Carnegie Mellon University
28 * All Rights Reserved.
29 *
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.
35 *
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.
39 *
40 * Carnegie Mellon requests users of this software to return to
41 *
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
46 *
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
49 */
50 /*
51 */
52
53 #include <cpus.h>
54 #include <platforms.h>
55
56 #include <mach/exception_types.h>
57 #include <mach/i386/thread_status.h>
58 #include <mach/i386/fp_reg.h>
59
60 #include <kern/mach_param.h>
61 #include <kern/thread.h>
62 #include <kern/zalloc.h>
63 #include <kern/misc_protos.h>
64 #include <kern/spl.h>
65 #include <kern/assert.h>
66
67 #include <i386/thread.h>
68 #include <i386/fpu.h>
69 #include <i386/trap.h>
70 #include <i386/pio.h>
71 #include <i386/cpuid.h>
72 #include <i386/misc_protos.h>
73
74 #if 0
75 #include <i386/ipl.h>
76 extern int curr_ipl;
77 #define ASSERT_IPL(L) \
78 { \
79 if (curr_ipl != L) { \
80 printf("IPL is %d, expected %d\n", curr_ipl, L); \
81 panic("fpu: wrong ipl"); \
82 } \
83 }
84 #else
85 #define ASSERT_IPL(L)
86 #endif
87
88 int fp_kind = FP_387; /* 80387 present */
89 zone_t ifps_zone; /* zone for FPU save area */
90
91 #if NCPUS == 1
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;
96
97
98 #define clear_fpu() \
99 { \
100 set_ts(); \
101 fp_act = THR_ACT_NULL; \
102 }
103
104 #else /* NCPUS > 1 */
105 #define clear_fpu() \
106 { \
107 set_ts(); \
108 }
109
110 #endif
111
112 #define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
113
114 /* Forward */
115
116 extern void fpinit(void);
117 extern void fp_save(
118 thread_act_t thr_act);
119 extern void fp_load(
120 thread_act_t thr_act);
121
122 /*
123 * Look for FPU and initialize it.
124 * Called on each CPU.
125 */
126 void
127 init_fpu(void)
128 {
129 unsigned short status, control;
130
131 /*
132 * Check for FPU by initializing it,
133 * then trying to read the correct bit patterns from
134 * the control and status registers.
135 */
136 set_cr0(get_cr0() & ~(CR0_EM|CR0_TS)); /* allow use of FPU */
137
138 fninit();
139 status = fnstsw();
140 fnstcw(&control);
141
142 if ((status & 0xff) == 0 &&
143 (control & 0x103f) == 0x3f)
144 {
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) {
148 fp_kind = FP_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);
155 }
156 printf(" opcodes\n");
157 }
158
159 /*
160 * Trap wait instructions. Turn off FPU for now.
161 */
162 set_cr0(get_cr0() | CR0_TS | CR0_MP);
163 }
164 else
165 {
166 /*
167 * NO FPU.
168 */
169 fp_kind = FP_NO;
170 set_cr0(get_cr0() | CR0_EM);
171 }
172 }
173
174 /*
175 * Initialize FP handling.
176 */
177 void
178 fpu_module_init(void)
179 {
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");
184 }
185
186 /*
187 * Free a FPU save area.
188 * Called only when thread terminating - no locking necessary.
189 */
190 void
191 fp_free(fps)
192 struct i386_fpsave_state *fps;
193 {
194 ASSERT_IPL(SPL0);
195 #if NCPUS == 1
196 if ((fp_act != THR_ACT_NULL) && (fp_act->mact.pcb->ims.ifps == fps)) {
197 /*
198 * Make sure we don't get FPU interrupts later for
199 * this thread
200 */
201 fwait();
202
203 /* Mark it free and disable access */
204 clear_fpu();
205 }
206 #endif /* NCPUS == 1 */
207 zfree(ifps_zone, (vm_offset_t) fps);
208 }
209
210 /*
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.
218 */
219 kern_return_t
220 fpu_set_fxstate(
221 thread_act_t thr_act,
222 struct i386_float_state *state)
223 {
224 register pcb_t pcb;
225 register struct i386_fpsave_state *ifps;
226 register struct i386_fpsave_state *new_ifps;
227
228 ASSERT_IPL(SPL0);
229 if (fp_kind == FP_NO)
230 return KERN_FAILURE;
231
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);
235 }
236
237 assert(thr_act != THR_ACT_NULL);
238 pcb = thr_act->mact.pcb;
239
240 #if NCPUS == 1
241
242 /*
243 * If this thread`s state is in the FPU,
244 * discard it; we are replacing the entire
245 * FPU state.
246 */
247 if (fp_act == thr_act) {
248 fwait(); /* wait for possible interrupt */
249 clear_fpu(); /* no state in FPU */
250 }
251 #endif
252
253 if (state->initialized == 0) {
254 /*
255 * new FPU state is 'invalid'.
256 * Deallocate the fp state if it exists.
257 */
258 simple_lock(&pcb->lock);
259 ifps = pcb->ims.ifps;
260 pcb->ims.ifps = 0;
261 simple_unlock(&pcb->lock);
262
263 if (ifps != 0) {
264 zfree(ifps_zone, (vm_offset_t) ifps);
265 }
266 }
267 else {
268 /*
269 * Valid state. Allocate the fp state if there is none.
270 */
271
272 new_ifps = 0;
273 Retry:
274 simple_lock(&pcb->lock);
275 ifps = pcb->ims.ifps;
276 if (ifps == 0) {
277 if (new_ifps == 0) {
278 simple_unlock(&pcb->lock);
279 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
280 assert(ALIGNED(new_ifps,16));
281 goto Retry;
282 }
283 ifps = new_ifps;
284 new_ifps = 0;
285 bzero((char *)ifps, sizeof *ifps);
286 pcb->ims.ifps = ifps;
287 }
288
289 /*
290 * now copy over the new data.
291 */
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);
295 if (new_ifps != 0)
296 zfree(ifps_zone, (vm_offset_t) ifps);
297 }
298
299 return KERN_SUCCESS;
300 }
301
302 /*
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.
307 */
308 kern_return_t
309 fpu_get_fxstate(
310 thread_act_t thr_act,
311 register struct i386_float_state *state)
312 {
313 register pcb_t pcb;
314 register struct i386_fpsave_state *ifps;
315
316 ASSERT_IPL(SPL0);
317 if (fp_kind == FP_NO)
318 return KERN_FAILURE;
319
320 assert(thr_act != THR_ACT_NULL);
321 pcb = thr_act->mact.pcb;
322
323 simple_lock(&pcb->lock);
324 ifps = pcb->ims.ifps;
325 if (ifps == 0) {
326 /*
327 * No valid floating-point state.
328 */
329 simple_unlock(&pcb->lock);
330 bzero((char *)state, sizeof(struct i386_float_state));
331 return KERN_SUCCESS;
332 }
333
334 /* Make sure we`ve got the latest fp state info */
335 /* If the live fpu state belongs to our target */
336 #if NCPUS == 1
337 if (thr_act == fp_act)
338 #else
339 if (thr_act == current_act())
340 #endif
341 {
342 clear_ts();
343 fp_save(thr_act);
344 clear_fpu();
345 }
346
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));
351
352 simple_unlock(&pcb->lock);
353
354 return KERN_SUCCESS;
355 }
356
357 /*
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.
362 */
363 kern_return_t
364 fpu_set_state(
365 thread_act_t thr_act,
366 struct i386_float_state *state)
367 {
368 register pcb_t pcb;
369 register struct i386_fpsave_state *ifps;
370 register struct i386_fpsave_state *new_ifps;
371
372 ASSERT_IPL(SPL0);
373 if (fp_kind == FP_NO)
374 return KERN_FAILURE;
375
376 assert(thr_act != THR_ACT_NULL);
377 pcb = thr_act->mact.pcb;
378
379 #if NCPUS == 1
380
381 /*
382 * If this thread`s state is in the FPU,
383 * discard it; we are replacing the entire
384 * FPU state.
385 */
386 if (fp_act == thr_act) {
387 fwait(); /* wait for possible interrupt */
388 clear_fpu(); /* no state in FPU */
389 }
390 #endif
391
392 if (state->initialized == 0) {
393 /*
394 * new FPU state is 'invalid'.
395 * Deallocate the fp state if it exists.
396 */
397 simple_lock(&pcb->lock);
398 ifps = pcb->ims.ifps;
399 pcb->ims.ifps = 0;
400 simple_unlock(&pcb->lock);
401
402 if (ifps != 0) {
403 zfree(ifps_zone, (vm_offset_t) ifps);
404 }
405 }
406 else {
407 /*
408 * Valid state. Allocate the fp state if there is none.
409 */
410 register struct i386_fp_save *user_fp_state;
411 register struct i386_fp_regs *user_fp_regs;
412
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)];
416
417 new_ifps = 0;
418 Retry:
419 simple_lock(&pcb->lock);
420 ifps = pcb->ims.ifps;
421 if (ifps == 0) {
422 if (new_ifps == 0) {
423 simple_unlock(&pcb->lock);
424 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
425 assert(ALIGNED(new_ifps,16));
426 goto Retry;
427 }
428 ifps = new_ifps;
429 new_ifps = 0;
430 bzero((char *)ifps, sizeof *ifps); // zero ALL fields first
431 pcb->ims.ifps = ifps;
432 }
433
434 /*
435 * Ensure that reserved parts of the environment are 0.
436 */
437 bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
438
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);
450 if (new_ifps != 0)
451 zfree(ifps_zone, (vm_offset_t) ifps);
452 }
453
454 return KERN_SUCCESS;
455 }
456
457 /*
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.
462 */
463 kern_return_t
464 fpu_get_state(
465 thread_act_t thr_act,
466 register struct i386_float_state *state)
467 {
468 register pcb_t pcb;
469 register struct i386_fpsave_state *ifps;
470
471 ASSERT_IPL(SPL0);
472 if (fp_kind == FP_NO)
473 return KERN_FAILURE;
474
475 assert(thr_act != THR_ACT_NULL);
476 pcb = thr_act->mact.pcb;
477
478 simple_lock(&pcb->lock);
479 ifps = pcb->ims.ifps;
480 if (ifps == 0) {
481 /*
482 * No valid floating-point state.
483 */
484 simple_unlock(&pcb->lock);
485 bzero((char *)state, sizeof(struct i386_float_state));
486 return KERN_SUCCESS;
487 }
488
489 /* Make sure we`ve got the latest fp state info */
490 /* If the live fpu state belongs to our target */
491 #if NCPUS == 1
492 if (thr_act == fp_act)
493 #else
494 if (thr_act == current_act())
495 #endif
496 {
497 clear_ts();
498 fp_save(thr_act);
499 clear_fpu();
500 }
501
502 state->fpkind = fp_kind;
503 state->exc_status = 0;
504
505 {
506 register struct i386_fp_save *user_fp_state;
507 register struct i386_fp_regs *user_fp_regs;
508
509 state->initialized = ifps->fp_valid;
510
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)];
514
515 /*
516 * Ensure that reserved parts of the environment are 0.
517 */
518 bzero((char *)user_fp_state, sizeof(struct i386_fp_save));
519
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;
529 }
530 simple_unlock(&pcb->lock);
531
532 return KERN_SUCCESS;
533 }
534
535 /*
536 * Initialize FPU.
537 *
538 * Raise exceptions for:
539 * invalid operation
540 * divide by zero
541 * overflow
542 *
543 * Use 53-bit precision.
544 */
545 void
546 fpinit(void)
547 {
548 unsigned short control;
549
550 ASSERT_IPL(SPL0);
551 clear_ts();
552 fninit();
553 fnstcw(&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 */
563 fldcw(control);
564 }
565
566 /*
567 * Coprocessor not present.
568 */
569
570 void
571 fpnoextflt(void)
572 {
573 /*
574 * Enable FPU use.
575 */
576 ASSERT_IPL(SPL0);
577 clear_ts();
578 #if NCPUS == 1
579
580 /*
581 * If this thread`s state is in the FPU, we are done.
582 */
583 if (fp_act == current_act())
584 return;
585
586 /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
587 * here if the current fpu instruction generates an error.
588 */
589 fwait();
590 /*
591 * If another thread`s state is in the FPU, save it.
592 */
593 if (fp_act != THR_ACT_NULL) {
594 fp_save(fp_act);
595 }
596
597 /*
598 * Give this thread the FPU.
599 */
600 fp_act = current_act();
601
602 #endif /* NCPUS == 1 */
603
604 /*
605 * Load this thread`s state into the FPU.
606 */
607 fp_load(current_act());
608 }
609
610 /*
611 * FPU overran end of segment.
612 * Re-initialize FPU. Floating point state is not valid.
613 */
614
615 void
616 fpextovrflt(void)
617 {
618 register thread_act_t thr_act = current_act();
619 register pcb_t pcb;
620 register struct i386_fpsave_state *ifps;
621
622 #if NCPUS == 1
623
624 /*
625 * Is exception for the currently running thread?
626 */
627 if (fp_act != thr_act) {
628 /* Uh oh... */
629 panic("fpextovrflt");
630 }
631 #endif
632
633 /*
634 * This is a non-recoverable error.
635 * Invalidate the thread`s FPU state.
636 */
637 pcb = thr_act->mact.pcb;
638 simple_lock(&pcb->lock);
639 ifps = pcb->ims.ifps;
640 pcb->ims.ifps = 0;
641 simple_unlock(&pcb->lock);
642
643 /*
644 * Re-initialize the FPU.
645 */
646 clear_ts();
647 fninit();
648
649 /*
650 * And disable access.
651 */
652 clear_fpu();
653
654 if (ifps)
655 zfree(ifps_zone, (vm_offset_t) ifps);
656
657 /*
658 * Raise exception.
659 */
660 i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
661 /*NOTREACHED*/
662 }
663
664 /*
665 * FPU error. Called by AST.
666 */
667
668 void
669 fpexterrflt(void)
670 {
671 register thread_act_t thr_act = current_act();
672
673 ASSERT_IPL(SPL0);
674 #if NCPUS == 1
675 /*
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.
679 */
680 if (fp_act != THR_ACT_NULL) {
681 panic("fpexterrflt");
682 return;
683 }
684
685 /*
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).
691 */
692 if (fp_intr_act != thr_act) {
693 if (fp_intr_act == THR_ACT_NULL) {
694 panic("fpexterrflt: fp_intr_act == THR_ACT_NULL");
695 return;
696 }
697 fp_intr_act->mact.pcb->ims.ifps->fp_valid = 2;
698 fp_intr_act = THR_ACT_NULL;
699 return;
700 }
701 fp_intr_act = THR_ACT_NULL;
702 #else /* NCPUS == 1 */
703 /*
704 * Save the FPU state and turn off the FPU.
705 */
706 fp_save(thr_act);
707 #endif /* NCPUS == 1 */
708
709 /*
710 * Raise FPU exception.
711 * Locking not needed on pcb->ims.ifps,
712 * since thread is running.
713 */
714 i386_exception(EXC_ARITHMETIC,
715 EXC_I386_EXTERR,
716 thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
717 /*NOTREACHED*/
718 }
719
720 /*
721 * Save FPU state.
722 *
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.
727 */
728 void
729 fp_save(
730 thread_act_t thr_act)
731 {
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;
738 if (FXSAFE()) {
739 fxsave(&ifps->fx_save_state); // save the SSE2/Fp state in addition is enabled
740 ifps->fp_save_flavor = FP_FXSR;
741 }
742 fnsave(&ifps->fp_save_state); // also update the old save area for now...
743 }
744 }
745
746 /*
747 * Restore FPU state from PCB.
748 *
749 * Locking not needed; always called on the current thread.
750 */
751
752 void
753 fp_load(
754 thread_act_t thr_act)
755 {
756 register pcb_t pcb = thr_act->mact.pcb;
757 register struct i386_fpsave_state *ifps;
758
759 ASSERT_IPL(SPL0);
760 ifps = pcb->ims.ifps;
761 if (ifps == 0) {
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;
766 fpinit();
767 #if 1
768 /*
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.
772 */
773 } else if (ifps->fp_valid == 2) {
774 /* delayed exception pending */
775
776 ifps->fp_valid = TRUE;
777 clear_fpu();
778 /*
779 * Raise FPU exception.
780 * Locking not needed on pcb->ims.ifps,
781 * since thread is running.
782 */
783 i386_exception(EXC_ARITHMETIC,
784 EXC_I386_EXTERR,
785 thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
786 /*NOTREACHED*/
787 #endif
788 } else {
789 if (ifps->fp_save_flavor == FP_FXSR) fxrstor(&ifps->fx_save_state);
790 else frstor(ifps->fp_save_state);
791 }
792 ifps->fp_valid = FALSE; /* in FPU */
793 }
794
795
796 /*
797 * Allocate and initialize FP state for current thread.
798 * Don't load state.
799 *
800 * Locking not needed; always called on the current thread.
801 */
802 void
803 fp_state_alloc(void)
804 {
805 pcb_t pcb = current_act()->mact.pcb;
806 struct i386_fpsave_state *ifps;
807
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;
812
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;
823
824 }
825
826
827 /*
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)
833 */
834 void
835 fpflush(thread_act_t thr_act)
836 {
837 #if NCPUS == 1
838 if (fp_act && thr_act == fp_act) {
839 clear_ts();
840 fwait();
841 clear_fpu();
842 }
843 #else
844 /* not needed on MP x86s; fp not lazily evaluated */
845 #endif
846 }
847
848
849 /*
850 * Handle a coprocessor error interrupt on the AT386.
851 * This comes in on line 5 of the slave PIC at SPL1.
852 */
853
854 void
855 fpintr(void)
856 {
857 spl_t s;
858 thread_act_t thr_act = current_act();
859
860 ASSERT_IPL(SPL1);
861 /*
862 * Turn off the extended 'busy' line.
863 */
864 outb(0xf0, 0);
865
866 /*
867 * Save the FPU context to the thread using it.
868 */
869 #if NCPUS == 1
870 if (fp_act == THR_ACT_NULL) {
871 printf("fpintr: FPU not belonging to anyone!\n");
872 clear_ts();
873 fninit();
874 clear_fpu();
875 return;
876 }
877
878 if (fp_act != thr_act) {
879 /*
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).
883 */
884 clear_ts();
885 fp_save(fp_act);
886 fp_act->mact.pcb->ims.ifps->fp_valid = 2;
887 fninit();
888 clear_fpu();
889 /* leave fp_intr_act THR_ACT_NULL */
890 return;
891 }
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 */
896
897 clear_ts();
898 fp_save(thr_act);
899 fninit();
900 clear_fpu();
901
902 /*
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.
906 *
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.
910 */
911 s = splsched();
912 mp_disable_preemption();
913 ast_on(AST_I386_FP);
914 mp_enable_preemption();
915 splx(s);
916 }