+/*
+ * Set the floating-point state for a thread based
+ * on the FXSave formatted data. This is basically
+ * the same as fpu_set_state except it uses the
+ * expanded data structure.
+ * If the thread is not the current thread, it is
+ * not running (held). Locking needed against
+ * concurrent fpu_set_state or fpu_get_state.
+ */
+kern_return_t
+fpu_set_fxstate(
+ thread_act_t thr_act,
+ struct i386_float_state *state)
+{
+ register pcb_t pcb;
+ register struct i386_fpsave_state *ifps;
+ register struct i386_fpsave_state *new_ifps;
+
+ASSERT_IPL(SPL0);
+ if (fp_kind == FP_NO)
+ return KERN_FAILURE;
+
+ if (state->fpkind != FP_FXSR) {
+ /* strange if this happens, but in case someone builds one of these manually... */
+ return fpu_set_state(thr_act, state);
+ }
+
+ assert(thr_act != THR_ACT_NULL);
+ pcb = thr_act->mact.pcb;
+
+#if NCPUS == 1
+
+ /*
+ * If this thread`s state is in the FPU,
+ * discard it; we are replacing the entire
+ * FPU state.
+ */
+ if (fp_act == thr_act) {
+ fwait(); /* wait for possible interrupt */
+ clear_fpu(); /* no state in FPU */
+ }
+#endif
+
+ if (state->initialized == 0) {
+ /*
+ * new FPU state is 'invalid'.
+ * Deallocate the fp state if it exists.
+ */
+ simple_lock(&pcb->lock);
+ ifps = pcb->ims.ifps;
+ pcb->ims.ifps = 0;
+ simple_unlock(&pcb->lock);
+
+ if (ifps != 0) {
+ zfree(ifps_zone, (vm_offset_t) ifps);
+ }
+ }
+ else {
+ /*
+ * Valid state. Allocate the fp state if there is none.
+ */
+
+ new_ifps = 0;
+ Retry:
+ simple_lock(&pcb->lock);
+ ifps = pcb->ims.ifps;
+ if (ifps == 0) {
+ if (new_ifps == 0) {
+ simple_unlock(&pcb->lock);
+ new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
+ assert(ALIGNED(new_ifps,16));
+ goto Retry;
+ }
+ ifps = new_ifps;
+ new_ifps = 0;
+ bzero((char *)ifps, sizeof *ifps);
+ pcb->ims.ifps = ifps;
+ }
+
+ /*
+ * now copy over the new data.
+ */
+ bcopy((char *)&state->hw_state[0], (char *)&ifps->fx_save_state, sizeof(struct i386_fx_save));
+ ifps->fp_save_flavor = FP_FXSR;
+ simple_unlock(&pcb->lock);
+ if (new_ifps != 0)
+ zfree(ifps_zone, (vm_offset_t) ifps);
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Get the floating-point state for a thread.
+ * If the thread is not the current thread, it is
+ * not running (held). Locking needed against
+ * concurrent fpu_set_state or fpu_get_state.
+ */
+kern_return_t
+fpu_get_fxstate(
+ thread_act_t thr_act,
+ register struct i386_float_state *state)
+{
+ register pcb_t pcb;
+ register struct i386_fpsave_state *ifps;
+
+ASSERT_IPL(SPL0);
+ if (fp_kind == FP_NO)
+ return KERN_FAILURE;
+
+ assert(thr_act != THR_ACT_NULL);
+ pcb = thr_act->mact.pcb;
+
+ simple_lock(&pcb->lock);
+ ifps = pcb->ims.ifps;
+ if (ifps == 0) {
+ /*
+ * No valid floating-point state.
+ */
+ simple_unlock(&pcb->lock);
+ bzero((char *)state, sizeof(struct i386_float_state));
+ return KERN_SUCCESS;
+ }
+
+ /* Make sure we`ve got the latest fp state info */
+ /* If the live fpu state belongs to our target */
+#if NCPUS == 1
+ if (thr_act == fp_act)
+#else
+ if (thr_act == current_act())
+#endif
+ {
+ clear_ts();
+ fp_save(thr_act);
+ clear_fpu();
+ }
+
+ state->fpkind = fp_kind;
+ state->exc_status = 0;
+ state->initialized = ifps->fp_valid;
+ bcopy( (char *)&ifps->fx_save_state, (char *)&state->hw_state[0], sizeof(struct i386_fx_save));
+
+ simple_unlock(&pcb->lock);
+
+ return KERN_SUCCESS;
+}
+