* i386/i386/fpu.c: extend current getter and setter to support the extended state; move the struct casting here to reuse the locking and allocation logic for the thread state; make sure the new state is set as valid, otherwise it won't be applied; add i386_get_xstate_size() to dynamically retrieve the FPU state size. * i386/i386/fpu.h: update prototypes to accept generic thread state * i386/i386/pcb.c: forward raw thread state to getter and setter, only checking for minimum size and use the new i386_get_xstate_size() helper. * i386/include/mach/i386/mach_i386.defs: expose the new helper i386_get_xstate_size(). * i386/include/mach/i386/thread_status.h: add interface definition for I386_XFLOAT_STATE and the corresponding data structure. --- i386/i386/fpu.c | 139 +++++++++++++++++++------ i386/i386/fpu.h | 9 +- i386/i386/pcb.c | 37 ++++++- i386/include/mach/i386/mach_i386.defs | 7 ++ i386/include/mach/i386/thread_status.h | 12 +++ 5 files changed, 161 insertions(+), 43 deletions(-)
diff --git a/i386/i386/fpu.c b/i386/i386/fpu.c index 316e3b41..43771e7f 100644 --- a/i386/i386/fpu.c +++ b/i386/i386/fpu.c @@ -250,6 +250,17 @@ init_fpu(void) } } +kern_return_t +i386_get_xstate_size(host_t host, vm_size_t *size) +{ + if (host == HOST_NULL) + return KERN_INVALID_ARGUMENT; + + *size = sizeof(struct i386_xfloat_state) + fp_xsave_size; + + return KERN_SUCCESS; +} + /* * Initialize FP handling. */ @@ -385,10 +396,11 @@ twd_fxsr_to_i387 (struct i386_xfp_save *fxsave) * concurrent fpu_set_state or fpu_get_state. */ kern_return_t -fpu_set_state(const thread_t thread, - struct i386_float_state *state) +fpu_set_state(const thread_t thread, void *state, int flavor) { pcb_t pcb = thread->pcb; + struct i386_float_state *fstate = (struct i386_float_state*)state; + struct i386_xfloat_state *xfstate = (struct i386_xfloat_state*)state; struct i386_fpsave_state *ifps; struct i386_fpsave_state *new_ifps; @@ -410,7 +422,8 @@ ASSERT_IPL(SPL0); } #endif - if (state->initialized == 0) { + if ((flavor == i386_FLOAT_STATE && fstate->initialized == 0) || + (flavor == i386_XFLOAT_STATE && xfstate->initialized == 0)) { /* * new FPU state is 'invalid'. * Deallocate the fp state if it exists. @@ -428,13 +441,6 @@ ASSERT_IPL(SPL0); /* * Valid state. Allocate the fp state if there is none. */ - struct i386_fp_save *user_fp_state; - struct i386_fp_regs *user_fp_regs; - - user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; - user_fp_regs = (struct i386_fp_regs *) - &state->hw_state[sizeof(struct i386_fp_save)]; - new_ifps = 0; Retry: simple_lock(&pcb->lock); @@ -454,10 +460,43 @@ ASSERT_IPL(SPL0); * Ensure that reserved parts of the environment are 0. */ memset(ifps, 0, fp_xsave_size); + ifps->fp_valid = TRUE; - if (fp_save_kind != FP_FNSAVE) { - int i; + if (flavor == i386_FLOAT_STATE) { + struct i386_fp_save *user_fp_state; + struct i386_fp_regs *user_fp_regs; + + user_fp_state = (struct i386_fp_save *) &fstate->hw_state[0]; + user_fp_regs = (struct i386_fp_regs *) + &fstate->hw_state[sizeof(struct i386_fp_save)]; + if (fp_save_kind != FP_FNSAVE) { + int i; + + ifps->xfp_save_state.fp_control = user_fp_state->fp_control; + ifps->xfp_save_state.fp_status = user_fp_state->fp_status; + ifps->xfp_save_state.fp_tag = twd_i387_to_fxsr(user_fp_state->fp_tag); + ifps->xfp_save_state.fp_eip = user_fp_state->fp_eip; + ifps->xfp_save_state.fp_cs = user_fp_state->fp_cs; + ifps->xfp_save_state.fp_opcode = user_fp_state->fp_opcode; + ifps->xfp_save_state.fp_dp = user_fp_state->fp_dp; + ifps->xfp_save_state.fp_ds = user_fp_state->fp_ds; + for (i=0; i<8; i++) + memcpy(&ifps->xfp_save_state.fp_reg_word[i], &user_fp_regs->fp_reg_word[i], sizeof(user_fp_regs->fp_reg_word[i])); + } else { + ifps->fp_save_state.fp_control = user_fp_state->fp_control; + ifps->fp_save_state.fp_status = user_fp_state->fp_status; + ifps->fp_save_state.fp_tag = user_fp_state->fp_tag; + ifps->fp_save_state.fp_eip = user_fp_state->fp_eip; + ifps->fp_save_state.fp_cs = user_fp_state->fp_cs; + ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode; + ifps->fp_save_state.fp_dp = user_fp_state->fp_dp; + ifps->fp_save_state.fp_ds = user_fp_state->fp_ds; + ifps->fp_regs = *user_fp_regs; + } + } else if ((flavor == i386_XFLOAT_STATE) && (xfstate->fp_save_kind == fp_save_kind)) { + int i; + struct i386_xfp_save *user_fp_state = (struct i386_xfp_save *) &xfstate->hw_state[0]; ifps->xfp_save_state.fp_control = user_fp_state->fp_control; ifps->xfp_save_state.fp_status = user_fp_state->fp_status; ifps->xfp_save_state.fp_tag = twd_i387_to_fxsr(user_fp_state->fp_tag); @@ -467,17 +506,17 @@ ASSERT_IPL(SPL0); ifps->xfp_save_state.fp_dp = user_fp_state->fp_dp; ifps->xfp_save_state.fp_ds = user_fp_state->fp_ds; for (i=0; i<8; i++) - memcpy(&ifps->xfp_save_state.fp_reg_word[i], &user_fp_regs->fp_reg_word[i], sizeof(user_fp_regs->fp_reg_word[i])); - } else { - ifps->fp_save_state.fp_control = user_fp_state->fp_control; - ifps->fp_save_state.fp_status = user_fp_state->fp_status; - ifps->fp_save_state.fp_tag = user_fp_state->fp_tag; - ifps->fp_save_state.fp_eip = user_fp_state->fp_eip; - ifps->fp_save_state.fp_cs = user_fp_state->fp_cs; - ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode; - ifps->fp_save_state.fp_dp = user_fp_state->fp_dp; - ifps->fp_save_state.fp_ds = user_fp_state->fp_ds; - ifps->fp_regs = *user_fp_regs; + memcpy(&ifps->xfp_save_state.fp_reg_word[i], &user_fp_state->fp_reg_word[i], sizeof(user_fp_state->fp_reg_word[i])); + for (i=0; i<16; i++) + memcpy(&ifps->xfp_save_state.fp_xreg_word[i], &user_fp_state->fp_xreg_word[i], sizeof(user_fp_state->fp_xreg_word[i])); + + memcpy(&ifps->xfp_save_state.header, &user_fp_state->header, + sizeof(ifps->xfp_save_state.header)); + if (fp_xsave_size > sizeof(struct i386_xfp_save)) { + memcpy(&ifps->xfp_save_state.extended, &user_fp_state->extended, + fp_xsave_size - sizeof(struct i386_xfp_save)); + } + } simple_unlock(&pcb->lock); @@ -495,10 +534,11 @@ ASSERT_IPL(SPL0); * concurrent fpu_set_state or fpu_get_state. */ kern_return_t -fpu_get_state(const thread_t thread, - struct i386_float_state *state) +fpu_get_state(const thread_t thread, void *state, int flavor) { pcb_t pcb = thread->pcb; + struct i386_float_state *fstate = (struct i386_float_state*)state; + struct i386_xfloat_state *xfstate = (struct i386_xfloat_state*)state; struct i386_fpsave_state *ifps; ASSERT_IPL(SPL0); @@ -512,7 +552,10 @@ ASSERT_IPL(SPL0); * No valid floating-point state. */ simple_unlock(&pcb->lock); - memset(state, 0, sizeof(struct i386_float_state)); + if (flavor == i386_FLOAT_STATE) + memset(state, 0, sizeof(struct i386_float_state)); + else if (flavor == i386_XFLOAT_STATE) + memset(state, 0, fp_xsave_size); return KERN_SUCCESS; } @@ -529,18 +572,17 @@ ASSERT_IPL(SPL0); clear_fpu(); } - state->fpkind = fp_kind; - state->exc_status = 0; - - { + if (flavor == i386_FLOAT_STATE) { struct i386_fp_save *user_fp_state; struct i386_fp_regs *user_fp_regs; - state->initialized = ifps->fp_valid; + fstate->fpkind = fp_kind; + fstate->exc_status = 0; + fstate->initialized = ifps->fp_valid; - user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; + user_fp_state = (struct i386_fp_save *) &fstate->hw_state[0]; user_fp_regs = (struct i386_fp_regs *) - &state->hw_state[sizeof(struct i386_fp_save)]; + &fstate->hw_state[sizeof(struct i386_fp_save)]; /* * Ensure that reserved parts of the environment are 0. @@ -571,6 +613,37 @@ ASSERT_IPL(SPL0); user_fp_state->fp_ds = ifps->fp_save_state.fp_ds; *user_fp_regs = ifps->fp_regs; } + } else if (flavor == i386_XFLOAT_STATE) { + int i; + struct i386_xfp_save *user_fp_state; + + xfstate->fpkind = fp_kind; + xfstate->exc_status = 0; + xfstate->initialized = ifps->fp_valid; + xfstate->fp_save_kind = fp_save_kind; + + user_fp_state = (struct i386_xfp_save *) &xfstate->hw_state[0]; + memset(user_fp_state, 0, sizeof(struct i386_xfp_save)); + + user_fp_state->fp_control = ifps->xfp_save_state.fp_control; + user_fp_state->fp_status = ifps->xfp_save_state.fp_status; + user_fp_state->fp_tag = twd_fxsr_to_i387(&ifps->xfp_save_state); + user_fp_state->fp_eip = ifps->xfp_save_state.fp_eip; + user_fp_state->fp_cs = ifps->xfp_save_state.fp_cs; + user_fp_state->fp_opcode = ifps->xfp_save_state.fp_opcode; + user_fp_state->fp_dp = ifps->xfp_save_state.fp_dp; + user_fp_state->fp_ds = ifps->xfp_save_state.fp_ds; + for (i=0; i<8; i++) + memcpy(&user_fp_state->fp_reg_word[i], &ifps->xfp_save_state.fp_reg_word[i], sizeof(user_fp_state->fp_reg_word[i])); + for (i=0; i<16; i++) + memcpy(&user_fp_state->fp_xreg_word[i], &ifps->xfp_save_state.fp_xreg_word[i], sizeof(user_fp_state->fp_xreg_word[i])); + + memcpy(&user_fp_state->header, &ifps->xfp_save_state.header, + sizeof(ifps->xfp_save_state.header)); + if (fp_xsave_size > sizeof(struct i386_xfp_save)) { + memcpy(&user_fp_state->extended, &ifps->xfp_save_state.extended, + fp_xsave_size - sizeof(struct i386_xfp_save)); + } } simple_unlock(&pcb->lock); diff --git a/i386/i386/fpu.h b/i386/i386/fpu.h index 51e0f31d..83a8f2d6 100644 --- a/i386/i386/fpu.h +++ b/i386/i386/fpu.h @@ -35,6 +35,7 @@ #include <sys/types.h> #include <i386/proc_reg.h> #include <kern/thread.h> +#include "i386/i386/mach_i386.server.h" /* * FPU instructions. @@ -233,12 +234,8 @@ extern void fp_save(thread_t thread); extern void fp_load(thread_t thread); extern void fp_free(struct i386_fpsave_state *fps); extern void fpu_module_init(void); -extern kern_return_t fpu_set_state( - thread_t thread, - struct i386_float_state *state); -extern kern_return_t fpu_get_state( - thread_t thread, - struct i386_float_state *state); +extern kern_return_t fpu_set_state(thread_t thread, void *state, int flavor); +extern kern_return_t fpu_get_state(thread_t thread, void *state, int flavor); extern void fpnoextflt(void); extern void fpextovrflt(void); extern void fpexterrflt(void); diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c index e8901550..5ca6f124 100644 --- a/i386/i386/pcb.c +++ b/i386/i386/pcb.c @@ -613,8 +613,22 @@ kern_return_t thread_setstatus( if (count < i386_FLOAT_STATE_COUNT) return(KERN_INVALID_ARGUMENT); - return fpu_set_state(thread, - (struct i386_float_state *) tstate); + return fpu_set_state(thread, tstate, flavor); + } + + case i386_XFLOAT_STATE: { + + vm_size_t xfp_size; + kern_return_t kr; + kr = i386_get_xstate_size(&realhost, &xfp_size); + if (kr != KERN_SUCCESS) + return kr; + + xfp_size /= sizeof(integer_t); + if (count < xfp_size) + return(KERN_INVALID_ARGUMENT); + + return fpu_set_state(thread, tstate, flavor); } /* @@ -831,8 +845,23 @@ kern_return_t thread_getstatus( return(KERN_INVALID_ARGUMENT); *count = i386_FLOAT_STATE_COUNT; - return fpu_get_state(thread, - (struct i386_float_state *)tstate); + return fpu_get_state(thread, tstate, flavor); + } + + case i386_XFLOAT_STATE: { + + vm_size_t xfp_size; + kern_return_t kr; + kr = i386_get_xstate_size(&realhost, &xfp_size); + if (kr != KERN_SUCCESS) + return kr; + + xfp_size /= sizeof(integer_t); + if (*count < xfp_size) + return(KERN_INVALID_ARGUMENT); + + *count = xfp_size; + return fpu_get_state(thread, tstate, flavor); } /* diff --git a/i386/include/mach/i386/mach_i386.defs b/i386/include/mach/i386/mach_i386.defs index 965d5c3b..78bbc2ed 100644 --- a/i386/include/mach/i386/mach_i386.defs +++ b/i386/include/mach/i386/mach_i386.defs @@ -111,3 +111,10 @@ routine i386_get_gdt( target_thread : thread_t; selector : int; out desc : descriptor_t); + +/* Returns the size in bytes of the FPU extended state, to be used + with i386_XFLOAT_STATE instead of i386_XFLOAT_STATE_COUNT. + */ +routine i386_get_xstate_size( + host : host_t; + out size : vm_size_t); diff --git a/i386/include/mach/i386/thread_status.h b/i386/include/mach/i386/thread_status.h index 94596a74..2f472247 100644 --- a/i386/include/mach/i386/thread_status.h +++ b/i386/include/mach/i386/thread_status.h @@ -58,6 +58,7 @@ #define i386_REGS_SEGS_STATE 5 #define i386_DEBUG_STATE 6 #define i386_FSGS_BASE_STATE 7 +#define i386_XFLOAT_STATE 8 /* * This structure is used for both @@ -148,6 +149,17 @@ struct i386_float_state { }; #define i386_FLOAT_STATE_COUNT (sizeof(struct i386_float_state)/sizeof(unsigned int)) +struct i386_xfloat_state { + int fpkind; /* FP_NO..FP_387X (readonly) */ + int initialized; + int exc_status; /* exception status (readonly) */ + int fp_save_kind; /* format of hardware state */ + unsigned char hw_state[]; /* actual "hardware" state */ + /* don't add anything here, as hw_state is dynamically sized */ +}; +/* NOTE: i386_XFLOAT_STATE_COUNT is not defined as i386_xfloat_state is + * dynamically sized. Use i386_get_xstate_size(host) to get the current + * size. */ #define PORT_MAP_BITS 0x400 struct i386_isa_port_map_state { -- 2.39.2