]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/fpu.c
xnu-517.3.15.tar.gz
[apple/xnu.git] / osfmk / i386 / fpu.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
43866e37
A
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
1c79356b
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25/*
26 * @OSF_COPYRIGHT@
27 */
28/*
29 * Mach Operating System
30 * Copyright (c) 1992-1990 Carnegie Mellon University
31 * All Rights Reserved.
32 *
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
38 *
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
42 *
43 * Carnegie Mellon requests users of this software to return to
44 *
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
49 *
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
52 */
53/*
54 */
55
56#include <cpus.h>
57#include <platforms.h>
58
59#include <mach/exception_types.h>
60#include <mach/i386/thread_status.h>
61#include <mach/i386/fp_reg.h>
62
63#include <kern/mach_param.h>
64#include <kern/thread.h>
65#include <kern/zalloc.h>
66#include <kern/misc_protos.h>
67#include <kern/spl.h>
68#include <kern/assert.h>
69
70#include <i386/thread.h>
71#include <i386/fpu.h>
72#include <i386/trap.h>
73#include <i386/pio.h>
55e303ae 74#include <i386/cpuid.h>
1c79356b
A
75#include <i386/misc_protos.h>
76
77#if 0
78#include <i386/ipl.h>
79extern int curr_ipl;
80#define ASSERT_IPL(L) \
81{ \
82 if (curr_ipl != L) { \
83 printf("IPL is %d, expected %d\n", curr_ipl, L); \
84 panic("fpu: wrong ipl"); \
85 } \
86}
87#else
88#define ASSERT_IPL(L)
89#endif
90
91int fp_kind = FP_387; /* 80387 present */
92zone_t ifps_zone; /* zone for FPU save area */
93
94#if NCPUS == 1
95volatile thread_act_t fp_act = THR_ACT_NULL;
96 /* thread whose state is in FPU */
97 /* always THR_ACT_NULL if emulating FPU */
98volatile thread_act_t fp_intr_act = THR_ACT_NULL;
99
100
101#define clear_fpu() \
102 { \
103 set_ts(); \
104 fp_act = THR_ACT_NULL; \
105 }
106
107#else /* NCPUS > 1 */
108#define clear_fpu() \
109 { \
110 set_ts(); \
111 }
112
113#endif
55e303ae
A
114
115#define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
1c79356b
A
116
117/* Forward */
118
119extern void fpinit(void);
120extern void fp_save(
121 thread_act_t thr_act);
122extern void fp_load(
123 thread_act_t thr_act);
124
125/*
126 * Look for FPU and initialize it.
127 * Called on each CPU.
128 */
129void
130init_fpu(void)
131{
132 unsigned short status, control;
133
134 /*
135 * Check for FPU by initializing it,
136 * then trying to read the correct bit patterns from
137 * the control and status registers.
138 */
139 set_cr0(get_cr0() & ~(CR0_EM|CR0_TS)); /* allow use of FPU */
140
141 fninit();
142 status = fnstsw();
143 fnstcw(&control);
144
145 if ((status & 0xff) == 0 &&
55e303ae
A
146 (control & 0x103f) == 0x3f)
147 {
148 fp_kind = FP_387; /* assume we have a 387 compatible instruction set */
149 /* Use FPU save/restore instructions if available */
150 if (cpuid_features() & CPUID_FEATURE_FXSR) {
151 fp_kind = FP_FXSR;
152 set_cr4(get_cr4() | CR4_FXS);
153 printf("Enabling XMM register save/restore");
154 /* And allow SIMD instructions if present */
155 if (cpuid_features() & CPUID_FEATURE_SSE) {
156 printf(" and SSE/SSE2");
157 set_cr4(get_cr4() | CR4_XMM);
158 }
159 printf(" opcodes\n");
1c79356b 160 }
55e303ae 161
1c79356b
A
162 /*
163 * Trap wait instructions. Turn off FPU for now.
164 */
165 set_cr0(get_cr0() | CR0_TS | CR0_MP);
166 }
167 else
168 {
169 /*
170 * NO FPU.
171 */
172 fp_kind = FP_NO;
173 set_cr0(get_cr0() | CR0_EM);
174 }
175}
176
177/*
178 * Initialize FP handling.
179 */
180void
181fpu_module_init(void)
182{
183 ifps_zone = zinit(sizeof(struct i386_fpsave_state),
184 THREAD_MAX * sizeof(struct i386_fpsave_state),
185 THREAD_CHUNK * sizeof(struct i386_fpsave_state),
186 "i386 fpsave state");
187}
188
189/*
190 * Free a FPU save area.
191 * Called only when thread terminating - no locking necessary.
192 */
193void
194fp_free(fps)
195 struct i386_fpsave_state *fps;
196{
197ASSERT_IPL(SPL0);
198#if NCPUS == 1
199 if ((fp_act != THR_ACT_NULL) && (fp_act->mact.pcb->ims.ifps == fps)) {
200 /*
201 * Make sure we don't get FPU interrupts later for
202 * this thread
203 */
204 fwait();
205
206 /* Mark it free and disable access */
207 clear_fpu();
208 }
209#endif /* NCPUS == 1 */
210 zfree(ifps_zone, (vm_offset_t) fps);
211}
212
55e303ae
A
213/*
214 * Set the floating-point state for a thread based
215 * on the FXSave formatted data. This is basically
216 * the same as fpu_set_state except it uses the
217 * expanded data structure.
218 * If the thread is not the current thread, it is
219 * not running (held). Locking needed against
220 * concurrent fpu_set_state or fpu_get_state.
221 */
222kern_return_t
223fpu_set_fxstate(
224 thread_act_t thr_act,
225 struct i386_float_state *state)
226{
227 register pcb_t pcb;
228 register struct i386_fpsave_state *ifps;
229 register struct i386_fpsave_state *new_ifps;
230
231ASSERT_IPL(SPL0);
232 if (fp_kind == FP_NO)
233 return KERN_FAILURE;
234
235 if (state->fpkind != FP_FXSR) {
236 /* strange if this happens, but in case someone builds one of these manually... */
237 return fpu_set_state(thr_act, state);
238 }
239
240 assert(thr_act != THR_ACT_NULL);
241 pcb = thr_act->mact.pcb;
242
243#if NCPUS == 1
244
245 /*
246 * If this thread`s state is in the FPU,
247 * discard it; we are replacing the entire
248 * FPU state.
249 */
250 if (fp_act == thr_act) {
251 fwait(); /* wait for possible interrupt */
252 clear_fpu(); /* no state in FPU */
253 }
254#endif
255
256 if (state->initialized == 0) {
257 /*
258 * new FPU state is 'invalid'.
259 * Deallocate the fp state if it exists.
260 */
261 simple_lock(&pcb->lock);
262 ifps = pcb->ims.ifps;
263 pcb->ims.ifps = 0;
264 simple_unlock(&pcb->lock);
265
266 if (ifps != 0) {
267 zfree(ifps_zone, (vm_offset_t) ifps);
268 }
269 }
270 else {
271 /*
272 * Valid state. Allocate the fp state if there is none.
273 */
274
275 new_ifps = 0;
276 Retry:
277 simple_lock(&pcb->lock);
278 ifps = pcb->ims.ifps;
279 if (ifps == 0) {
280 if (new_ifps == 0) {
281 simple_unlock(&pcb->lock);
282 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
283 assert(ALIGNED(new_ifps,16));
284 goto Retry;
285 }
286 ifps = new_ifps;
287 new_ifps = 0;
288 bzero((char *)ifps, sizeof *ifps);
289 pcb->ims.ifps = ifps;
290 }
291
292 /*
293 * now copy over the new data.
294 */
295 bcopy((char *)&state->hw_state[0], (char *)&ifps->fx_save_state, sizeof(struct i386_fx_save));
296 ifps->fp_save_flavor = FP_FXSR;
297 simple_unlock(&pcb->lock);
298 if (new_ifps != 0)
299 zfree(ifps_zone, (vm_offset_t) ifps);
300 }
301
302 return KERN_SUCCESS;
303}
304
305/*
306 * Get the floating-point state for a thread.
307 * If the thread is not the current thread, it is
308 * not running (held). Locking needed against
309 * concurrent fpu_set_state or fpu_get_state.
310 */
311kern_return_t
312fpu_get_fxstate(
313 thread_act_t thr_act,
314 register struct i386_float_state *state)
315{
316 register pcb_t pcb;
317 register struct i386_fpsave_state *ifps;
318
319ASSERT_IPL(SPL0);
320 if (fp_kind == FP_NO)
321 return KERN_FAILURE;
322
323 assert(thr_act != THR_ACT_NULL);
324 pcb = thr_act->mact.pcb;
325
326 simple_lock(&pcb->lock);
327 ifps = pcb->ims.ifps;
328 if (ifps == 0) {
329 /*
330 * No valid floating-point state.
331 */
332 simple_unlock(&pcb->lock);
333 bzero((char *)state, sizeof(struct i386_float_state));
334 return KERN_SUCCESS;
335 }
336
337 /* Make sure we`ve got the latest fp state info */
338 /* If the live fpu state belongs to our target */
339#if NCPUS == 1
340 if (thr_act == fp_act)
341#else
342 if (thr_act == current_act())
343#endif
344 {
345 clear_ts();
346 fp_save(thr_act);
347 clear_fpu();
348 }
349
350 state->fpkind = fp_kind;
351 state->exc_status = 0;
352 state->initialized = ifps->fp_valid;
353 bcopy( (char *)&ifps->fx_save_state, (char *)&state->hw_state[0], sizeof(struct i386_fx_save));
354
355 simple_unlock(&pcb->lock);
356
357 return KERN_SUCCESS;
358}
359
1c79356b
A
360/*
361 * Set the floating-point state for a thread.
362 * If the thread is not the current thread, it is
363 * not running (held). Locking needed against
364 * concurrent fpu_set_state or fpu_get_state.
365 */
366kern_return_t
367fpu_set_state(
368 thread_act_t thr_act,
369 struct i386_float_state *state)
370{
371 register pcb_t pcb;
372 register struct i386_fpsave_state *ifps;
373 register struct i386_fpsave_state *new_ifps;
374
375ASSERT_IPL(SPL0);
376 if (fp_kind == FP_NO)
377 return KERN_FAILURE;
378
379 assert(thr_act != THR_ACT_NULL);
380 pcb = thr_act->mact.pcb;
381
382#if NCPUS == 1
383
384 /*
385 * If this thread`s state is in the FPU,
386 * discard it; we are replacing the entire
387 * FPU state.
388 */
389 if (fp_act == thr_act) {
390 fwait(); /* wait for possible interrupt */
391 clear_fpu(); /* no state in FPU */
392 }
393#endif
394
395 if (state->initialized == 0) {
396 /*
397 * new FPU state is 'invalid'.
398 * Deallocate the fp state if it exists.
399 */
400 simple_lock(&pcb->lock);
401 ifps = pcb->ims.ifps;
402 pcb->ims.ifps = 0;
403 simple_unlock(&pcb->lock);
404
405 if (ifps != 0) {
406 zfree(ifps_zone, (vm_offset_t) ifps);
407 }
408 }
409 else {
410 /*
411 * Valid state. Allocate the fp state if there is none.
412 */
413 register struct i386_fp_save *user_fp_state;
414 register struct i386_fp_regs *user_fp_regs;
415
416 user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
417 user_fp_regs = (struct i386_fp_regs *)
418 &state->hw_state[sizeof(struct i386_fp_save)];
419
420 new_ifps = 0;
421 Retry:
422 simple_lock(&pcb->lock);
423 ifps = pcb->ims.ifps;
424 if (ifps == 0) {
425 if (new_ifps == 0) {
426 simple_unlock(&pcb->lock);
427 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
55e303ae 428 assert(ALIGNED(new_ifps,16));
1c79356b
A
429 goto Retry;
430 }
431 ifps = new_ifps;
432 new_ifps = 0;
55e303ae 433 bzero((char *)ifps, sizeof *ifps); // zero ALL fields first
1c79356b
A
434 pcb->ims.ifps = ifps;
435 }
436
437 /*
438 * Ensure that reserved parts of the environment are 0.
439 */
440 bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
441
442 ifps->fp_save_state.fp_control = user_fp_state->fp_control;
443 ifps->fp_save_state.fp_status = user_fp_state->fp_status;
444 ifps->fp_save_state.fp_tag = user_fp_state->fp_tag;
445 ifps->fp_save_state.fp_eip = user_fp_state->fp_eip;
446 ifps->fp_save_state.fp_cs = user_fp_state->fp_cs;
447 ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode;
448 ifps->fp_save_state.fp_dp = user_fp_state->fp_dp;
449 ifps->fp_save_state.fp_ds = user_fp_state->fp_ds;
450 ifps->fp_regs = *user_fp_regs;
55e303ae 451 ifps->fp_save_flavor = FP_387;
1c79356b
A
452 simple_unlock(&pcb->lock);
453 if (new_ifps != 0)
454 zfree(ifps_zone, (vm_offset_t) ifps);
455 }
456
457 return KERN_SUCCESS;
458}
459
460/*
461 * Get the floating-point state for a thread.
462 * If the thread is not the current thread, it is
463 * not running (held). Locking needed against
464 * concurrent fpu_set_state or fpu_get_state.
465 */
466kern_return_t
467fpu_get_state(
468 thread_act_t thr_act,
469 register struct i386_float_state *state)
470{
471 register pcb_t pcb;
472 register struct i386_fpsave_state *ifps;
473
474ASSERT_IPL(SPL0);
475 if (fp_kind == FP_NO)
476 return KERN_FAILURE;
477
478 assert(thr_act != THR_ACT_NULL);
479 pcb = thr_act->mact.pcb;
480
481 simple_lock(&pcb->lock);
482 ifps = pcb->ims.ifps;
483 if (ifps == 0) {
484 /*
485 * No valid floating-point state.
486 */
487 simple_unlock(&pcb->lock);
488 bzero((char *)state, sizeof(struct i386_float_state));
489 return KERN_SUCCESS;
490 }
491
492 /* Make sure we`ve got the latest fp state info */
493 /* If the live fpu state belongs to our target */
494#if NCPUS == 1
495 if (thr_act == fp_act)
496#else
497 if (thr_act == current_act())
498#endif
499 {
500 clear_ts();
501 fp_save(thr_act);
502 clear_fpu();
503 }
504
505 state->fpkind = fp_kind;
506 state->exc_status = 0;
507
508 {
509 register struct i386_fp_save *user_fp_state;
510 register struct i386_fp_regs *user_fp_regs;
511
512 state->initialized = ifps->fp_valid;
513
514 user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
515 user_fp_regs = (struct i386_fp_regs *)
516 &state->hw_state[sizeof(struct i386_fp_save)];
517
518 /*
519 * Ensure that reserved parts of the environment are 0.
520 */
521 bzero((char *)user_fp_state, sizeof(struct i386_fp_save));
522
523 user_fp_state->fp_control = ifps->fp_save_state.fp_control;
524 user_fp_state->fp_status = ifps->fp_save_state.fp_status;
525 user_fp_state->fp_tag = ifps->fp_save_state.fp_tag;
526 user_fp_state->fp_eip = ifps->fp_save_state.fp_eip;
527 user_fp_state->fp_cs = ifps->fp_save_state.fp_cs;
528 user_fp_state->fp_opcode = ifps->fp_save_state.fp_opcode;
529 user_fp_state->fp_dp = ifps->fp_save_state.fp_dp;
530 user_fp_state->fp_ds = ifps->fp_save_state.fp_ds;
531 *user_fp_regs = ifps->fp_regs;
532 }
533 simple_unlock(&pcb->lock);
534
535 return KERN_SUCCESS;
536}
537
538/*
539 * Initialize FPU.
540 *
541 * Raise exceptions for:
542 * invalid operation
543 * divide by zero
544 * overflow
545 *
546 * Use 53-bit precision.
547 */
548void
549fpinit(void)
550{
551 unsigned short control;
552
553ASSERT_IPL(SPL0);
554 clear_ts();
555 fninit();
556 fnstcw(&control);
557 control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
558 control |= (FPC_PC_53 | /* Set precision */
559 FPC_RC_RN | /* round-to-nearest */
560 FPC_ZE | /* Suppress zero-divide */
561 FPC_OE | /* and overflow */
562 FPC_UE | /* underflow */
563 FPC_IE | /* Allow NaNQs and +-INF */
564 FPC_DE | /* Allow denorms as operands */
565 FPC_PE); /* No trap for precision loss */
566 fldcw(control);
567}
568
569/*
570 * Coprocessor not present.
571 */
572
573void
574fpnoextflt(void)
575{
576 /*
577 * Enable FPU use.
578 */
579ASSERT_IPL(SPL0);
580 clear_ts();
581#if NCPUS == 1
582
583 /*
584 * If this thread`s state is in the FPU, we are done.
585 */
586 if (fp_act == current_act())
587 return;
588
589 /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
590 * here if the current fpu instruction generates an error.
591 */
592 fwait();
593 /*
594 * If another thread`s state is in the FPU, save it.
595 */
596 if (fp_act != THR_ACT_NULL) {
597 fp_save(fp_act);
598 }
599
600 /*
601 * Give this thread the FPU.
602 */
603 fp_act = current_act();
604
605#endif /* NCPUS == 1 */
606
607 /*
608 * Load this thread`s state into the FPU.
609 */
610 fp_load(current_act());
611}
612
613/*
614 * FPU overran end of segment.
615 * Re-initialize FPU. Floating point state is not valid.
616 */
617
618void
619fpextovrflt(void)
620{
621 register thread_act_t thr_act = current_act();
622 register pcb_t pcb;
623 register struct i386_fpsave_state *ifps;
624
625#if NCPUS == 1
626
627 /*
628 * Is exception for the currently running thread?
629 */
630 if (fp_act != thr_act) {
631 /* Uh oh... */
632 panic("fpextovrflt");
633 }
634#endif
635
636 /*
637 * This is a non-recoverable error.
638 * Invalidate the thread`s FPU state.
639 */
640 pcb = thr_act->mact.pcb;
641 simple_lock(&pcb->lock);
642 ifps = pcb->ims.ifps;
643 pcb->ims.ifps = 0;
644 simple_unlock(&pcb->lock);
645
646 /*
647 * Re-initialize the FPU.
648 */
649 clear_ts();
650 fninit();
651
652 /*
653 * And disable access.
654 */
655 clear_fpu();
656
657 if (ifps)
658 zfree(ifps_zone, (vm_offset_t) ifps);
659
660 /*
661 * Raise exception.
662 */
663 i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
664 /*NOTREACHED*/
665}
666
667/*
668 * FPU error. Called by AST.
669 */
670
671void
672fpexterrflt(void)
673{
674 register thread_act_t thr_act = current_act();
675
676ASSERT_IPL(SPL0);
677#if NCPUS == 1
678 /*
679 * Since FPU errors only occur on ESC or WAIT instructions,
680 * the current thread should own the FPU. If it didn`t,
681 * we should have gotten the task-switched interrupt first.
682 */
683 if (fp_act != THR_ACT_NULL) {
684 panic("fpexterrflt");
685 return;
686 }
687
688 /*
689 * Check if we got a context switch between the interrupt and the AST
690 * This can happen if the interrupt arrived after the FPU AST was
691 * checked. In this case, raise the exception in fp_load when this
692 * thread next time uses the FPU. Remember exception condition in
693 * fp_valid (extended boolean 2).
694 */
695 if (fp_intr_act != thr_act) {
696 if (fp_intr_act == THR_ACT_NULL) {
697 panic("fpexterrflt: fp_intr_act == THR_ACT_NULL");
698 return;
699 }
700 fp_intr_act->mact.pcb->ims.ifps->fp_valid = 2;
701 fp_intr_act = THR_ACT_NULL;
702 return;
703 }
704 fp_intr_act = THR_ACT_NULL;
705#else /* NCPUS == 1 */
706 /*
707 * Save the FPU state and turn off the FPU.
708 */
709 fp_save(thr_act);
710#endif /* NCPUS == 1 */
711
712 /*
713 * Raise FPU exception.
714 * Locking not needed on pcb->ims.ifps,
715 * since thread is running.
716 */
717 i386_exception(EXC_ARITHMETIC,
718 EXC_I386_EXTERR,
719 thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
720 /*NOTREACHED*/
721}
722
723/*
724 * Save FPU state.
725 *
726 * Locking not needed:
727 * . if called from fpu_get_state, pcb already locked.
728 * . if called from fpnoextflt or fp_intr, we are single-cpu
729 * . otherwise, thread is running.
730 */
1c79356b
A
731void
732fp_save(
733 thread_act_t thr_act)
734{
735 register pcb_t pcb = thr_act->mact.pcb;
736 register struct i386_fpsave_state *ifps = pcb->ims.ifps;
1c79356b
A
737 if (ifps != 0 && !ifps->fp_valid) {
738 /* registers are in FPU */
739 ifps->fp_valid = TRUE;
55e303ae
A
740 ifps->fp_save_flavor = FP_387;
741 if (FXSAFE()) {
742 fxsave(&ifps->fx_save_state); // save the SSE2/Fp state in addition is enabled
743 ifps->fp_save_flavor = FP_FXSR;
744 }
745 fnsave(&ifps->fp_save_state); // also update the old save area for now...
1c79356b
A
746 }
747}
748
749/*
750 * Restore FPU state from PCB.
751 *
752 * Locking not needed; always called on the current thread.
753 */
754
755void
756fp_load(
757 thread_act_t thr_act)
758{
759 register pcb_t pcb = thr_act->mact.pcb;
760 register struct i386_fpsave_state *ifps;
761
762ASSERT_IPL(SPL0);
763 ifps = pcb->ims.ifps;
764 if (ifps == 0) {
765 ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
55e303ae 766 assert(ALIGNED(ifps,16));
1c79356b
A
767 bzero((char *)ifps, sizeof *ifps);
768 pcb->ims.ifps = ifps;
769 fpinit();
770#if 1
771/*
772 * I'm not sure this is needed. Does the fpu regenerate the interrupt in
773 * frstor or not? Without this code we may miss some exceptions, with it
774 * we might send too many exceptions.
775 */
776 } else if (ifps->fp_valid == 2) {
777 /* delayed exception pending */
778
779 ifps->fp_valid = TRUE;
780 clear_fpu();
781 /*
782 * Raise FPU exception.
783 * Locking not needed on pcb->ims.ifps,
784 * since thread is running.
785 */
786 i386_exception(EXC_ARITHMETIC,
787 EXC_I386_EXTERR,
788 thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
789 /*NOTREACHED*/
790#endif
791 } else {
55e303ae
A
792 if (ifps->fp_save_flavor == FP_FXSR) fxrstor(&ifps->fx_save_state);
793 else frstor(ifps->fp_save_state);
1c79356b
A
794 }
795 ifps->fp_valid = FALSE; /* in FPU */
796}
797
55e303ae 798
1c79356b
A
799/*
800 * Allocate and initialize FP state for current thread.
801 * Don't load state.
802 *
803 * Locking not needed; always called on the current thread.
804 */
805void
806fp_state_alloc(void)
807{
808 pcb_t pcb = current_act()->mact.pcb;
809 struct i386_fpsave_state *ifps;
810
811 ifps = (struct i386_fpsave_state *)zalloc(ifps_zone);
55e303ae 812 assert(ALIGNED(ifps,16));
1c79356b
A
813 bzero((char *)ifps, sizeof *ifps);
814 pcb->ims.ifps = ifps;
815
816 ifps->fp_valid = TRUE;
817 ifps->fp_save_state.fp_control = (0x037f
818 & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC))
819 | (FPC_PC_53|FPC_IC_AFF);
820 ifps->fp_save_state.fp_status = 0;
821 ifps->fp_save_state.fp_tag = 0xffff; /* all empty */
55e303ae
A
822 ifps->fx_save_state.fx_control = ifps->fp_save_state.fp_control;
823 ifps->fx_save_state.fx_status = ifps->fp_save_state.fp_status;
824 ifps->fx_save_state.fx_tag = 0x00;
825 ifps->fx_save_state.fx_MXCSR = 0x1f80;
826
1c79356b
A
827}
828
829
830/*
831 * fpflush(thread_act_t)
832 * Flush the current act's state, if needed
833 * (used by thread_terminate_self to ensure fp faults
834 * aren't satisfied by overly general trap code in the
835 * context of the reaper thread)
836 */
837void
838fpflush(thread_act_t thr_act)
839{
840#if NCPUS == 1
841 if (fp_act && thr_act == fp_act) {
842 clear_ts();
843 fwait();
844 clear_fpu();
845 }
846#else
847 /* not needed on MP x86s; fp not lazily evaluated */
848#endif
849}
850
851
852/*
853 * Handle a coprocessor error interrupt on the AT386.
854 * This comes in on line 5 of the slave PIC at SPL1.
855 */
856
857void
858fpintr(void)
859{
860 spl_t s;
861 thread_act_t thr_act = current_act();
862
863ASSERT_IPL(SPL1);
864 /*
865 * Turn off the extended 'busy' line.
866 */
867 outb(0xf0, 0);
868
869 /*
870 * Save the FPU context to the thread using it.
871 */
872#if NCPUS == 1
873 if (fp_act == THR_ACT_NULL) {
874 printf("fpintr: FPU not belonging to anyone!\n");
875 clear_ts();
876 fninit();
877 clear_fpu();
878 return;
879 }
880
881 if (fp_act != thr_act) {
882 /*
883 * FPU exception is for a different thread.
884 * When that thread again uses the FPU an exception will be
885 * raised in fp_load. Remember the condition in fp_valid (== 2).
886 */
887 clear_ts();
888 fp_save(fp_act);
889 fp_act->mact.pcb->ims.ifps->fp_valid = 2;
890 fninit();
891 clear_fpu();
892 /* leave fp_intr_act THR_ACT_NULL */
893 return;
894 }
895 if (fp_intr_act != THR_ACT_NULL)
896 panic("fp_intr: already caught intr");
897 fp_intr_act = thr_act;
898#endif /* NCPUS == 1 */
899
900 clear_ts();
901 fp_save(thr_act);
902 fninit();
903 clear_fpu();
904
905 /*
906 * Since we are running on the interrupt stack, we must
907 * signal the thread to take the exception when we return
908 * to user mode. Use an AST to do this.
909 *
910 * Don`t set the thread`s AST field. If the thread is
911 * descheduled before it takes the AST, it will notice
912 * the FPU error when it reloads its FPU state.
913 */
914 s = splsched();
915 mp_disable_preemption();
916 ast_on(AST_I386_FP);
917 mp_enable_preemption();
918 splx(s);
919}