git: 7bcaff05223e - main - x86: Add routines for querying XSAVE feature information

From: Bojan Novković <bnovkov_at_FreeBSD.org>
Date: Sun, 15 Dec 2024 15:40:39 UTC
The branch main has been updated by bnovkov:

URL: https://cgit.FreeBSD.org/src/commit/?id=7bcaff05223eb81611372e341a120391925fa724

commit 7bcaff05223eb81611372e341a120391925fa724
Author:     Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2024-12-15 14:04:58 +0000
Commit:     Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2024-12-15 15:39:36 +0000

    x86: Add routines for querying XSAVE feature information
    
    This patch adds several routines that track and expose information
    about various XSAVE-related features. More specifically, it adds the
    ability to check whether a given XFEATURE is supported and which XSAVE
    extensions are supported. Furthermore, it adds several routines for
    calculating the size and offsets within a save area given a XSAVE
    feature bitmap.
    
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D47394
---
 sys/amd64/amd64/fpu.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++-
 sys/x86/include/fpu.h |   6 +++
 2 files changed, 116 insertions(+), 1 deletion(-)

diff --git a/sys/amd64/amd64/fpu.c b/sys/amd64/amd64/fpu.c
index 58a135e827a8..591bd196ca7d 100644
--- a/sys/amd64/amd64/fpu.c
+++ b/sys/amd64/amd64/fpu.c
@@ -164,12 +164,14 @@ SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD,
 
 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 @@ fpuinitstate(void *arg __unused)
 	 * 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 @@ fpuinitstate(void *arg __unused)
 
 		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 @@ fpu_save_area_reset(struct savefpu *fsa)
 
 	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
index cf2235e4d2bf..9d2e43f6386e 100644
--- a/sys/x86/include/fpu.h
+++ b/sys/x86/include/fpu.h
@@ -218,6 +218,12 @@ struct savefpu_ymm {
  */
 #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_ */