summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--i386/i386/fpu.c105
-rw-r--r--i386/i386/fpu.h1
-rw-r--r--i386/i386/trap.c2
3 files changed, 76 insertions, 32 deletions
diff --git a/i386/i386/fpu.c b/i386/i386/fpu.c
index 6ff35a0..5f045f5 100644
--- a/i386/i386/fpu.c
+++ b/i386/i386/fpu.c
@@ -95,13 +95,17 @@ void
init_fpu()
{
unsigned short status, control;
+ unsigned int native = 0;
+
+ if (machine_slot[cpu_number()].cpu_type >= CPU_TYPE_I486)
+ native = CR0_NE;
/*
* Check for FPU by initializing it,
* then trying to read the correct bit patterns from
* the control and status registers.
*/
- set_cr0(get_cr0() & ~(CR0_EM|CR0_TS|CR0_NE)); /* allow use of FPU */
+ set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | native); /* allow use of FPU */
fninit();
status = fnstsw();
@@ -480,14 +484,80 @@ fpextovrflt()
/*NOTREACHED*/
}
+static int
+fphandleerr()
+{
+ register thread_t thread = current_thread();
+
+ /*
+ * Save the FPU context to the thread using it.
+ */
+#if NCPUS == 1
+ if (fp_thread == THREAD_NULL) {
+ printf("fpintr: FPU not belonging to anyone!\n");
+ clear_ts();
+ fninit();
+ clear_fpu();
+ return 1;
+ }
+
+ if (fp_thread != thread) {
+ /*
+ * FPU exception is for a different thread.
+ * When that thread again uses the FPU an exception will be
+ * raised in fp_load. Remember the condition in fp_valid (== 2).
+ */
+ clear_ts();
+ fp_save(fp_thread);
+ fp_thread->pcb->ims.ifps->fp_valid = 2;
+ fninit();
+ clear_fpu();
+ /* leave fp_intr_thread THREAD_NULL */
+ return 1;
+ }
+#endif /* NCPUS == 1 */
+
+ /*
+ * Save the FPU state and turn off the FPU.
+ */
+ clear_ts();
+ fp_save(thread);
+ fninit();
+ clear_fpu();
+
+ return 0;
+}
+
/*
- * FPU error. Called by AST.
+ * FPU error. Called by exception handler.
*/
void
fpexterrflt()
{
register thread_t thread = current_thread();
+ if (fphandleerr())
+ return;
+
+ /*
+ * Raise FPU exception.
+ * Locking not needed on pcb->ims.ifps,
+ * since thread is running.
+ */
+ i386_exception(EXC_ARITHMETIC,
+ EXC_I386_EXTERR,
+ thread->pcb->ims.ifps->fp_save_state.fp_status);
+ /*NOTREACHED*/
+}
+
+/*
+ * FPU error. Called by AST.
+ */
+void
+fpastintr()
+{
+ register thread_t thread = current_thread();
+
ASSERT_IPL(SPL0);
#if NCPUS == 1
/*
@@ -647,42 +717,15 @@ ASSERT_IPL(SPL1);
*/
outb(0xf0, 0);
- /*
- * Save the FPU context to the thread using it.
- */
-#if NCPUS == 1
- if (fp_thread == THREAD_NULL) {
- printf("fpintr: FPU not belonging to anyone!\n");
- clear_ts();
- fninit();
- clear_fpu();
+ if (fphandleerr())
return;
- }
- if (fp_thread != thread) {
- /*
- * FPU exception is for a different thread.
- * When that thread again uses the FPU an exception will be
- * raised in fp_load. Remember the condition in fp_valid (== 2).
- */
- clear_ts();
- fp_save(fp_thread);
- fp_thread->pcb->ims.ifps->fp_valid = 2;
- fninit();
- clear_fpu();
- /* leave fp_intr_thread THREAD_NULL */
- return;
- }
+#if NCPUS == 1
if (fp_intr_thread != THREAD_NULL && fp_intr_thread != thread)
panic("fp_intr: already caught intr");
fp_intr_thread = thread;
#endif /* NCPUS == 1 */
- clear_ts();
- fp_save(thread);
- fninit();
- clear_fpu();
-
/*
* Since we are running on the interrupt stack, we must
* signal the thread to take the exception when we return
diff --git a/i386/i386/fpu.h b/i386/i386/fpu.h
index 4237bea..7efb7e2 100644
--- a/i386/i386/fpu.h
+++ b/i386/i386/fpu.h
@@ -113,6 +113,7 @@ extern kern_return_t fpu_get_state(
extern void fpnoextflt(void);
extern void fpextovrflt(void);
extern void fpexterrflt(void);
+extern void fpastintr(void);
extern void init_fpu(void);
#endif /* _I386_FPU_H_ */
diff --git a/i386/i386/trap.c b/i386/i386/trap.c
index 111fdc4..4361fcd 100644
--- a/i386/i386/trap.c
+++ b/i386/i386/trap.c
@@ -594,7 +594,7 @@ i386_astintr()
ast_off(mycpu, AST_I386_FP);
(void) spl0();
- fpexterrflt();
+ fpastintr();
}
else {
/*