[PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model

Ahmed S. Darwish posted 34 patches 1 month, 2 weeks ago
There is a newer version of this series
[PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model
Posted by Ahmed S. Darwish 1 month, 2 weeks ago
** Context

The x86-cpuid-db project generates a C header file with full C99 bitfield
listings for all known CPUID leaf/subleaf query outputs.

That header is now merged by parent commits at <asm/cpuid/leaf_types.h>,
and is in the form:

    struct leaf_0x0_0 { /* CPUID(0x0), subleaf 0, C99 bitfields */ };
    ...
    struct leaf_0x7_0 { /* CPUID(0x7), subleaf 0, C99 bitfields */ };
    struct leaf_0x7_1 { /* CPUID(0x7), subleaf 1, C99 bitfields */ };
    ...

** Goal

Introduce a structured, size-efficient, per-CPU, CPUID data repository.

Use the x86-cpuid-db auto-generated data types, and custom CPUID leaf
parsers, to build that repository.  Given a leaf, subleaf, and index,
provide direct memory access to the parsed and cached per-CPU CPUID
output.

** Long-term goal

Remove the need for drivers and other areas in the kernel to invoke
direct CPUID queries.  Only one place in the kernel should be allowed to
use the CPUID instruction: the CPUID parser code.

** Implementation

Introduce CPUID_LEAF() to build a compact CPUID storage layout in the
form:

    struct leaf_0x0_0		leaf_0x0_0[1];
    struct leaf_query_info	leaf_0x0_0_info;

    struct leaf_0x1_0		leaf_0x1_0[1];
    struct leaf_query_info	leaf_0x0_0_info;

    struct leaf_0x4_0		leaf_0x4_0[8];
    struct leaf_query_info	leaf_0x4_0_info;
     ...

where each CPUID leaf 0xN subleaf M query stores its output at the
designated leaf_0xN_M[] array and has an associated "CPUID query info"
structure.

Introduce 'struct cpuid_leaves' to group all the parsed CPUID outputs and
their metadata –in the layout above– in one structure.  Define a 'struct
cpuid_table' to wrap it, so that global per-table CPUID data can be added
later.  Embed that 'struct cpuid_table' inside 'struct cpuinfo_x86' to
ensure early-boot and per-CPU access through the current CPU's capability
structure.

Given the data layout above, and assuming a CPU capability structure 'c',
a macro can access CPUID(0x7) subleaf 0 parsed query output using the
compile time tokenization below:

    const struct leaf_0x7_0 *l7_0;

    l7_0 = cpuid_subleaf(c, 0x7, 0);
                         |   |   └────────┐
                         |   └─────────┐  |
                         *             *  *
                        &c.cpuid.leaf_0x7_0[0]

Similarly, CPUID(0x7) subleaf 1 output can be accessed using the CPP
tokenization:

    const struct leaf_0x7_1 *l7_1;

    l7_1 = cpuid_subleaf(c, 0x7, 1);
                         |   |   └────────┐
                         |   └─────────┐  |
                         *             *  *
                        &c.cpuid.leaf_0x7_1[0]

which all translate to a single assembly instruction offset calculation.

Use an array of CPUID output storage entries for each leaf/subleaf
combination to accommodate leaves which produce the same output format
for a large subleaf range.  This is typical for CPUID leaves enumerating
hierarchical objects; e.g. CPUID(0x4) cache topology enumeration,
CPUID(0xd) XSAVE enumeration, and CPUID(0x12) SGX Enclave Page Cache
enumeration.

In the CPUID_LEAF() data layout above, CPUID(0x4) has 8 storage entries
to accomodate the suleaves 0 to 7, which all have the same bitfield's
output format.  With that, CPUID(0x4) subleaves 0->7 can be accessed
using the compile time tokenization:

    const struct leaf_0x4_0 *l4_0, *l4_1, l4_2;

    l4_0 = cpuid_subleaf_index(c, 0x4, 0);
                               |   |   └──────────┐
                               |   └─────────┐    |
                               *             *    v
                              &c.cpuid.leaf_0x4_0[0]

    l4_1 = cpuid_subleaf_index(c, 0x4, 1);
                               |   |   └──────────┐
                               |   └─────────┐    |
                               *             *    v
                              &c.cpuid.leaf_0x4_0[1]

    l4_2 = cpuid_subleaf_index(c, 0x4, 2);
                               |   |   └──────────┐
                               |   └─────────┐    |
                               *             *    v
                              &c.cpuid.leaf_0x4_0[2]

where the indices 0, 1, 2 above can be provided dynamically.  This is by
design since call-sites hierarchical CPUID enumeration usually passes the
CPUID subleaf enumeration index dynamically; e.g., within a for loop.

For each of the CPUID leaf/subleaf output storage entries, attach a
'struct leaf_query_info' leaf_0xN_M_info instance.  It is to be filled by
the CPUID parsing logic filling the CPUID table(s).  For now, this info
structure has one element: the number of filled slots by the CPUID
paraser in the CPUID leaf/subleaf output storage array.

** Call-site APIs

Introduce below APIs for CPUID leaves with static subleaves:

    cpuid_subleaf(_cpuinfo, _leaf, _subleaf)
    cpuid_leaf(_cpuinfo, _leaf)
    cpuid_leaf_regs(_cpuinfo, _leaf)

And below APIs for CPUID leaves with dynamic subleaves:

    cpuid_subleaf_count(_cpuinfo, _leaf)
    cpuid_subleaf_index(_cpuinfo, _leaf, _idx)
    cpuid_subleaf_index_regs(_cpuinfo, _leaf, _idx)

At <cpuid/api.h>, add a clear rationale for why call sites should use the
above APIs instead of directly invoking CPUID queries.

** Next steps

For now, define entries for CPUID(0x0) and CPUID(0x1) in the CPUID table.
Generic CPUID parser logic to fill the per-CPU CPUID tables, along with
more CPUID leaves support, will be added next.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>	# CPUID data model
Suggested-by: Andrew Cooper <andrew.cooper3@citrix.com>	# x86-cpuid-db schema
Suggested-by: Ingo Molnar <mingo@kernel.org>		# CPUID APIs restructuring
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Link: https://lore.kernel.org/lkml/874ixernra.ffs@tglx
Link: https://gitlab.com/x86-cpuid.org/x86-cpuid-db
---
 arch/x86/include/asm/cpuid/api.h   | 254 +++++++++++++++++++++++++++++
 arch/x86/include/asm/cpuid/types.h | 104 ++++++++++++
 arch/x86/include/asm/processor.h   |   2 +
 3 files changed, 360 insertions(+)

diff --git a/arch/x86/include/asm/cpuid/api.h b/arch/x86/include/asm/cpuid/api.h
index 2b9750cc8a75..0c8621d3cea0 100644
--- a/arch/x86/include/asm/cpuid/api.h
+++ b/arch/x86/include/asm/cpuid/api.h
@@ -289,4 +289,258 @@ static inline bool cpuid_amd_hygon_has_l3_cache(void)
 	return cpuid_edx(0x80000006);
 }
 
+/*
+ * 'struct cpuid_leaves' accessors:
+ *
+ * For internal-use by the CPUID parser.  These macros do not perform any
+ * sanity checks.
+ */
+
+/**
+ * __cpuid_leaves_subleaf_idx() - Get parsed CPUID output (without sanity checks)
+ * @_leaves:	&struct cpuid_leaves instance
+ * @_leaf:	CPUID leaf, in compile-time 0xN format
+ * @_subleaf:	CPUID subleaf, in compile-time decimal format
+ * @_idx:	@_leaf/@_subleaf CPUID output's storage array index.  Check
+ *		__CPUID_LEAF() for info on CPUID output storage arrays indexing.
+ *
+ * Returns the parsed CPUID output at @_leaves as a <cpuid/leaf_types.h> data
+ * type: 'struct leaf_0xN_M', where 0xN is the token provided at @_leaf, and M
+ * is token provided at @_subleaf.
+ */
+#define __cpuid_leaves_subleaf_idx(_leaves, _leaf, _subleaf, _idx)	\
+	((_leaves)->leaf_ ## _leaf ## _ ## _subleaf)[_idx]
+
+/**
+ * __cpuid_leaves_subleaf_0() - Get parsed CPUID output (without sanity checks)
+ * @_leaves:	&struct cpuid_leaves instance
+ * @_leaf:	CPUID leaf, in compile-time 0xN format
+ *
+ * Like __cpuid_leaves_subleaf_idx(), but with subleaf = 0 and index = 0.
+ */
+#define __cpuid_leaves_subleaf_0(_leaves, _leaf)			\
+	__cpuid_leaves_subleaf_idx(_leaves, _leaf, 0, 0)
+
+/**
+ * __cpuid_leaves_subleaf_info() - Get CPUID query info for @_leaf/@_subleaf
+ * @_leaves:	&struct cpuid_leaves instance
+ * @_leaf:	CPUID leaf, in compile-time 0xN format
+ * @_subleaf:	CPUID subleaf, in compile-time decimal format
+ *
+ * Returns a pointer to the &struct leaf_query_info instance associated with
+ * the given @_leaf/@_subleaf pair at the CPUID @_leaves data repository. See
+ * __CPUID_LEAF().
+ */
+#define __cpuid_leaves_subleaf_info(_leaves, _leaf, _subleaf)		\
+	((_leaves)->leaf_ ## _leaf ## _ ## _subleaf ## _ ## info)
+
+/*
+ * 'struct cpuid_table' accessors:
+ *
+ * For internal-use by the CPUID parser.  These macros perform the necessary
+ * sanity checks by default.
+ */
+
+/**
+ * __cpuid_table_subleaf_idx() - Get parsed CPUID output (with sanity checks)
+ * @_table:	&struct cpuid_table instance
+ * @_leaf:	CPUID leaf, in compile-time 0xN format
+ * @_subleaf:	CPUID subleaf, in compile-time decimal format
+ * @_idx:	@_leaf/@_subleaf CPUID query output's storage array index.
+ *		See __CPUID_LEAF().
+ *
+ * Return a pointer to the requested parsed CPUID output at @_table, as a
+ * <cpuid/leaf_types.h> data type: 'struct leaf_0xN_M', where 0xN is the token
+ * provided at @_leaf, and M is the token provided at @_subleaf; e.g. 'struct
+ * leaf_0x7_0'.
+ *
+ * Returns NULL if the requested CPUID @_leaf/@_subleaf/@_idx query output is
+ * not present at @_table.
+ */
+#define __cpuid_table_subleaf_idx(_table, _leaf, _subleaf, _idx)	\
+	(((_idx) >= __cpuid_leaves_subleaf_info(&((_table)->leaves), _leaf, _subleaf).nr_entries) ? \
+	 NULL : &__cpuid_leaves_subleaf_idx(&((_table)->leaves), _leaf, _subleaf, _idx))
+
+/**
+ * __cpuid_table_subleaf() - Get parsed CPUID output (with sanity checks)
+ * @_table:	&struct cpuid_table instance
+ * @_leaf:	CPUID leaf, in compile-time 0xN format
+ * @_subleaf:	CPUID subleaf, in compile-time decimal format
+ *
+ * Like __cpuid_table_subleaf_idx(), but with CPUID output storage index = 0.
+ */
+#define __cpuid_table_subleaf(_table, _leaf, _subleaf)			\
+	__cpuid_table_subleaf_idx(_table, _leaf, _subleaf, 0)
+
+/*
+ * External APIs for accessing parsed CPUID data:
+ *
+ * Call sites should use below APIs instead of invoking direct CPUID queries.
+ *
+ * Benefits include:
+ *
+ * - Return CPUID output as typed C structures that are auto-generated from a
+ *   centralized database (see <cpuid/leaf_types.h).  Such data types have a
+ *   full C99 bitfield layout per CPUID leaf/subleaf combination.  Call sites
+ *   can thus avoid doing ugly and cryptic bitwise operations on raw CPUID data.
+ *
+ * - Return cached, per-CPU, CPUID output.  Below APIs do not invoke any CPUID
+ *   queries, thus avoiding their side effects like serialization and VM exits.
+ *   Call-site-specific hard coded constants and macros for caching CPUID query
+ *   outputs can also be avoided.
+ *
+ * - Return sanitized CPUID data.  Below APIs return NULL if the given CPUID
+ *   leaf/subleaf input is not supported by hardware, or if the hardware CPUID
+ *   output was deemed invalid by the CPUID parser.  This centralizes all CPUID
+ *   data sanitization in one place (the kernel's CPUID parser.)
+ *
+ * - A centralized global view of system CPUID data.  Below APIs will reflect
+ *   any kernel-enforced feature masking or overrides, unlike ad hoc parsing of
+ *   raw CPUID output by drivers and individual call sites.
+ */
+
+/**
+ * cpuid_subleaf() - Access parsed CPUID data
+ * @_cpuinfo:	CPU capability structure reference ('struct cpuinfo_x86')
+ * @_leaf:	CPUID leaf, in compile-time 0xN format; e.g. 0x7, 0xf
+ * @_subleaf:	CPUID subleaf, in compile-time decimal format; e.g. 0, 1, 3
+ *
+ * Returns a pointer to parsed CPUID output, from the CPUID table inside
+ * @_cpuinfo, as a <cpuid/leaf_types.h> data type: 'struct leaf_0xN_M', where
+ * 0xN is the token provided at @_leaf, and M is the token provided at
+ * @_subleaf; e.g. struct leaf_0x7_0.
+ *
+ * Returns NULL if the requested CPUID @_leaf/@_subleaf query output is not
+ * present at the parsed CPUID table inside @_cpuinfo.  This can happen if:
+ *
+ * - The CPUID table inside @_cpuinfo has not yet been populated.
+ * - The CPUID table inside @_cpuinfo was populated, but the CPU does not
+ *   implement the requested CPUID @_leaf/@_subleaf combination.
+ * - The CPUID table inside @_cpuinfo was populated, but the kernel's CPUID
+ *   parser has predetermined that the requested CPUID @_leaf/@_subleaf
+ *   hardware output is invalid or unsupported.
+ *
+ * Example usage::
+ *
+ *	const struct leaf_0x7_0 *l7_0 = cpuid_subleaf(c, 0x7, 0);
+ *	if (!l7_0) {
+ *		// Handle error
+ *	}
+ *
+ *	const struct leaf_0x7_1 *l7_1 = cpuid_subleaf(c, 0x7, 1);
+ *	if (!l7_1) {
+ *		// Handle error
+ *	}
+ */
+#define cpuid_subleaf(_cpuinfo, _leaf, _subleaf)			\
+	__cpuid_table_subleaf(&(_cpuinfo)->cpuid, _leaf, _subleaf)
+
+/**
+ * cpuid_leaf() - Access parsed CPUID data
+ * @_cpuinfo:	CPU capability structure reference ('struct cpuinfo_x86')
+ * @_leaf:	CPUID leaf, in compile-time 0xN format; e.g. 0x0, 0x2, 0x80000000
+ *
+ * Similar to cpuid_subleaf(), but with a CPUID subleaf = 0.
+ *
+ * Example usage::
+ *
+ *	const struct leaf_0x0_0 *l0 = cpuid_leaf(c, 0x0);
+ *	if (!l0) {
+ *		// Handle error
+ *	}
+ *
+ *	const struct leaf_0x80000000_0 *el0 = cpuid_leaf(c, 0x80000000);
+ *	if (!el0) {
+ *		// Handle error
+ *	}
+ */
+#define cpuid_leaf(_cpuinfo, _leaf)					\
+	cpuid_subleaf(_cpuinfo, _leaf, 0)
+
+/**
+ * cpuid_leaf_regs() - Access parsed CPUID data in raw format
+ * @_cpuinfo:	CPU capability structure reference ('struct cpuinfo_x86')
+ * @_leaf:	CPUID leaf, in compile-time 0xN format
+ *
+ * Similar to cpuid_leaf(), but returns a raw 'struct cpuid_regs' pointer to
+ * the parsed CPUID data instead of a "typed" <cpuid/leaf_types.h> pointer.
+ */
+#define cpuid_leaf_regs(_cpuinfo, _leaf)				\
+	((struct cpuid_regs *)(cpuid_leaf(_cpuinfo, _leaf)))
+
+#define __cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf)	\
+	static_assert(ARRAY_SIZE((_cpuinfo)->cpuid.leaves.leaf_ ## _leaf ## _0) > 1);
+
+/**
+ * cpuid_subleaf_index() - Access parsed CPUID data at runtime subleaf index
+ * @_cpuinfo:	CPU capability structure reference ('struct cpuinfo_x86')
+ * @_leaf:	CPUID leaf, in compile-time 0xN format; e.g. 0x4, 0x8000001d
+ * @_idx:	Index within CPUID(@_leaf) output storage array.  It must be
+ *		smaller than "cpuid_subleaf_count(@_cpuinfo, @_leaf)".  Unlike
+ *		@_leaf, this value can be provided dynamically.
+ *
+ * For a given leaf/subleaf combination, the CPUID table inside @_cpuinfo
+ * contains an array of CPUID output storage entries.  An array of storage
+ * entries is used to accommodate CPUID leaves which produce the same output
+ * format for a large subleaf range.  This is common for CPUID hierarchical
+ * objects enumeration; e.g., CPUID(0x4) and CPUID(0xd).  Check CPUID_LEAF().
+ *
+ * CPUID leaves that are to be accessed using this macro are specified at
+ * <cpuid/types.h>, 'struct cpuid_leaves', with a CPUID_LEAF() count field
+ * bigger than 1.  A build-time error will be generated otherwise.
+ *
+ * Example usage::
+ *
+ *	const struct leaf_0x4_0 *l4;
+ *
+ *	for (int i = 0; i < cpuid_subleaf_count(c, 0x4); i++) {
+ *		l4 = cpuid_subleaf_index(c, 0x4, i);
+ *		if (!l4) {
+ *			// Handle error
+ *		}
+ *
+ *		// Access CPUID(0x4, i) data; e.g. l4->cache_type
+ *	}
+ *
+ * Beside the standard error situations detailed at cpuid_subleaf(), this
+ * macro will return NULL if @_idx is out of range.
+ */
+#define cpuid_subleaf_index(_cpuinfo, _leaf, _idx)			\
+({									\
+	__cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf);	\
+	__cpuid_table_subleaf_idx(&(_cpuinfo)->cpuid, _leaf, 0, _idx);	\
+})
+
+/**
+ * cpuid_subleaf_index_regs() - Access parsed CPUID data at runtime subleaf index
+ * @_cpuinfo:	CPU capability structure reference ('struct cpuinfo_x86')
+ * @_leaf:	CPUID leaf, in compile-time 0xN format; e.g. 0x4, 0x8000001d
+ * @_idx:	Index within CPUID(@_leaf) output storage array.  It must be
+ *		smaller than "cpuid_subleaf_count(@_cpuinfo, @_leaf)".
+ *
+ * Similar to cpuid_subleaf_index(), but returns a raw 'struct cpuid_regs'
+ * pointer to the parsed CPUID data, instead of a "typed" <cpuid/leaf_types.h>
+ * pointer.
+ */
+#define cpuid_subleaf_index_regs(_cpuinfo, _leaf, _idx)			\
+	((struct cpuid_regs *)cpuid_subleaf_index(_cpuinfo, _leaf, _idx))
+
+/**
+ * cpuid_subleaf_count() - Number of valid (filled) subleaves for @_leaf
+ * @_cpuinfo:	CPU capability structure reference ('struct cpuinfo_x86')
+ * @_leaf:	CPUID leaf, in compile-time 0xN format; e.g. 0x4, 0x8000001d
+ *
+ * Return the number of subleaves filled by the CPUID parser for @_leaf. Check
+ * cpuid_subleaf_index().
+ *
+ * CPUID leaves that are to be accessed using this macro are specified at
+ * <cpuid/types.h>, 'struct cpuid_leaves', with a CPUID_LEAF() count field
+ * bigger than 1.  A build-time error will be generated otherwise.
+ */
+#define cpuid_subleaf_count(_cpuinfo, _leaf)				\
+({									\
+	__cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf);	\
+	__cpuid_leaves_subleaf_info(&(_cpuinfo)->cpuid.leaves, _leaf, 0).nr_entries; \
+})
+
 #endif /* _ASM_X86_CPUID_API_H */
diff --git a/arch/x86/include/asm/cpuid/types.h b/arch/x86/include/asm/cpuid/types.h
index 8a00364b79de..f1b51ba21ca4 100644
--- a/arch/x86/include/asm/cpuid/types.h
+++ b/arch/x86/include/asm/cpuid/types.h
@@ -5,6 +5,8 @@
 #include <linux/build_bug.h>
 #include <linux/types.h>
 
+#include <asm/cpuid/leaf_types.h>
+
 /*
  * Types for raw CPUID access:
  */
@@ -124,4 +126,106 @@ extern const struct leaf_0x2_table cpuid_0x2_table[256];
  */
 #define TLB_0x63_2M_4M_ENTRIES		32
 
+/*
+ * Types for centralized CPUID tables:
+ *
+ * For internal use by the CPUID parser.
+ */
+
+/**
+ * struct leaf_query_info - Parse info for a CPUID leaf/subleaf query
+ * @nr_entries:	Number of valid output storage entries filled by the CPUID parser
+ *
+ * In a CPUID table (struct cpuid_leaves), each CPUID leaf/subleaf query output
+ * storage entry from <cpuid/leaf_types.h> is paired with a unique instance of
+ * this type.
+ */
+struct leaf_query_info {
+	unsigned int		nr_entries;
+};
+
+/**
+ * __CPUID_LEAF() - Define CPUID output storage and query info entry
+ * @_name:	Struct type name of the CPUID leaf/subleaf (e.g. 'leaf_0x4_0').
+ *		Such types are defined at <cpuid/leaf_types.h>, and follow the
+ *		format 'struct leaf_0xN_M', where 0xN is the leaf and M is the
+ *		subleaf.
+ * @_count:	Number of storage entries to allocate for this leaf/subleaf
+ *
+ * For the given leaf/subleaf combination, define an array of CPUID output
+ * storage entries and an associated query info structure — both residing in a
+ * 'struct cpuid_leaves' instance.
+ *
+ * Use an array of storage entries to accommodate CPUID leaves which produce
+ * the same output format for a large subleaf range.  This is common for
+ * hierarchical objects enumeration; e.g., CPUID(0x4), CPUID(0xd), and
+ * CPUID(0x12).
+ *
+ * The example invocation for CPUID(0x7) storage, subleaves 0->1:
+ *
+ *	__CPUID_LEAF(leaf_0x7_0, 1);
+ *	__CPUID_LEAF(leaf_0x7_1, 1);
+ *
+ * generates 'struct cpuid_leaves' storage entries in the form::
+ *
+ *	struct leaf_0x7_0		leaf_0x7_0[1];
+ *	struct leaf_query_info		leaf_0x7_0_info;
+ *
+ *	struct leaf_0x7_1		leaf_0x7_1[1];
+ *	struct leaf_query_info		leaf_0x7_1_info;
+ *
+ * The example invocation for CPUID(0x4) storage::
+ *
+ *	__CPUID_LEAF(leaf_0x4_0, 8);
+ *
+ * generates storage entries in the form:
+ *
+ *	struct leaf_0x4_0		leaf_0x4_0[8];
+ *	struct leaf_query_info		leaf_0x4_0_info;
+ *
+ * where the 'leaf_0x4_0[8]' storage array can accommodate the output of
+ * CPUID(0x4) subleaves 0->7, since they all have the same output format.
+ */
+#define __CPUID_LEAF(_name, _count)				\
+	struct _name		_name[_count];			\
+	struct leaf_query_info	_name##_info
+
+/**
+ * CPUID_LEAF() - Define a CPUID storage entry in 'struct cpuid_leaves'
+ * @_leaf:	CPUID Leaf number in the 0xN format; e.g., 0x4.
+ * @_subleaf:	Subleaf number in decimal
+ * @_count:	Number of repeated storage entries for this @_leaf/@_subleaf
+ *
+ * Convenience wrapper around __CPUID_LEAF().
+ */
+#define CPUID_LEAF(_leaf, _subleaf, _count)			\
+	__CPUID_LEAF(leaf_ ## _leaf ## _ ## _subleaf, _count)
+
+/*
+ * struct cpuid_leaves - Structured CPUID data repository
+ */
+struct cpuid_leaves {
+	/*         leaf		subleaf		count */
+	CPUID_LEAF(0x0,		0,		1);
+	CPUID_LEAF(0x1,		0,		1);
+};
+
+/*
+ * Types for centralized CPUID tables:
+ *
+ * For external use.
+ */
+
+/**
+ * struct cpuid_table - Per-CPU CPUID data repository
+ * @leaves:	CPUID leaf/subleaf queries' output and metadata
+ *
+ * Embedded inside 'struct cpuinfo_x86' to provide access to stored, parsed,
+ * and sanitized CPUID output per-CPU.  Thus removing the need for any direct
+ * CPUID query by call sites.
+ */
+struct cpuid_table {
+	struct cpuid_leaves	leaves;
+};
+
 #endif /* _ASM_X86_CPUID_TYPES_H */
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 910e36b0c00d..88f8ee33bfca 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -16,6 +16,7 @@ struct vm86;
 #include <uapi/asm/sigcontext.h>
 #include <asm/current.h>
 #include <asm/cpufeatures.h>
+#include <asm/cpuid/types.h>
 #include <asm/page.h>
 #include <asm/pgtable_types.h>
 #include <asm/percpu.h>
@@ -164,6 +165,7 @@ struct cpuinfo_x86 {
 	char			x86_vendor_id[16];
 	char			x86_model_id[64];
 	struct cpuinfo_topology	topo;
+	struct cpuid_table	cpuid;
 	/* in KB - valid for CPUS which support this call: */
 	unsigned int		x86_cache_size;
 	int			x86_cache_alignment;	/* In bytes */
-- 
2.50.1

Re: [PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model
Posted by Sean Christopherson 1 month, 2 weeks ago
On Fri, Aug 15, 2025, Ahmed S. Darwish wrote:
> + * For a given leaf/subleaf combination, the CPUID table inside @_cpuinfo
> + * contains an array of CPUID output storage entries.  An array of storage
> + * entries is used to accommodate CPUID leaves which produce the same output
> + * format for a large subleaf range.  This is common for CPUID hierarchical
> + * objects enumeration; e.g., CPUID(0x4) and CPUID(0xd).  Check CPUID_LEAF().

IMO, the end result is quite misleading for leaves with "repeated" entries.  0xd
in particular will be bizarre due to its array starting at ECX=2.  E.g.
cpuid_subleaf_index() straight up won't work (without lying) due to hardcoding
'0' as the subleaf.

#define cpuid_subleaf_index(_cpuinfo, _leaf, _idx)			\
({									\
	__cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf);	\
	__cpuid_table_subleaf_idx(&(_cpuinfo)->cpuid, _leaf, 0, _idx);	\
                                                             ^
                                                             |

And then the usage would be similarly bizarre, e.g.

	for (i = XFEATURE_YMM; i < ARRAY_SIZE(xstate_sizes); i++) {
		struct cpuid_xstate_sizes *xs = &xstate_sizes[i];
		struct cpuid_0xd_2 *c = cpuid_subleaf_index(..., 0xD, i - 2);

		...
	}

Even the cases where the array starts at '0' look weird:

	const struct leaf_0x4_0 *regs = cpuid_subleaf_index(c, 0x4, index);

because the code is obviously not grabbing CPUID(0x4).0.

And the subleaf_index naming is also weird, because they're essentially the same
thing, e.g. the SDM refers to "sub-leaf index" for more than just the repeated
cases.

Rather than define the structures names using an explicit starting subleaf, what
if the structures and APIs explicitly reference 'n' as the subleaf?  That would
communicate that the struct represents a repeated subleaf, explicitly tie the API
to that structure, and would provide macro/function names that don't make the
reader tease out the subtle usage of "index".

And then instead of just the array size, capture the start:end of the repeated
subleaf so that the caller doesn't need to manually do the math.

E.g.

	const struct leaf_0x4_n *regs = cpuid_subleaf_n(c, 0x4, index);

	struct cpuid_0xd_n *c = cpuid_subleaf_n(..., 0xD, i);
and

Tangentially related, having to manually specific count=1 to CPUID_LEAF() for
the common case is also ugly.  If a dedicated CPUID_LEAF_N() macro is added to
specificy the start:end of the range, then CPUID_LEAF() can just hardcode the
count to '1'.  E.g.

struct cpuid_leaves {
	CPUID_LEAF(0x0,		 	0);
	CPUID_LEAF(0x1,			0);
	CPUID_LEAF(0x2,		 	0);
	CPUID_LEAF_N(0x4,		0,	7);
	CPUID_LEAF(0xd,			0);
	CPUID_LEAF(0xd,			1);
	CPUID_LEAF_N(0xd,		2,	63);
	CPUID_LEAF(0x16,	 	0);
	CPUID_LEAF(0x80000000,		0);
	CPUID_LEAF(0x80000005,		0);
	CPUID_LEAF(0x80000006,		0);
	CPUID_LEAF_N(0x8000001d,	0,	7);
};
Re: [PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model
Posted by Ahmed S. Darwish 1 month, 2 weeks ago
Hi!

On Fri, 15 Aug 2025, Sean Christopherson wrote:
>
> IMO, the end result is quite misleading for leaves with "repeated" entries.  0xd
> in particular will be bizarre due to its array starting at ECX=2.  E.g.
> cpuid_subleaf_index() straight up won't work (without lying) due to hardcoding
> '0' as the subleaf.
>
> #define cpuid_subleaf_index(_cpuinfo, _leaf, _idx)			\
> ({									\
> 	__cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf);	\
> 	__cpuid_table_subleaf_idx(&(_cpuinfo)->cpuid, _leaf, 0, _idx);	\
>                                                              ^
>                                                              |
>
> And then the usage would be similarly bizarre, e.g.
>
> 	for (i = XFEATURE_YMM; i < ARRAY_SIZE(xstate_sizes); i++) {
> 		struct cpuid_xstate_sizes *xs = &xstate_sizes[i];
> 		struct cpuid_0xd_2 *c = cpuid_subleaf_index(..., 0xD, i - 2);
>
> 		...
> 	}
>
> Even the cases where the array starts at '0' look weird:
>
> 	const struct leaf_0x4_0 *regs = cpuid_subleaf_index(c, 0x4, index);
>
> because the code is obviously not grabbing CPUID(0x4).0.
>
> And the subleaf_index naming is also weird, because they're essentially the same
> thing, e.g. the SDM refers to "sub-leaf index" for more than just the repeated
> cases.
>
> Rather than define the structures names using an explicit starting subleaf, what
> if the structures and APIs explicitly reference 'n' as the subleaf?  That would
> communicate that the struct represents a repeated subleaf, explicitly tie the API
> to that structure, and would provide macro/function names that don't make the
> reader tease out the subtle usage of "index".
>
> And then instead of just the array size, capture the start:end of the repeated
> subleaf so that the caller doesn't need to manually do the math.
>
> E.g.
>
> 	const struct leaf_0x4_n *regs = cpuid_subleaf_n(c, 0x4, index);
>
> 	struct cpuid_0xd_n *c = cpuid_subleaf_n(..., 0xD, i);
>

Thanks a lot for all these remarks.  I was indeed struggling with good
names for the array indexing case, so the above was really helpful.

In this v4 iteration, I did not add a CPUID parser API for the cases
where the subleaves are repeated but do not start from zero.  Not for any
technical reason, but to avoid having kernel APIs with no call sites.

What I internally have is:

  /**
   * __cpuid_subleaf_index() - Access parsed CPUID data at runtime subleaf index
   * @_cpuinfo:	CPU capability structure reference ('struct cpuinfo_x86')
   * @_leaf:	CPUID leaf in compile-time 0xN format; e.g. 0x4, 0x8000001d
   * @_subleaf:	CPUID subleaf in compile-time decimal format; e.g. 0, 1, 3
   * @_idx:	Index within CPUID(@_leaf).@_subleaf output storage array.
   *		Unlike @_leaf and @_subleaf, this index value can be provided
   *		dynamically.
   * ...
   */
  #define __cpuid_subleaf_index(_cpuinfo, _leaf, _subleaf, @_idx)	\
  ...

Thus, the arch/x86/kvm/cpuid.c loop you quoted would be tokenized as:

    const struct leaf_0xd_2 *ld;
    for (int i = 0; i < ARRAY_SIZE(xstate_sizes) - XFEATURE_YMM; i++) {
        ...
        ld = __cpuid_subleaf_index(c, 0xd, 2, i);
	                           |   |   |  └───────┐
                                   |   |   └────────┐ |
                                   |   └─────────┐  | |
                                   *             *  * v
        ld =                      &c.cpuid.leaf_0xd_2[i];
        ...
    }

I actually agree that, in this case, types like "struct leaf_0xd_2" are
deceiving... Let's divide the problem in two.

Easy case: Subleaves start repeating from subleaf = 0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This would be the CPUID leaves:

    x86-cpuid-db/db/xml (tip)> git grep 'id="0" array='
    leaf_04.xml:        <subleaf id="0" array="32">
    leaf_0b.xml:        <subleaf id="0" array="2">
    leaf_18.xml:        <subleaf id="0" array="32">
    leaf_1b.xml:        <subleaf id="0" array="32">
    leaf_1f.xml:        <subleaf id="0" array="6">
    leaf_8000001d.xml:	<subleaf id="0" array="32">
    leaf_80000026.xml:	<subleaf id="0" array="4">

For example, patch 24/34 ("x86/cacheinfo: Use parsed CPUID(0x4)"):

    static int
    intel_fill_cpuid4_info(struct cpuinfo_x86 *c, int index, ...)
    {
	const struct leaf_0x4_0 *regs = cpuid_subleaf_index(c, 0x4, index);
	...
    }

In that case, we might actually generate leaf data types in the form:

    struct leaf_0x4_n { ... };

And have the access macros called cpuid_subleaf_n():

    static int
    intel_fill_cpuid4_info(struct cpuinfo_x86 *c, int index, ...) {
    {
	const struct leaf_0x4_n *regs = cpuid_subleaf_n(c, 0x4, index);
	...
    }

I think this indeed looks much much better.

I will do that for v5 of the model.

Hard case: Subleaves start repeating from subleaf > 0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This would be the CPUID leaves:

    x86-cpuid-db/db/xml (tip)> git grep 'id="[1-9][0-9]*" array='

    leaf_0d.xml:    <subleaf id="2" array="62">
    leaf_10.xml:    <subleaf id="1" array="2">
    leaf_12.xml:    <subleaf id="2" array="30">
    leaf_17.xml:    <subleaf id="1" array="3">

For something like CPUID(0xd), I cannot just blindly define a 'struct
cpuid_0xd_n' data type.  We already have:

    struct leaf_0xd_0 { ... };
    struct leaf_0xd_1 { ... };
    struct leaf_0xd_2 { ... };

and they all have different bitfields.  A similar case exist for
CPUID(0x10), CPUID(0x12), and CPUID(0x17).

But, we can still have:

    struct leaf_0xd_0	{ ... };
    struct leaf_0xd_1	{ ... };
    struct leaf_0xd_2_n	{ ... };

With this, the CPUID(0x12) call site at kernel/cpu/sgx/main.c can change
from:

    u32 eax, ebx, ecx, edx;

    for (i = 0; i < ARRAY_SIZE(sgx_epc_sections); i++) {
        ...
	cpuid_count(SGX_CPUID, i + SGX_CPUID_EPC, &eax, ...);
    }

to:

    const struct leaf_0x12_2_n *l;

    for (i = 0; i < ARRAY_SIZE(sgx_epc_sections); i++) {
        ...
	l = __cpuid_subleaf_n(0x12, 2, i);
    }

And the aforementioned KVM snippet would be:

    const struct leaf_0xd_2_n *l;

    for (int i = 0; i < ARRAY_SIZE(xstate_sizes) - XFEATURE_YMM; i++) {
        l = __cpuid_subleaf_n(c, 0xd, 2, i);
    }

I'm open for better names than __cpuid_subleaf_n() for this.  The only
constraint with any suggested name is that I need to have 0xd and 2 both
passed as CPP literals.

API Summary
~~~~~~~~~~~

For CPUID leaves with static subleaves:

    cpuid_leaf(_cpuinfo, _leaf)
    cpuid_subleaf(_cpuinfo, _leaf, _subleaf)

For CPUID leaves with dynamic subleaves:

    cpuid_subleaf_n(_cpuinfo, _leaf, _idx)
    __cpuid_subleaf_n(_cpuinfo, _leaf, _subleaf, _idx)

Sounds good?

>
> Tangentially related, having to manually specific count=1 to CPUID_LEAF() for
> the common case is also ugly.  If a dedicated CPUID_LEAF_N() macro is added to
> specificy the start:end of the range, then CPUID_LEAF() can just hardcode the
> count to '1'.  E.g.
>
> struct cpuid_leaves {
> 	CPUID_LEAF(0x0,		 	0);
> 	CPUID_LEAF(0x1,			0);
> 	CPUID_LEAF(0x2,		 	0);
> 	CPUID_LEAF_N(0x4,		0,	7);
> 	CPUID_LEAF(0xd,			0);
> 	CPUID_LEAF(0xd,			1);
> 	CPUID_LEAF_N(0xd,		2,	63);
> 	CPUID_LEAF(0x16,	 	0);
> 	CPUID_LEAF(0x80000000,		0);
> 	CPUID_LEAF(0x80000005,		0);
> 	CPUID_LEAF(0x80000006,		0);
> 	CPUID_LEAF_N(0x8000001d,	0,	7);
> };
>

This is much better; will do.

Thanks!

--
Ahmed S. Darwish
Linutronix GmbH
Re: [PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model
Posted by Sean Christopherson 1 month, 2 weeks ago
On Mon, Aug 18, 2025, Ahmed S. Darwish wrote:
> > Rather than define the structures names using an explicit starting subleaf, what
> > if the structures and APIs explicitly reference 'n' as the subleaf?  That would
> > communicate that the struct represents a repeated subleaf, explicitly tie the API
> > to that structure, and would provide macro/function names that don't make the
> > reader tease out the subtle usage of "index".
> >
> > And then instead of just the array size, capture the start:end of the repeated
> > subleaf so that the caller doesn't need to manually do the math.
> >
> > E.g.
> >
> > 	const struct leaf_0x4_n *regs = cpuid_subleaf_n(c, 0x4, index);
> >
> > 	struct cpuid_0xd_n *c = cpuid_subleaf_n(..., 0xD, i);
> Hard case: Subleaves start repeating from subleaf > 0
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> This would be the CPUID leaves:
> 
>     x86-cpuid-db/db/xml (tip)> git grep 'id="[1-9][0-9]*" array='
> 
>     leaf_0d.xml:    <subleaf id="2" array="62">
>     leaf_10.xml:    <subleaf id="1" array="2">
>     leaf_12.xml:    <subleaf id="2" array="30">
>     leaf_17.xml:    <subleaf id="1" array="3">
> 
> For something like CPUID(0xd), I cannot just blindly define a 'struct
> cpuid_0xd_n' data type.

Why not?  Like C structs, there can only be one variable sized array, i.e. there
can't be multiple "n" subleafs.  If the concern is calling __cpuid_subleaf_n()
with i < start, then I don't see how embedding start in the structure name helps
in any way, since 'i' isn't a compile-time constant and so needs to be checked at
runtime no matter what.

> We already have:
> 
>     struct leaf_0xd_0 { ... };
>     struct leaf_0xd_1 { ... };
>     struct leaf_0xd_2 { ... };
> 
> and they all have different bitfields.  A similar case exist for
> CPUID(0x10), CPUID(0x12), and CPUID(0x17).
> 
> But, we can still have:
> 
>     struct leaf_0xd_0	{ ... };
>     struct leaf_0xd_1	{ ... };
>     struct leaf_0xd_2_n	{ ... };
> 

...

> And the aforementioned KVM snippet would be:
> 
>     const struct leaf_0xd_2_n *l;
> 
>     for (int i = 0; i < ARRAY_SIZE(xstate_sizes) - XFEATURE_YMM; i++) {
>         l = __cpuid_subleaf_n(c, 0xd, 2, i);

IMO, this is still ugly and confusing.
Re: [PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model
Posted by Ahmed S. Darwish 1 month, 2 weeks ago
Hi,

On Mon, 18 Aug 2025, Sean Christopherson wrote:
>
> Why not?  Like C structs, there can only be one variable sized array, i.e. there
> can't be multiple "n" subleafs.  If the concern is calling __cpuid_subleaf_n()
> with i < start, then I don't see how embedding start in the structure name helps
> in any way, since 'i' isn't a compile-time constant and so needs to be checked at
> runtime no matter what.
>

Hmmm...

I can indeed find the offset of the dynamic 'leaf_0x.._n' subleaf storage
by CPP tokenization, since the CPUID table will be in the form:

    struct leaf_0x0_0           leaf_0x0_0[1];
    struct leaf_0x1_0           leaf_0x1_0[1];
    struct leaf_0x2_0           leaf_0x2_0[1];
    struct leaf_0x4_0           leaf_0x4_0[8];
    struct leaf_0xd_0		leaf_0xd_0[1];
    struct leaf_0xd_1		leaf_0xd_1[1];
    struct leaf_0xd_n		leaf_0xd_n[62];
    struct leaf_0x16_0          leaf_0x16_0[1];
    struct leaf_0x80000000_0    leaf_0x80000000_0[1];
    struct leaf_0x80000005_0    leaf_0x80000005_0[1];
    struct leaf_0x80000006_0    leaf_0x80000006_0[1];
    struct leaf_0x8000001d_0    leaf_0x8000001d_0[8];

I was also kinda worried about the different subleaf semantics:

    struct leaf_0x4_n		=> starts from subleaf 0
    struct leaf_0xd_n		=> starts from subleaf 2

But, hopefully it would be clear when looking at the generated header in
total.

Still: for the API you're proposing, I'll need to generate an error for
things like:

    cpuid_subleaf_n(c, 0xd, 0);
    cpuid_subleaf_n(c, 0xd, 1);

which could not have happened in the API I proposed earlier.  But... I
can let the XML files generate some symbols in the form:

    LEAF_0x4_n_MIN_SUBLEAF	0
    LEAF_0xd_n_MIN_SUBLEAF	2

and generate an error (once) if the passed subleaf is less than the
minimum.  I can also generate that error (once) at compile-time if the
given subleaf was a compile-time constant.

Maybe there's a cleaner way for detecting this subleaf lower-bound error,
I will see; what's important now is the API implementation feasibility...

=> Then, we can have:

API Summary
~~~~~~~~~~~

For CPUID leaves with static subleaves:

    cpuid_leaf(_cpuinfo, _leaf)
    cpuid_subleaf(_cpuinfo, _leaf, _subleaf)

For CPUID leaves with dynamic subleaves:

    cpuid_subleaf_n(_cpuinfo, _leaf, _idx)

New SGX snippet
~~~~~~~~~~~~~~~

At arch/x86/kernel/cpu/sgx/main.c:

    const struct leaf_0x12_n *l;

    for (int i = 0; i < ARRAY_SIZE(sgx_epc_sections); i++) {

        l = cpuid_subleaf_n(0x12, i + SGX_CPUID_EPC);

	// Use l->subleaf_type, etc. as appropriate
    }

New KVM snippet
~~~~~~~~~~~~~~~

At arch/x86/kvm/cpuid.c:

    const struct leaf_0xd_n *l;

    for (int i = XFEATURE_YMM; i < ARRAY_SIZE(xstate_sizes); i++) {

	l = cpuid_subleaf_n(c, 0xd, i);

        // Use l->xsave_sz, l->xsave_offset, etc. as appropriate
    }

This looks much better indeed; for both KVM and SGX :-)

Thanks a lot!

--
Ahmed S. Darwish
Linutronix GmbH
Re: [PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model
Posted by Sean Christopherson 1 month, 2 weeks ago
On Mon, Aug 18, 2025, Ahmed S. Darwish wrote:
> Hi,
> 
> On Mon, 18 Aug 2025, Sean Christopherson wrote:
> >
> > Why not?  Like C structs, there can only be one variable sized array, i.e. there
> > can't be multiple "n" subleafs.  If the concern is calling __cpuid_subleaf_n()
> > with i < start, then I don't see how embedding start in the structure name helps
> > in any way, since 'i' isn't a compile-time constant and so needs to be checked at
> > runtime no matter what.
> >
> 
> Hmmm...
> 
> I can indeed find the offset of the dynamic 'leaf_0x.._n' subleaf storage
> by CPP tokenization, since the CPUID table will be in the form:
> 
>     struct leaf_0x0_0           leaf_0x0_0[1];
>     struct leaf_0x1_0           leaf_0x1_0[1];
>     struct leaf_0x2_0           leaf_0x2_0[1];
>     struct leaf_0x4_0           leaf_0x4_0[8];
>     struct leaf_0xd_0		leaf_0xd_0[1];
>     struct leaf_0xd_1		leaf_0xd_1[1];
>     struct leaf_0xd_n		leaf_0xd_n[62];
>     struct leaf_0x16_0          leaf_0x16_0[1];
>     struct leaf_0x80000000_0    leaf_0x80000000_0[1];
>     struct leaf_0x80000005_0    leaf_0x80000005_0[1];
>     struct leaf_0x80000006_0    leaf_0x80000006_0[1];
>     struct leaf_0x8000001d_0    leaf_0x8000001d_0[8];
> 
> I was also kinda worried about the different subleaf semantics:
> 
>     struct leaf_0x4_n		=> starts from subleaf 0
>     struct leaf_0xd_n		=> starts from subleaf 2
> 
> But, hopefully it would be clear when looking at the generated header in
> total.
> 
> Still: for the API you're proposing, I'll need to generate an error for
> things like:
> 
>     cpuid_subleaf_n(c, 0xd, 0);
>     cpuid_subleaf_n(c, 0xd, 1);
> 
> which could not have happened in the API I proposed earlier.  But... I
> can let the XML files generate some symbols in the form:
> 
>     LEAF_0x4_n_MIN_SUBLEAF	0
>     LEAF_0xd_n_MIN_SUBLEAF	2
> 
> and generate an error (once) if the passed subleaf is less than the
> minimum.  I can also generate that error (once) at compile-time if the
> given subleaf was a compile-time constant.

Eh, if the kernel can flag "cpuid_subleaf_n(c, 0xd, 1)" at compile time, then yay.
But I don't think it's worth going too far out of the way to detect, and it's most
definitely not worth bleeding the lower bound into the APIs.

E.g. it's not really any different than doing:

   cpuid_subleaf_n(c, 0xd, 2, 64);

and AFAIT the original code wouldn't flag that at compile time.

> Maybe there's a cleaner way for detecting this subleaf lower-bound error,

Not sure "cleaner" is the right word, but if you really want to add compile-time
sanity checks, you could put the actual definitions in a dedicated header file
that's included multiple times without any #ifdef guards.  Once to define
"struct cpuid_leaves", and a second time to define global metadata for each leaf,
e.g. the first/last subleaf in a dynamic range.

---
 arch/x86/include/asm/cpuid/api.h       | 11 ++++---
 arch/x86/include/asm/cpuid/leaf_defs.h | 12 +++++++
 arch/x86/include/asm/cpuid/types.h     | 43 +++++++++++++++-----------
 3 files changed, 44 insertions(+), 22 deletions(-)
 create mode 100644 arch/x86/include/asm/cpuid/leaf_defs.h

diff --git a/arch/x86/include/asm/cpuid/api.h b/arch/x86/include/asm/cpuid/api.h
index d4e50e394e0b..3be741b9b461 100644
--- a/arch/x86/include/asm/cpuid/api.h
+++ b/arch/x86/include/asm/cpuid/api.h
@@ -393,7 +393,7 @@ static inline u32 cpuid_base_hypervisor(const char *sig, u32 leaves)
 	((struct cpuid_regs *)(cpuid_leaf(_cpuinfo, _leaf)))
 
 #define __cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf)	\
-	static_assert(ARRAY_SIZE((_cpuinfo)->cpuid.leaves.leaf_ ## _leaf ## _0) > 1);
+	static_assert(ARRAY_SIZE((_cpuinfo)->cpuid.leaves.leaf_ ## _leaf ## _n) > 1);
 
 /**
  * cpuid_subleaf_index() - Access parsed CPUID data at runtime subleaf index
@@ -415,7 +415,7 @@ static inline u32 cpuid_base_hypervisor(const char *sig, u32 leaves)
  *
  * Example usage::
  *
- *	const struct leaf_0x4_0 *l4;
+ *	const struct leaf_0x4_n *l4;
  *
  *	for (int i = 0; i < cpuid_subleaf_count(c, 0x4); i++) {
  *		l4 = cpuid_subleaf_index(c, 0x4, i);
@@ -432,7 +432,10 @@ static inline u32 cpuid_base_hypervisor(const char *sig, u32 leaves)
 #define cpuid_subleaf_index(_cpuinfo, _leaf, _idx)			\
 ({									\
 	__cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf);	\
-	__cpuid_table_subleaf_idx(&(_cpuinfo)->cpuid, _leaf, 0, _idx);	\
+	BUILD_BUG_ON(__builtin_constant_p(_idx) &&			\
+		     ((_idx) < CPUID_LEAF_ ## _leaf ## _N_FIRST ||	\
+		      (_idx) > CPUID_LEAF_ ## _leaf ## _N_LAST));	\
+	__cpuid_table_subleaf_idx(&(_cpuinfo)->cpuid, _leaf, n, _idx);	\
 })
 
 /**
@@ -464,7 +467,7 @@ static inline u32 cpuid_base_hypervisor(const char *sig, u32 leaves)
 #define cpuid_subleaf_count(_cpuinfo, _leaf)				\
 ({									\
 	__cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf);	\
-	__cpuid_leaves_subleaf_info(&(_cpuinfo)->cpuid.leaves, _leaf, 0).nr_entries; \
+	__cpuid_leaves_subleaf_info(&(_cpuinfo)->cpuid.leaves, _leaf, n).nr_entries; \
 })
 
 /*
diff --git a/arch/x86/include/asm/cpuid/leaf_defs.h b/arch/x86/include/asm/cpuid/leaf_defs.h
new file mode 100644
index 000000000000..bb3d744939c2
--- /dev/null
+++ b/arch/x86/include/asm/cpuid/leaf_defs.h
@@ -0,0 +1,12 @@
+CPUID_LEAF(0x0,                 0)
+CPUID_LEAF(0x1,                 0)
+CPUID_LEAF(0x2,                 0)
+CPUID_LEAF_N(0x4,               0,      7)
+CPUID_LEAF(0xd,                 0)
+CPUID_LEAF(0xd,                 1)
+CPUID_LEAF_N(0xd,               2,      63)
+CPUID_LEAF(0x16,                0)
+CPUID_LEAF(0x80000000,          0)
+CPUID_LEAF(0x80000005,          0)
+CPUID_LEAF(0x80000006,          0)
+CPUID_LEAF_N(0x8000001d,        0,      7)
\ No newline at end of file
diff --git a/arch/x86/include/asm/cpuid/types.h b/arch/x86/include/asm/cpuid/types.h
index c044f7bc7137..2a0d30e13b71 100644
--- a/arch/x86/include/asm/cpuid/types.h
+++ b/arch/x86/include/asm/cpuid/types.h
@@ -153,7 +153,7 @@ struct leaf_query_info {
 
 /**
  * __CPUID_LEAF() - Define CPUID output storage and query info entry
- * @_name:	Struct type name of the CPUID leaf/subleaf (e.g. 'leaf_0x4_0').
+ * @_name:	Struct type name of the CPUID leaf/subleaf (e.g. 'leaf_0x4_n').
  *		Such types are defined at <cpuid/leaf_types.h>, and follow the
  *		format 'struct leaf_0xN_M', where 0xN is the leaf and M is the
  *		subleaf.
@@ -183,19 +183,19 @@ struct leaf_query_info {
  *
  * The example invocation for CPUID(0x4) storage::
  *
- *	__CPUID_LEAF(leaf_0x4_0, 8);
+ *	__CPUID_LEAF(leaf_0x4_n, 8);
  *
  * generates storage entries in the form:
  *
- *	struct leaf_0x4_0		leaf_0x4_0[8];
- *	struct leaf_query_info		leaf_0x4_0_info;
+ *	struct leaf_0x4_n		leaf_0x4_n[8];
+ *	struct leaf_query_info		leaf_0x4_n_info;
  *
- * where the 'leaf_0x4_0[8]' storage array can accommodate the output of
+ * where the 'leaf_0x4_n[8]' storage array can accommodate the output of
  * CPUID(0x4) subleaves 0->7, since they all have the same output format.
  */
 #define __CPUID_LEAF(_name, _count)				\
 	struct _name		_name[_count];			\
-	struct leaf_query_info	_name##_info
+	struct leaf_query_info	_name##_info;
 
 /**
  * CPUID_LEAF() - Define a CPUID storage entry in 'struct cpuid_leaves'
@@ -205,23 +205,30 @@ struct leaf_query_info {
  *
  * Convenience wrapper around __CPUID_LEAF().
  */
-#define CPUID_LEAF(_leaf, _subleaf, _count)			\
-	__CPUID_LEAF(leaf_ ## _leaf ## _ ## _subleaf, _count)
+#define CPUID_LEAF(_leaf, _subleaf)			\
+	__CPUID_LEAF(leaf_ ## _leaf ## _ ## _subleaf, 1)
+
+#define CPUID_LEAF_N(_leaf, _first, _last)		\
+	__CPUID_LEAF(leaf_ ## _leaf ## _n, _last - _first + 1)
+
+
 
 /*
  * struct cpuid_leaves - Structured CPUID data repository
  */
 struct cpuid_leaves {
-	/*         leaf		subleaf		count */
-	CPUID_LEAF(0x0,		0,		1);
-	CPUID_LEAF(0x1,		0,		1);
-	CPUID_LEAF(0x2,		0,		1);
-	CPUID_LEAF(0x4,		0,		8);
-	CPUID_LEAF(0x16,	0,		1);
-	CPUID_LEAF(0x80000000,	0,		1);
-	CPUID_LEAF(0x80000005,	0,		1);
-	CPUID_LEAF(0x80000006,	0,		1);
-	CPUID_LEAF(0x8000001d,	0,		8);
+#include "leaf_defs.h"
+};
+
+#undef CPUID_LEAF
+#undef CPUID_LEAF_N
+#define CPUID_LEAF(_leaf, _subleaf)
+#define CPUID_LEAF_N(_leaf, _first, _last)		\
+	CPUID_LEAF_ ## _leaf ## _N_FIRST = _first,	\
+	CPUID_LEAF_ ## _leaf ## _N_LAST  = _last,
+
+enum cpuid_dynamic_leaf_ranges {
+#include "leaf_defs.h"
 };
 
 /*

base-commit: 2f267fb52973ddf5949127628a4338d4d976ff11
--
Re: [PATCH v4 07/34] x86/cpuid: Introduce a centralized CPUID data model
Posted by Ahmed S. Darwish 1 month, 2 weeks ago
Hi,

On Mon, 18 Aug 2025, Sean Christopherson wrote:
>
> Not sure "cleaner" is the right word, but if you really want to add
> compile-time sanity checks, you could put the actual definitions in a
> dedicated header file that's included multiple times without any #ifdef
> guards.  Once to define "struct cpuid_leaves", and a second time to
> define global metadata for each leaf, e.g. the first/last subleaf in a
> dynamic range.
>
...
> @@ -432,7 +432,10 @@ static inline u32 cpuid_base_hypervisor(const char *sig, u32 leaves)
>  #define cpuid_subleaf_index(_cpuinfo, _leaf, _idx)			\
>  ({									\
>  	__cpuid_assert_leaf_has_dynamic_subleaves(_cpuinfo, _leaf);	\
> -	__cpuid_table_subleaf_idx(&(_cpuinfo)->cpuid, _leaf, 0, _idx);	\
> +	BUILD_BUG_ON(__builtin_constant_p(_idx) &&			\
> +		     ((_idx) < CPUID_LEAF_ ## _leaf ## _N_FIRST ||	\
> +		      (_idx) > CPUID_LEAF_ ## _leaf ## _N_LAST));	\
> +	__cpuid_table_subleaf_idx(&(_cpuinfo)->cpuid, _leaf, n, _idx);	\
>  })
>
...
> -#define CPUID_LEAF(_leaf, _subleaf, _count)			\
> -	__CPUID_LEAF(leaf_ ## _leaf ## _ ## _subleaf, _count)
> +#define CPUID_LEAF(_leaf, _subleaf)			\
> +	__CPUID_LEAF(leaf_ ## _leaf ## _ ## _subleaf, 1)
> +
> +#define CPUID_LEAF_N(_leaf, _first, _last)		\
> +	__CPUID_LEAF(leaf_ ## _leaf ## _n, _last - _first + 1)
> +
> +
>
...
> +
> +#undef CPUID_LEAF
> +#undef CPUID_LEAF_N
> +#define CPUID_LEAF(_leaf, _subleaf)
> +#define CPUID_LEAF_N(_leaf, _first, _last)		\
> +	CPUID_LEAF_ ## _leaf ## _N_FIRST = _first,	\
> +	CPUID_LEAF_ ## _leaf ## _N_LAST  = _last,
> +
> +enum cpuid_dynamic_leaf_ranges {
> +#include "leaf_defs.h"
>  };
...

Thanks a lot for jumping-in and posting code on top; really appreciated!

Yes, I'm doing a new 'x86-cpuid-db' release now.

It will generate <asm/cpuid/api/leaf_types.h> in below form; showing only
the dynamic CPUID leaves here:

    struct leaf_0x4_n { ... };

    #define LEAF_0x4_SUBLEAF_N_FIRST		0
    #define LEAF_0x4_SUBLEAF_N_LAST		31

    struct leaf_0xb_n { ... };

    #define LEAF_0xb_SUBLEAF_N_FIRST		0
    #define LEAF_0xb_SUBLEAF_N_LAST		1

    struct leaf_0xd_n { ... };

    #define LEAF_0xd_SUBLEAF_N_FIRST		2
    #define LEAF_0xd_SUBLEAF_N_LAST		62

    struct leaf_0x10_n { ... };

    #define LEAF_0x10_SUBLEAF_N_FIRST		1
    #define LEAF_0x10_SUBLEAF_N_LAST		2

    struct leaf_0x12_n { ... };

    #define LEAF_0x12_SUBLEAF_N_FIRST		2
    #define LEAF_0x12_SUBLEAF_N_LAST		9

    // ...

    struct leaf_0x8000001d_n { ... };

    #define LEAF_0x8000001d_SUBLEAF_N_FIRST	0
    #define LEAF_0x8000001d_SUBLEAF_N_LAST	31

    struct leaf_0x80000026_n { ... };

    #define LEAF_0x80000026_SUBLEAF_N_FIRST	0
    #define LEAF_0x80000026_SUBLEAF_N_LAST	3

IMO, these are a clean set of symbols.  They are in the same header file
because Ingo would like to keep the header structure simple (and 100%
agreement from my side; especially not to hit any ugly generated files
version mismatches later).  That is:

    arch/x86/include/asm/cpuid/
    ├── api.h
    ├── leaf_types.h				// One auto-generated file
    └── types.h

The _SUBLEAF_N_FIRST/LAST symbols reflect below CPUID XML database lines:

    leaf_04.xml:        <subleaf id="0" last="31">
    leaf_0b.xml:        <subleaf id="0" last="1">
    leaf_0d.xml:        <subleaf id="2" last="62">
    leaf_10.xml:        <subleaf id="1" last="2">
    leaf_12.xml:        <subleaf id="2" last="9">
    ...
    leaf_8000001d.xml:	<subleaf id="0" last="31">
    leaf_80000026.xml:	<subleaf id="0" last="3">

In current release, the last="" attribute is called array="", but that's
an implementation detail. The new attribute name is much more clear
(thanks for the _FIRST/_LAST suggestion); it fits current semantics.

Then, everything else, like the snippet you posted (slightly adapted):

> +	BUILD_BUG_ON(__builtin_constant_p(_idx) &&			\
> +		     ((_idx) < LEAF_ ## _leaf ## SUBLEAF_N_FIRST ||	\
> +		      (_idx) > LEAF_ ## _leaf ## SUBLEAF__N_LAST));	\

would work perfectly out of the box.

To give freedom to the Linux Kernel code though, the kernel's own per-CPU
CPUID tables can have storage areas less than

    LEAF_M_SUBLEAF_N_LAST - LEAF_M_SUBLEAF_N_FIST + 1

For example:

    struct leaf_0x0_0           leaf_0x0_0[1];
    ...
    struct leaf_0x4_0           leaf_0x4_0[8];		// 8 < 32
    ...
    struct leaf_0x8000001d_0    leaf_0x8000001d_0[8];	// 8 < 32

That should be OK, and is by design.  The BUILD_BUG_ON() snippet above
can easily be adapted with a ARRAY_SIZE().

After all that (which is not much extra atually, thanks to the XML symbol
generation), we can have pretty nice compile- and run-time safety for the
new API:

    cpuid_subleaf_n(_cpuinfo, _leaf, _idx)

The other CPUID parser APIs:

    cpuid_leaf(_cpuinfo, _leaf)
    cpuid_subleaf(_cpuinfo, _leaf, _subleaf)

already have total compile-time safety due to the zero-casting.

This is all is pretty neat.  Thanks again!

Kind regards,
Ahmed