diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index a580eb5c7e5284..af3700e0dbd2ab 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -382,19 +382,48 @@ void fpu__resume_cpu(void) * This is the API that is called to get xstate address in either * standard format or compacted format of xsave area. * + * Note that if there is no data for the field in the xsave buffer + * this will return NULL. + * * Inputs: - * xsave: base address of the xsave area; - * xstate: state which is defined in xsave.h (e.g. XSTATE_FP, XSTATE_SSE, - * etc.) + * xstate: the thread's storage area for all FPU data + * xstate_feature: state which is defined in xsave.h (e.g. + * XSTATE_FP, XSTATE_SSE, etc...) * Output: - * address of the state in the xsave area. + * address of the state in the xsave area, or NULL if the + * field is not present in the xsave buffer. */ -void *get_xsave_addr(struct xregs_state *xsave, int xstate) +void *get_xsave_addr(struct xregs_state *xsave, int xstate_feature) { - int feature = fls64(xstate) - 1; - if (!test_bit(feature, (unsigned long *)&xfeatures_mask)) + int feature_nr = fls64(xstate_feature) - 1; + /* + * Do we even *have* xsave state? + */ + if (!boot_cpu_has(X86_FEATURE_XSAVE)) + return NULL; + + xsave = ¤t->thread.fpu.state.xsave; + /* + * We should not ever be requesting features that we + * have not enabled. Remember that pcntxt_mask is + * what we write to the XCR0 register. + */ + WARN_ONCE(!(xfeatures_mask & xstate_feature), + "get of unsupported state"); + /* + * This assumes the last 'xsave*' instruction to + * have requested that 'xstate_feature' be saved. + * If it did not, we might be seeing and old value + * of the field in the buffer. + * + * This can happen because the last 'xsave' did not + * request that this feature be saved (unlikely) + * or because the "init optimization" caused it + * to not be saved. + */ + if (!(xsave->header.xfeatures & xstate_feature)) return NULL; - return (void *)xsave + xstate_comp_offsets[feature]; + return (void *)xsave + xstate_comp_offsets[feature_nr]; } EXPORT_SYMBOL_GPL(get_xsave_addr);