|
@@ -678,6 +678,19 @@ void fpu__resume_cpu(void)
|
|
xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
|
|
xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Given an xstate feature mask, calculate where in the xsave
|
|
|
|
+ * buffer the state is. Callers should ensure that the buffer
|
|
|
|
+ * is valid.
|
|
|
|
+ *
|
|
|
|
+ * Note: does not work for compacted buffers.
|
|
|
|
+ */
|
|
|
|
+void *__raw_xsave_addr(struct xregs_state *xsave, int xstate_feature_mask)
|
|
|
|
+{
|
|
|
|
+ int feature_nr = fls64(xstate_feature_mask) - 1;
|
|
|
|
+
|
|
|
|
+ return (void *)xsave + xstate_comp_offsets[feature_nr];
|
|
|
|
+}
|
|
/*
|
|
/*
|
|
* Given the xsave area and a state inside, this function returns the
|
|
* Given the xsave area and a state inside, this function returns the
|
|
* address of the state.
|
|
* address of the state.
|
|
@@ -698,7 +711,6 @@ void fpu__resume_cpu(void)
|
|
*/
|
|
*/
|
|
void *get_xsave_addr(struct xregs_state *xsave, int xstate_feature)
|
|
void *get_xsave_addr(struct xregs_state *xsave, int xstate_feature)
|
|
{
|
|
{
|
|
- int feature_nr = fls64(xstate_feature) - 1;
|
|
|
|
/*
|
|
/*
|
|
* Do we even *have* xsave state?
|
|
* Do we even *have* xsave state?
|
|
*/
|
|
*/
|
|
@@ -726,7 +738,7 @@ void *get_xsave_addr(struct xregs_state *xsave, int xstate_feature)
|
|
if (!(xsave->header.xfeatures & xstate_feature))
|
|
if (!(xsave->header.xfeatures & xstate_feature))
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
- return (void *)xsave + xstate_comp_offsets[feature_nr];
|
|
|
|
|
|
+ return __raw_xsave_addr(xsave, xstate_feature);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(get_xsave_addr);
|
|
EXPORT_SYMBOL_GPL(get_xsave_addr);
|
|
|
|
|
|
@@ -761,3 +773,85 @@ const void *get_xsave_field_ptr(int xsave_state)
|
|
|
|
|
|
return get_xsave_addr(&fpu->state.xsave, xsave_state);
|
|
return get_xsave_addr(&fpu->state.xsave, xsave_state);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Set xfeatures (aka XSTATE_BV) bit for a feature that we want
|
|
|
|
+ * to take out of its "init state". This will ensure that an
|
|
|
|
+ * XRSTOR actually restores the state.
|
|
|
|
+ */
|
|
|
|
+static void fpu__xfeature_set_non_init(struct xregs_state *xsave,
|
|
|
|
+ int xstate_feature_mask)
|
|
|
|
+{
|
|
|
|
+ xsave->header.xfeatures |= xstate_feature_mask;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * This function is safe to call whether the FPU is in use or not.
|
|
|
|
+ *
|
|
|
|
+ * Note that this only works on the current task.
|
|
|
|
+ *
|
|
|
|
+ * Inputs:
|
|
|
|
+ * @xsave_state: state which is defined in xsave.h (e.g. XFEATURE_MASK_FP,
|
|
|
|
+ * XFEATURE_MASK_SSE, etc...)
|
|
|
|
+ * @xsave_state_ptr: a pointer to a copy of the state that you would
|
|
|
|
+ * like written in to the current task's FPU xsave state. This pointer
|
|
|
|
+ * must not be located in the current tasks's xsave area.
|
|
|
|
+ * Output:
|
|
|
|
+ * address of the state in the xsave area or NULL if the state
|
|
|
|
+ * is not present or is in its 'init state'.
|
|
|
|
+ */
|
|
|
|
+static void fpu__xfeature_set_state(int xstate_feature_mask,
|
|
|
|
+ void *xstate_feature_src, size_t len)
|
|
|
|
+{
|
|
|
|
+ struct xregs_state *xsave = ¤t->thread.fpu.state.xsave;
|
|
|
|
+ struct fpu *fpu = ¤t->thread.fpu;
|
|
|
|
+ void *dst;
|
|
|
|
+
|
|
|
|
+ if (!boot_cpu_has(X86_FEATURE_XSAVE)) {
|
|
|
|
+ WARN_ONCE(1, "%s() attempted with no xsave support", __func__);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Tell the FPU code that we need the FPU state to be in
|
|
|
|
+ * 'fpu' (not in the registers), and that we need it to
|
|
|
|
+ * be stable while we write to it.
|
|
|
|
+ */
|
|
|
|
+ fpu__current_fpstate_write_begin();
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This method *WILL* *NOT* work for compact-format
|
|
|
|
+ * buffers. If the 'xstate_feature_mask' is unset in
|
|
|
|
+ * xcomp_bv then we may need to move other feature state
|
|
|
|
+ * "up" in the buffer.
|
|
|
|
+ */
|
|
|
|
+ if (xsave->header.xcomp_bv & xstate_feature_mask) {
|
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* find the location in the xsave buffer of the desired state */
|
|
|
|
+ dst = __raw_xsave_addr(&fpu->state.xsave, xstate_feature_mask);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Make sure that the pointer being passed in did not
|
|
|
|
+ * come from the xsave buffer itself.
|
|
|
|
+ */
|
|
|
|
+ WARN_ONCE(xstate_feature_src == dst, "set from xsave buffer itself");
|
|
|
|
+
|
|
|
|
+ /* put the caller-provided data in the location */
|
|
|
|
+ memcpy(dst, xstate_feature_src, len);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Mark the xfeature so that the CPU knows there is state
|
|
|
|
+ * in the buffer now.
|
|
|
|
+ */
|
|
|
|
+ fpu__xfeature_set_non_init(xsave, xstate_feature_mask);
|
|
|
|
+out:
|
|
|
|
+ /*
|
|
|
|
+ * We are done writing to the 'fpu'. Reenable preeption
|
|
|
|
+ * and (possibly) move the fpstate back in to the fpregs.
|
|
|
|
+ */
|
|
|
|
+ fpu__current_fpstate_write_end();
|
|
|
|
+}
|