]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/fpu.c
xnu-517.12.7.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 *
e5568f75
A
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.
1c79356b 11 *
e5568f75
A
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
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.
1c79356b
A
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>
55e303ae 71#include <i386/cpuid.h>
1c79356b
A
72#include <i386/misc_protos.h>
73
74#if 0
75#include <i386/ipl.h>
76extern 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
88int fp_kind = FP_387; /* 80387 present */
89zone_t ifps_zone; /* zone for FPU save area */
90
91#if NCPUS == 1
92volatile thread_act_t fp_act = THR_ACT_NULL;
93 /* thread whose state is in FPU */
94 /* always THR_ACT_NULL if emulating FPU */
95volatile 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
55e303ae
A
111
112#define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0)
1c79356b
A
113
114/* Forward */
115
116extern void fpinit(void);
117extern void fp_save(
118 thread_act_t thr_act);
119extern void fp_load(
120 thread_act_t thr_act);
121
122/*
123 * Look for FPU and initialize it.
124 * Called on each CPU.
125 */
126void
127init_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 &&
55e303ae
A
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");
1c79356b 157 }
55e303ae 158
1c79356b
A
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 */
177void
178fpu_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 */
190void
191fp_free(fps)
192 struct i386_fpsave_state *fps;
193{
194ASSERT_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
55e303ae
A
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 */
219kern_return_t
220fpu_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
228ASSERT_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 */
308kern_return_t
309fpu_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
316ASSERT_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
1c79356b
A
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 */
363kern_return_t
364fpu_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
372ASSERT_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);
55e303ae 425 assert(ALIGNED(new_ifps,16));
1c79356b
A
426 goto Retry;
427 }
428 ifps = new_ifps;
429 new_ifps = 0;
55e303ae 430 bzero((char *)ifps, sizeof *ifps); // zero ALL fields first
1c79356b
A
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;
55e303ae 448 ifps->fp_save_flavor = FP_387;
1c79356b
A
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 */
463kern_return_t
464fpu_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
471ASSERT_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 */
545void
546fpinit(void)
547{
548 unsigned short control;
549
550ASSERT_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
570void
571fpnoextflt(void)
572{
573 /*
574 * Enable FPU use.
575 */
576ASSERT_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
615void
616fpextovrflt(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
668void
669fpexterrflt(void)
670{
671 register thread_act_t thr_act = current_act();
672
673ASSERT_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 */
1c79356b
A
728void
729fp_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;
1c79356b
A
734 if (ifps != 0 && !ifps->fp_valid) {
735 /* registers are in FPU */
736 ifps->fp_valid = TRUE;
55e303ae
A
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...
1c79356b
A
743 }
744}
745
746/*
747 * Restore FPU state from PCB.
748 *
749 * Locking not needed; always called on the current thread.
750 */
751
752void
753fp_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
759ASSERT_IPL(SPL0);
760 ifps = pcb->ims.ifps;
761 if (ifps == 0) {
762 ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
55e303ae 763 assert(ALIGNED(ifps,16));
1c79356b
A
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 {
55e303ae
A
789 if (ifps->fp_save_flavor == FP_FXSR) fxrstor(&ifps->fx_save_state);
790 else frstor(ifps->fp_save_state);
1c79356b
A
791 }
792 ifps->fp_valid = FALSE; /* in FPU */
793}
794
55e303ae 795
1c79356b
A
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 */
802void
803fp_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);
55e303ae 809 assert(ALIGNED(ifps,16));
1c79356b
A
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 */
55e303ae
A
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
1c79356b
A
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 */
834void
835fpflush(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
854void
855fpintr(void)
856{
857 spl_t s;
858 thread_act_t thr_act = current_act();
859
860ASSERT_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}