diff options
-rw-r--r-- | i386/i386/fpu.c | 49 | ||||
-rw-r--r-- | i386/i386/fpu.h | 1 | ||||
-rw-r--r-- | i386/i386/pcb.c | 7 | ||||
-rw-r--r-- | i386/i386/pcb.h | 2 | ||||
-rw-r--r-- | i386/i386/thread.h | 1 | ||||
-rw-r--r-- | kern/thread.c | 2 |
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); |