summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2016-03-06 13:11:42 +0100
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2016-03-06 13:11:42 +0100
commit539626070dd9b9b1e2d89874ab84cdb02031c2a0 (patch)
tree84c1c5335e82bb79d39e89059828be5932a8cbff
parent689810afe2d20d5f24f2a2379d3b9feffd0e9698 (diff)
Inherit fpu control word from parent to child
* i386/i386/thread.h (struct pcb): Add init_control field. * i386/i386/fpu.h (fpinherit): New prototype. * i386/i386/fpu.c (fpinit): Add thread parameter. When init_control field is set, use that value instead of a hardcoded one. (fpinherit): New function. (fp_load): Pass thread parameter to fpinit(). * kern/thread.c (thread_create): Pass parent task to pcb_init(). * i386/i386/pcb.c (pcb_init): Add parent_task parameter, call fpinherit when it is equal to current_task().
-rw-r--r--i386/i386/fpu.c49
-rw-r--r--i386/i386/fpu.h1
-rw-r--r--i386/i386/pcb.c7
-rw-r--r--i386/i386/pcb.h2
-rw-r--r--i386/i386/thread.h1
-rw-r--r--kern/thread.c2
6 files changed, 47 insertions, 15 deletions
diff --git a/i386/i386/fpu.c b/i386/i386/fpu.c
index ddf4c8ed..6ee20150 100644
--- a/i386/i386/fpu.c
+++ b/i386/i386/fpu.c
@@ -494,27 +494,52 @@ ASSERT_IPL(SPL0);
*
* Use 53-bit precision.
*/
-void fpinit(void)
+static void fpinit(thread_t thread)
{
unsigned short control;
ASSERT_IPL(SPL0);
clear_ts();
fninit();
- fnstcw(&control);
- control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
- control |= (FPC_PC_53 | /* Set precision */
- FPC_RC_RN | /* round-to-nearest */
- FPC_ZE | /* Suppress zero-divide */
- FPC_OE | /* and overflow */
- FPC_UE | /* underflow */
- FPC_IE | /* Allow NaNQs and +-INF */
- FPC_DE | /* Allow denorms as operands */
- FPC_PE); /* No trap for precision loss */
+ if (thread->pcb->init_control) {
+ control = thread->pcb->init_control;
+ }
+ else
+ {
+ fnstcw(&control);
+ control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
+ control |= (FPC_PC_53 | /* Set precision */
+ FPC_RC_RN | /* round-to-nearest */
+ FPC_ZE | /* Suppress zero-divide */
+ FPC_OE | /* and overflow */
+ FPC_UE | /* underflow */
+ FPC_IE | /* Allow NaNQs and +-INF */
+ FPC_DE | /* Allow denorms as operands */
+ FPC_PE); /* No trap for precision loss */
+ }
fldcw(control);
}
/*
+ * Inherit FPU state from a parent to a child, if any
+ */
+void fpinherit(thread_t parent_thread, thread_t thread)
+{
+ pcb_t pcb = parent_thread->pcb;
+ struct i386_fpsave_state *ifps;
+
+ ifps = pcb->ims.ifps;
+ if (ifps) {
+ /* Parent does have a state, inherit it */
+ if (ifps->fp_valid == TRUE)
+ thread->pcb->init_control = ifps->fp_save_state.fp_control;
+ else
+ /* State is in the FPU, fetch from there */
+ fnstcw(&thread->pcb->init_control);
+ }
+}
+
+/*
* Coprocessor not present.
*/
void
@@ -778,7 +803,7 @@ ASSERT_IPL(SPL0);
ifps = (struct i386_fpsave_state *) kmem_cache_alloc(&ifps_cache);
memset(ifps, 0, sizeof *ifps);
pcb->ims.ifps = ifps;
- fpinit();
+ fpinit(thread);
#if 1
/*
* I'm not sure this is needed. Does the fpu regenerate the interrupt in
diff --git a/i386/i386/fpu.h b/i386/i386/fpu.h
index 7bc64388..caade5d4 100644
--- a/i386/i386/fpu.h
+++ b/i386/i386/fpu.h
@@ -125,5 +125,6 @@ extern void fpexterrflt(void);
extern void fpastintr(void);
extern void init_fpu(void);
extern void fpintr(int unit);
+extern void fpinherit(thread_t parent_thread, thread_t thread);
#endif /* _I386_FPU_H_ */
diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c
index 6b22e4c4..dd2042d8 100644
--- a/i386/i386/pcb.c
+++ b/i386/i386/pcb.c
@@ -376,7 +376,7 @@ void pcb_module_init(void)
fpu_module_init();
}
-void pcb_init(thread_t thread)
+void pcb_init(task_t parent_task, thread_t thread)
{
pcb_t pcb;
@@ -406,6 +406,11 @@ void pcb_init(thread_t thread)
pcb->iss.efl = EFL_USER_SET;
thread->pcb = pcb;
+
+ /* This is a new thread for the current task, make it inherit our FPU
+ state. */
+ if (parent_task == current_task())
+ fpinherit(current_thread(), thread);
}
void pcb_terminate(thread_t thread)
diff --git a/i386/i386/pcb.h b/i386/i386/pcb.h
index 708f30d8..cf476942 100644
--- a/i386/i386/pcb.h
+++ b/i386/i386/pcb.h
@@ -31,7 +31,7 @@
#include <mach/thread_status.h>
#include <machine/thread.h>
-extern void pcb_init (thread_t thread);
+extern void pcb_init (task_t parent_task, thread_t thread);
extern void pcb_terminate (thread_t thread);
diff --git a/i386/i386/thread.h b/i386/i386/thread.h
index 35a28025..9bda11f5 100644
--- a/i386/i386/thread.h
+++ b/i386/i386/thread.h
@@ -177,6 +177,7 @@ typedef struct pcb {
struct i386_saved_state iss;
struct i386_machine_state ims;
decl_simple_lock_data(, lock)
+ unsigned short init_control; /* Initial FPU control to set */
#ifdef LINUX_DEV
void *data;
#endif /* LINUX_DEV */
diff --git a/kern/thread.c b/kern/thread.c
index d8d6af57..a2ec3d31 100644
--- a/kern/thread.c
+++ b/kern/thread.c
@@ -440,7 +440,7 @@ kern_return_t thread_create(
* Create a pcb. The kernel stack is created later,
* when the thread is swapped-in.
*/
- pcb_init(new_thread);
+ pcb_init(parent_task, new_thread);
ipc_thread_init(new_thread);