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