diff --git a/sys/amd64/amd64/fpu.c b/sys/amd64/amd64/fpu.c --- a/sys/amd64/amd64/fpu.c +++ b/sys/amd64/amd64/fpu.c @@ -164,12 +164,14 @@ int use_xsave; /* non-static for cpu_switch.S */ uint64_t xsave_mask; /* the same */ +static uint64_t xsave_extensions; static uma_zone_t fpu_save_area_zone; static struct savefpu *fpu_initialstate; static struct xsave_area_elm_descr { u_int offset; u_int size; + u_int flags; } *xsave_area_desc; static void @@ -452,6 +454,9 @@ * Region of an XSAVE Area" for the source of offsets/sizes. */ if (use_xsave) { + cpuid_count(0xd, 1, cp); + xsave_extensions = cp[0]; + xstate_bv = (uint64_t *)((char *)(fpu_initialstate + 1) + offsetof(struct xstate_hdr, xstate_bv)); *xstate_bv = XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE; @@ -465,8 +470,9 @@ for (i = 2; i < max_ext_n; i++) { cpuid_count(0xd, i, cp); - xsave_area_desc[i].offset = cp[1]; xsave_area_desc[i].size = cp[0]; + xsave_area_desc[i].offset = cp[1]; + xsave_area_desc[i].flags = cp[2]; } } @@ -1285,3 +1291,106 @@ bcopy(fpu_initialstate, fsa, cpu_max_ext_state_size); } + +static __inline void +xsave_extfeature_check(uint64_t feature) +{ + + KASSERT((feature & (feature - 1)) == 0, + ("%s: invalid XFEATURE 0x%lx", __func__, feature)); + KASSERT(feature < flsl(xsave_mask), + ("%s: unsupported XFEATURE 0x%lx", __func__, feature)); +} + +static __inline void +xsave_extstate_bv_check(uint64_t xstate_bv) +{ + KASSERT(xstate_bv != 0 && ilog2(xstate_bv) < flsl(xsave_mask), + ("%s: invalid XSTATE_BV 0x%lx", __func__, xstate_bv)); +} + +/* + * Returns whether the XFEATURE 'feature' is supported as a user state + * or supervisor state component. + */ +bool +xsave_extfeature_supported(uint64_t feature, bool supervisor) +{ + int idx; + + KASSERT(use_xsave, ("%s: XSAVE not supported", __func__)); + xsave_extfeature_check(feature); + + if ((xsave_mask & feature) == 0) + return (false); + idx = ilog2(feature); + return (((xsave_area_desc[idx].flags & CPUID_EXTSTATE_SUPERVISOR) != 0) == + supervisor); +} + +/* + * Returns whether the given XSAVE extension is supported. + */ +bool +xsave_extension_supported(uint64_t extension) +{ + KASSERT(use_xsave, ("%s: XSAVE not supported", __func__)); + + return ((xsave_extensions & extension) != 0); +} + +/* + * Returns offset for XFEATURE 'feature' given the requested feature bitmap + * 'xstate_bv', and extended region format ('compact'). + */ +size_t +xsave_area_offset(uint64_t xstate_bv, uint64_t feature, + bool compact) +{ + int i, idx; + size_t offs; + struct xsave_area_elm_descr *xep; + + KASSERT(use_xsave, ("%s: XSAVE not supported", __func__)); + xsave_extstate_bv_check(xstate_bv); + xsave_extfeature_check(feature); + + idx = ilog2(feature); + if (!compact) + return (xsave_area_desc[idx].offset); + offs = sizeof(struct savefpu) + sizeof(struct xstate_hdr); + xstate_bv &= ~(XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE); + while ((i = ffs(xstate_bv) - 1) > 0 && i < idx) { + xep = &xsave_area_desc[i]; + if ((xep->flags & CPUID_EXTSTATE_ALIGNED) != 0) + offs = roundup2(offs, 64); + offs += xep->size; + xstate_bv &= ~((uint64_t)1 << i); + } + + return (offs); +} + +/* + * Returns the XSAVE area size for the requested feature bitmap + * 'xstate_bv' and extended region format ('compact'). + */ +size_t +xsave_area_size(uint64_t xstate_bv, bool compact) +{ + int last_idx; + + KASSERT(use_xsave, ("%s: XSAVE not supported", __func__)); + xsave_extstate_bv_check(xstate_bv); + + last_idx = ilog2(xstate_bv); + + return (xsave_area_offset(xstate_bv, (uint64_t)1 << last_idx, compact) + + xsave_area_desc[last_idx].size); +} + +size_t +xsave_area_hdr_offset(void) +{ + return (sizeof(struct savefpu)); +} diff --git a/sys/x86/include/fpu.h b/sys/x86/include/fpu.h --- a/sys/x86/include/fpu.h +++ b/sys/x86/include/fpu.h @@ -218,6 +218,12 @@ */ #define fpu_enable() clts() #define fpu_disable() load_cr0(rcr0() | CR0_TS) + +bool xsave_extfeature_supported(uint64_t feature, bool supervisor); +bool xsave_extension_supported(uint64_t extension); +size_t xsave_area_hdr_offset(void); +size_t xsave_area_offset(uint64_t xstate_bv, uint64_t feature, bool compact); +size_t xsave_area_size(uint64_t xstate_bv, bool compact); #endif #endif /* !_X86_FPU_H_ */