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