FreeBSD/Linux Kernel Cross Reference
sys/i386/fpu.c
1 /*
2 * Mach Operating System
3 * Copyright (c) 1992-1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26 /*
27 * HISTORY
28 * $Log: fpu.c,v $
29 * Revision 2.16 93/05/15 19:30:15 mrt
30 * machparam.h -> machspl.h
31 *
32 * Revision 2.15 93/05/10 21:18:27 rvb
33 * spl_t fixes.
34 * [93/05/06 11:15:58 af]
35 *
36 * Revision 2.14 93/02/04 07:55:57 danner
37 * Add PS2 to #if AT386 conditional.
38 * [93/01/25 rvb]
39 *
40 * Revision 2.12 92/08/06 10:02:35 jfriedl
41 * Added volatile to declarations of fp_inifinity, fp_one, fp_zero
42 * to prevent the code from being optimized away.
43 * Fix from Michael Bushnell - FSF
44 * [92/08/06 jfriedl]
45 *
46 * Revision 2.11 92/02/26 13:12:30 elf
47 * Fpinit fixes from dlb.
48 * [92/02/26 danner]
49 *
50 * Revision 2.10 92/02/19 15:08:00 elf
51 * Make sure the current thread's floating instruction has completed
52 * before freeing its fpu context and before context switching the FPU.
53 *
54 * Remember which thread the FPU AST was meant for and delay sending
55 * exception if a context switch has occurred between the interrupt and
56 * the AST handling. This can happen if an fpu interrupt arrives after
57 * FPU ASTs have been checked for but before thread_block.
58 *
59 * Mark pending exception for thread in fp_valid (= 2) in fpexterrflt or
60 * fp_intr if the thread that caused the exception is not running and
61 * send the exception when the thread next runs. (I'm not really sure
62 * this is necessary).
63 *
64 * Check fp_thread for THREAD_NULL in fp_free and fp_intr before
65 * accessing its PCB.
66 *
67 * Don't save FPU context in fpexterrflt. It is already saved in
68 * fp_intr.
69 * [92/02/01 jvh]
70 *
71 * Revision 2.9 92/01/03 20:05:42 dbg
72 * Move floating-point state manipulation from i386/pcb.c to this
73 * file. Add support for floating-point emulator. Restore
74 * FPU shuffling between threads if single CPU. Add locking
75 * to PCB to avoid losing fp_save structures.
76 * [91/10/20 dbg]
77 *
78 * Revision 2.8 91/06/19 11:55:02 rvb
79 * cputypes.h->platforms.h
80 * [91/06/12 13:44:41 rvb]
81 *
82 * Revision 2.7 91/05/14 16:07:37 mrt
83 * Correcting copyright
84 *
85 * Revision 2.6 91/05/08 12:31:32 dbg
86 * Simplify. Always store FP state on context switch.
87 * 1: It's unknown what happens when the i386 switches
88 * mappings while the i387 has a store instruction
89 * (e.g. fbstp) in progress. Where does the data go?
90 * 2: On a multiprocessor, we'd need interprocessor interrupts
91 * to fetch a thread's FP state from the CPU it last ran on.
92 * [91/04/26 14:34:04 dbg]
93 *
94 * Revision 2.5 91/03/16 14:44:07 rpd
95 * Pulled i386_fpsave_state out of i386_machine_state.
96 * Picked up fixes from dbg.
97 * [91/02/18 rpd]
98 *
99 * Revision 2.4 91/02/05 17:11:45 mrt
100 * Changed to new Mach copyright
101 * [91/02/01 17:33:57 mrt]
102 *
103 * Revision 2.3 91/01/08 15:10:29 rpd
104 * Split i386_machine_state off of i386_kernel_state.
105 * [90/12/31 rpd]
106 * Reorganized the pcb.
107 * [90/12/11 rpd]
108 *
109 * Revision 2.2 90/05/03 15:25:18 dbg
110 * Created.
111 * [90/02/11 dbg]
112 *
113 */
114
115 /*
116 * Support for 80387 floating point or FP emulator.
117 */
118 #include <cpus.h>
119 #include <fpe.h>
120 #include <platforms.h>
121
122 #include <mach/exception.h>
123 #include <mach/i386/thread_status.h>
124 #include <mach/i386/fp_reg.h>
125
126 #include <machine/machspl.h> /* spls */
127 #include <kern/mach_param.h>
128 #include <kern/thread.h>
129 #include <kern/zalloc.h>
130
131 #include <i386/thread.h>
132 #include <i386/fpu.h>
133
134 #if 0
135 #include <i386/ipl.h>
136 extern int curr_ipl;
137 #define ASSERT_IPL(L) \
138 { \
139 if (curr_ipl != L) { \
140 printf("IPL is %d, expected %d\n", curr_ipl, L); \
141 panic("fpu: wrong ipl"); \
142 } \
143 }
144 #else
145 #define ASSERT_IPL(L)
146 #endif
147
148 extern void i386_exception();
149
150 int fp_kind = FP_387; /* 80387 present */
151 zone_t ifps_zone; /* zone for FPU save area */
152
153 #if NCPUS == 1
154 volatile thread_t fp_thread = THREAD_NULL;
155 /* thread whose state is in FPU */
156 /* always THREAD_NULL if emulating
157 FPU */
158 volatile thread_t fp_intr_thread = THREAD_NULL;
159
160
161 #define clear_fpu() \
162 { \
163 set_ts(); \
164 fp_thread = THREAD_NULL; \
165 }
166
167 #else /* NCPUS > 1 */
168 #define clear_fpu() \
169 { \
170 set_ts(); \
171 }
172
173 #endif
174
175
176 /*
177 * Look for FPU and initialize it.
178 * Called on each CPU.
179 */
180 void
181 init_fpu()
182 {
183 unsigned short status, control;
184
185 /*
186 * Check for FPU by initializing it,
187 * then trying to read the correct bit patterns from
188 * the control and status registers.
189 */
190 set_cr0(get_cr0() & ~(CR0_EM|CR0_TS)); /* allow use of FPU */
191
192 fninit();
193 status = fnstsw();
194 fnstcw(&control);
195
196 if ((status & 0xff) == 0 &&
197 (control & 0x103f) == 0x3f)
198 {
199 /*
200 * We have a FPU of some sort.
201 * Compare -infinity against +infinity
202 * to check whether we have a 287 or a 387.
203 */
204 volatile double fp_infinity, fp_one, fp_zero;
205 fp_one = 1.0;
206 fp_zero = 0.0;
207 fp_infinity = fp_one / fp_zero;
208 if (fp_infinity == -fp_infinity) {
209 /*
210 * We have an 80287.
211 */
212 fp_kind = FP_287;
213 asm volatile(".byte 0xdb; .byte 0xe4"); /* fnsetpm */
214 }
215 else {
216 /*
217 * We have a 387.
218 */
219 fp_kind = FP_387;
220 }
221 /*
222 * Trap wait instructions. Turn off FPU for now.
223 */
224 set_cr0(get_cr0() | CR0_TS | CR0_MP);
225 }
226 else {
227 #if FPE
228 /*
229 * Use the floating-point emulator.
230 */
231 fp_kind = FP_SOFT;
232 fpe_init();
233 #else /* no fpe */
234 /*
235 * NO FPU.
236 */
237 fp_kind = FP_NO;
238 set_cr0(get_cr0() | CR0_EM);
239 #endif
240 }
241 }
242
243 /*
244 * Initialize FP handling.
245 */
246 void
247 fpu_module_init()
248 {
249 ifps_zone = zinit(sizeof(struct i386_fpsave_state),
250 THREAD_MAX * sizeof(struct i386_fpsave_state),
251 THREAD_CHUNK * sizeof(struct i386_fpsave_state),
252 FALSE, "i386 fpsave state");
253 }
254
255 /*
256 * Free a FPU save area.
257 * Called only when thread terminating - no locking necessary.
258 */
259 void
260 fp_free(fps)
261 struct i386_fpsave_state *fps;
262 {
263 ASSERT_IPL(SPL0);
264 #if NCPUS == 1
265 if ((fp_thread != THREAD_NULL) && (fp_thread->pcb->ims.ifps == fps)) {
266 /*
267 * Make sure we don't get FPU interrupts later for
268 * this thread
269 */
270 fwait();
271
272 /* Mark it free and disable access */
273 clear_fpu();
274 }
275 #endif /* NCPUS == 1 */
276 zfree(ifps_zone, (vm_offset_t) fps);
277 }
278
279 /*
280 * Set the floating-point state for a thread.
281 * If the thread is not the current thread, it is
282 * not running (held). Locking needed against
283 * concurrent fpu_set_state or fpu_get_state.
284 */
285 kern_return_t
286 fpu_set_state(thread, state)
287 thread_t thread;
288 struct i386_float_state *state;
289 {
290 register pcb_t pcb = thread->pcb;
291 register struct i386_fpsave_state *ifps;
292 register struct i386_fpsave_state *new_ifps;
293
294 ASSERT_IPL(SPL0);
295 if (fp_kind == FP_NO)
296 return KERN_FAILURE;
297
298 #if NCPUS == 1
299
300 /*
301 * If this thread`s state is in the FPU,
302 * discard it; we are replacing the entire
303 * FPU state.
304 */
305 if (fp_thread == thread) {
306 fwait(); /* wait for possible interrupt */
307 clear_fpu(); /* no state in FPU */
308 }
309 #endif
310
311 if (state->initialized == 0) {
312 /*
313 * new FPU state is 'invalid'.
314 * Deallocate the fp state if it exists.
315 */
316 simple_lock(&pcb->lock);
317 ifps = pcb->ims.ifps;
318 pcb->ims.ifps = 0;
319 simple_unlock(&pcb->lock);
320
321 if (ifps != 0) {
322 zfree(ifps_zone, (vm_offset_t) ifps);
323 }
324 }
325 else {
326 /*
327 * Valid state. Allocate the fp state if there is none.
328 */
329 register struct i386_fp_save *user_fp_state;
330 register struct i386_fp_regs *user_fp_regs;
331
332 user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
333 user_fp_regs = (struct i386_fp_regs *)
334 &state->hw_state[sizeof(struct i386_fp_save)];
335
336 new_ifps = 0;
337 Retry:
338 simple_lock(&pcb->lock);
339 ifps = pcb->ims.ifps;
340 if (ifps == 0) {
341 if (new_ifps == 0) {
342 simple_unlock(&pcb->lock);
343 new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
344 goto Retry;
345 }
346 ifps = new_ifps;
347 new_ifps = 0;
348 pcb->ims.ifps = ifps;
349 }
350
351 /*
352 * Ensure that reserved parts of the environment are 0.
353 */
354 bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
355
356 ifps->fp_save_state.fp_control = user_fp_state->fp_control;
357 ifps->fp_save_state.fp_status = user_fp_state->fp_status;
358 ifps->fp_save_state.fp_tag = user_fp_state->fp_tag;
359 ifps->fp_save_state.fp_eip = user_fp_state->fp_eip;
360 ifps->fp_save_state.fp_cs = user_fp_state->fp_cs;
361 ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode;
362 ifps->fp_save_state.fp_dp = user_fp_state->fp_dp;
363 ifps->fp_save_state.fp_ds = user_fp_state->fp_ds;
364
365 #if FPE
366 if (fp_kind == FP_SOFT) {
367 /*
368 * The emulator stores the registers by physical
369 * register number, not from top-of-stack.
370 * Shuffle the registers into the correct order.
371 */
372 register char *src; /* user regs */
373 register char *dst; /* kernel regs */
374 int i;
375
376 src = (char *)user_fp_regs;
377 dst = (char *)&ifps->fp_regs;
378 i = (ifps->fp_save_state.fp_status & FPS_TOS)
379 >> FPS_TOS_SHIFT; /* physical register
380 for st(0) */
381 if (i == 0)
382 bcopy(src, dst, 8 * 10);
383 else {
384 bcopy(src,
385 dst + 10 * i,
386 10 * (8 - i));
387 bcopy(src + 10 * (8 - i),
388 dst,
389 10 * i);
390 }
391 }
392 else
393 ifps->fp_regs = *user_fp_regs;
394 #else /* no FPE */
395 ifps->fp_regs = *user_fp_regs;
396 #endif /* FPE */
397
398 simple_unlock(&pcb->lock);
399 if (new_ifps != 0)
400 zfree(ifps_zone, (vm_offset_t) ifps);
401 }
402
403 return KERN_SUCCESS;
404 }
405
406 /*
407 * Get the floating-point state for a thread.
408 * If the thread is not the current thread, it is
409 * not running (held). Locking needed against
410 * concurrent fpu_set_state or fpu_get_state.
411 */
412 kern_return_t
413 fpu_get_state(thread, state)
414 thread_t thread;
415 register struct i386_float_state *state;
416 {
417 register pcb_t pcb = thread->pcb;
418 register struct i386_fpsave_state *ifps;
419
420 ASSERT_IPL(SPL0);
421 if (fp_kind == FP_NO)
422 return KERN_FAILURE;
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 clear_ts();
437 fp_save(thread);
438 clear_fpu();
439
440 state->fpkind = fp_kind;
441 state->exc_status = 0;
442
443 {
444 register struct i386_fp_save *user_fp_state;
445 register struct i386_fp_regs *user_fp_regs;
446
447 state->initialized = ifps->fp_valid;
448
449 user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
450 user_fp_regs = (struct i386_fp_regs *)
451 &state->hw_state[sizeof(struct i386_fp_save)];
452
453 /*
454 * Ensure that reserved parts of the environment are 0.
455 */
456 bzero((char *)user_fp_state, sizeof(struct i386_fp_save));
457
458 user_fp_state->fp_control = ifps->fp_save_state.fp_control;
459 user_fp_state->fp_status = ifps->fp_save_state.fp_status;
460 user_fp_state->fp_tag = ifps->fp_save_state.fp_tag;
461 user_fp_state->fp_eip = ifps->fp_save_state.fp_eip;
462 user_fp_state->fp_cs = ifps->fp_save_state.fp_cs;
463 user_fp_state->fp_opcode = ifps->fp_save_state.fp_opcode;
464 user_fp_state->fp_dp = ifps->fp_save_state.fp_dp;
465 user_fp_state->fp_ds = ifps->fp_save_state.fp_ds;
466
467 #if FPE
468 if (fp_kind == FP_SOFT) {
469 /*
470 * The emulator stores the registers by physical
471 * register number, not from top-of-stack.
472 * Shuffle the registers into the correct order.
473 */
474 register char *src; /* kernel regs */
475 register char *dst; /* user regs */
476 int i;
477
478 src = (char *)&ifps->fp_regs;
479 dst = (char *)user_fp_regs;
480 i = (ifps->fp_save_state.fp_status & FPS_TOS)
481 >> FPS_TOS_SHIFT; /* physical register
482 for st(0) */
483 if (i == 0)
484 bcopy(src, dst, 8 * 10);
485 else {
486 bcopy(src + 10 * i,
487 dst,
488 10 * (8 - i));
489 bcopy(src,
490 dst + 10 * (8 - i),
491 10 * i);
492 }
493 }
494 else
495 *user_fp_regs = ifps->fp_regs;
496 #else /* no FPE */
497 *user_fp_regs = ifps->fp_regs;
498 #endif /* FPE */
499 }
500 simple_unlock(&pcb->lock);
501
502 return KERN_SUCCESS;
503 }
504
505 /*
506 * Initialize FPU.
507 *
508 * Raise exceptions for:
509 * invalid operation
510 * divide by zero
511 * overflow
512 *
513 * Use 53-bit precision.
514 */
515 void fpinit()
516 {
517 unsigned short control;
518
519 ASSERT_IPL(SPL0);
520 clear_ts();
521 fninit();
522 fnstcw(&control);
523 control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
524 control |= (FPC_PC_53 | /* Set precision */
525 FPC_RC_RN | /* round-to-nearest */
526 FPC_ZE | /* Suppress zero-divide */
527 FPC_OE | /* and overflow */
528 FPC_UE | /* underflow */
529 FPC_IE | /* Allow NaNQs and +-INF */
530 FPC_DE | /* Allow denorms as operands */
531 FPC_PE); /* No trap for precision loss */
532 fldcw(control);
533 }
534
535 /*
536 * Coprocessor not present.
537 */
538 fpnoextflt()
539 {
540 /*
541 * Enable FPU use.
542 */
543 ASSERT_IPL(SPL0);
544 clear_ts();
545 #if NCPUS == 1
546
547 /*
548 * If this thread`s state is in the FPU, we are done.
549 */
550 if (fp_thread == current_thread())
551 return;
552
553 /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
554 * here if the current fpu instruction generates an error.
555 */
556 fwait();
557 /*
558 * If another thread`s state is in the FPU, save it.
559 */
560 if (fp_thread != THREAD_NULL) {
561 fp_save(fp_thread);
562 }
563
564 /*
565 * Give this thread the FPU.
566 */
567 fp_thread = current_thread();
568
569 #endif /* NCPUS == 1 */
570
571 /*
572 * Load this thread`s state into the FPU.
573 */
574 fp_load(current_thread());
575 }
576
577 /*
578 * FPU overran end of segment.
579 * Re-initialize FPU. Floating point state is not valid.
580 */
581 fpextovrflt()
582 {
583 register thread_t thread = current_thread();
584 register pcb_t pcb;
585 register struct i386_fpsave_state *ifps;
586
587 #if NCPUS == 1
588
589 /*
590 * Is exception for the currently running thread?
591 */
592 if (fp_thread != thread) {
593 /* Uh oh... */
594 panic("fpextovrflt");
595 }
596 #endif
597
598 /*
599 * This is a non-recoverable error.
600 * Invalidate the thread`s FPU state.
601 */
602 pcb = thread->pcb;
603 simple_lock(&pcb->lock);
604 ifps = pcb->ims.ifps;
605 pcb->ims.ifps = 0;
606 simple_unlock(&pcb->lock);
607
608 /*
609 * Re-initialize the FPU.
610 */
611 clear_ts();
612 fninit();
613
614 /*
615 * And disable access.
616 */
617 clear_fpu();
618
619 if (ifps)
620 zfree(ifps_zone, (vm_offset_t) ifps);
621
622 /*
623 * Raise exception.
624 */
625 i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
626 /*NOTREACHED*/
627 }
628
629 /*
630 * FPU error. Called by AST.
631 */
632 fpexterrflt()
633 {
634 register thread_t thread = current_thread();
635
636 ASSERT_IPL(SPL0);
637 #if NCPUS == 1
638 /*
639 * Since FPU errors only occur on ESC or WAIT instructions,
640 * the current thread should own the FPU. If it didn`t,
641 * we should have gotten the task-switched interrupt first.
642 */
643 if (fp_thread != THREAD_NULL) {
644 panic("fpexterrflt");
645 return;
646 }
647
648 /*
649 * Check if we got a context switch between the interrupt and the AST
650 * This can happen if the interrupt arrived after the FPU AST was
651 * checked. In this case, raise the exception in fp_load when this
652 * thread next time uses the FPU. Remember exception condition in
653 * fp_valid (extended boolean 2).
654 */
655 if (fp_intr_thread != thread) {
656 if (fp_intr_thread == THREAD_NULL) {
657 panic("fpexterrflt: fp_intr_thread == THREAD_NULL");
658 return;
659 }
660 fp_intr_thread->pcb->ims.ifps->fp_valid = 2;
661 fp_intr_thread = THREAD_NULL;
662 return;
663 }
664 fp_intr_thread = THREAD_NULL;
665 #else /* NCPUS == 1 */
666 /*
667 * Save the FPU state and turn off the FPU.
668 */
669 fp_save(thread);
670 #endif /* NCPUS == 1 */
671
672 /*
673 * Raise FPU exception.
674 * Locking not needed on pcb->ims.ifps,
675 * since thread is running.
676 */
677 i386_exception(EXC_ARITHMETIC,
678 EXC_I386_EXTERR,
679 thread->pcb->ims.ifps->fp_save_state.fp_status);
680 /*NOTREACHED*/
681 }
682
683 /*
684 * Save FPU state.
685 *
686 * Locking not needed:
687 * . if called from fpu_get_state, pcb already locked.
688 * . if called from fpnoextflt or fp_intr, we are single-cpu
689 * . otherwise, thread is running.
690 */
691 fp_save(thread)
692 register thread_t thread;
693 {
694 register pcb_t pcb = thread->pcb;
695 register struct i386_fpsave_state *ifps = pcb->ims.ifps;
696
697 if (ifps != 0 && !ifps->fp_valid) {
698 /* registers are in FPU */
699 ifps->fp_valid = TRUE;
700 fnsave(&ifps->fp_save_state);
701 }
702 }
703
704 /*
705 * Restore FPU state from PCB.
706 *
707 * Locking not needed; always called on the current thread.
708 */
709 fp_load(thread)
710 register thread_t thread;
711 {
712 register pcb_t pcb = thread->pcb;
713 register struct i386_fpsave_state *ifps;
714
715 ASSERT_IPL(SPL0);
716 ifps = pcb->ims.ifps;
717 if (ifps == 0) {
718 ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
719 bzero(ifps, sizeof *ifps);
720 pcb->ims.ifps = ifps;
721 fpinit();
722 #if 1
723 /*
724 * I'm not sure this is needed. Does the fpu regenerate the interrupt in
725 * frstor or not? Without this code we may miss some exceptions, with it
726 * we might send too many exceptions.
727 */
728 } else if (ifps->fp_valid == 2) {
729 /* delayed exception pending */
730
731 ifps->fp_valid = TRUE;
732 clear_fpu();
733 /*
734 * Raise FPU exception.
735 * Locking not needed on pcb->ims.ifps,
736 * since thread is running.
737 */
738 i386_exception(EXC_ARITHMETIC,
739 EXC_I386_EXTERR,
740 thread->pcb->ims.ifps->fp_save_state.fp_status);
741 /*NOTREACHED*/
742 #endif
743 } else {
744 frstor(ifps->fp_save_state);
745 }
746 ifps->fp_valid = FALSE; /* in FPU */
747 }
748
749 /*
750 * Allocate and initialize FP state for current thread.
751 * Don't load state.
752 *
753 * Locking not needed; always called on the current thread.
754 */
755 void
756 fp_state_alloc()
757 {
758 pcb_t pcb = current_thread()->pcb;
759 struct i386_fpsave_state *ifps;
760
761 ifps = (struct i386_fpsave_state *)zalloc(ifps_zone);
762 bzero(ifps, sizeof *ifps);
763 pcb->ims.ifps = ifps;
764
765 ifps->fp_valid = TRUE;
766 ifps->fp_save_state.fp_control = (0x037f
767 & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC))
768 | (FPC_PC_53|FPC_IC_AFF);
769 ifps->fp_save_state.fp_status = 0;
770 ifps->fp_save_state.fp_tag = 0xffff; /* all empty */
771 }
772
773 #if AT386 || PS2
774 /*
775 * Handle a coprocessor error interrupt on the AT386.
776 * This comes in on line 5 of the slave PIC at SPL1.
777 */
778 fpintr()
779 {
780 spl_t s;
781 thread_t thread = current_thread();
782
783 ASSERT_IPL(SPL1);
784 /*
785 * Turn off the extended 'busy' line.
786 */
787 outb(0xf0, 0);
788
789 /*
790 * Save the FPU context to the thread using it.
791 */
792 #if NCPUS == 1
793 if (fp_thread == THREAD_NULL) {
794 printf("fpintr: FPU not belonging to anyone!\n");
795 clear_ts();
796 fninit();
797 clear_fpu();
798 return;
799 }
800
801 if (fp_thread != thread) {
802 /*
803 * FPU exception is for a different thread.
804 * When that thread again uses the FPU an exception will be
805 * raised in fp_load. Remember the condition in fp_valid (== 2).
806 */
807 clear_ts();
808 fp_save(fp_thread);
809 fp_thread->pcb->ims.ifps->fp_valid = 2;
810 fninit();
811 clear_fpu();
812 /* leave fp_intr_thread THREAD_NULL */
813 return;
814 }
815 if (fp_intr_thread != THREAD_NULL)
816 panic("fp_intr: already caught intr");
817 fp_intr_thread = thread;
818 #endif /* NCPUS == 1 */
819
820 clear_ts();
821 fp_save(thread);
822 fninit();
823 clear_fpu();
824
825 /*
826 * Since we are running on the interrupt stack, we must
827 * signal the thread to take the exception when we return
828 * to user mode. Use an AST to do this.
829 *
830 * Don`t set the thread`s AST field. If the thread is
831 * descheduled before it takes the AST, it will notice
832 * the FPU error when it reloads its FPU state.
833 */
834 s = splsched();
835 ast_on(cpu_number(), AST_I386_FP);
836 splx(s);
837 }
838 #endif /* AT386 || PS2 */
Cache object: dddb858f6e49e06fc8924f3e14a265d5
|