[PATCH v1 04/26] x86/cpuid: Introduce centralized CPUID data

Ahmed S. Darwish posted 26 patches 7 months, 2 weeks ago
[PATCH v1 04/26] x86/cpuid: Introduce centralized CPUID data
Posted by Ahmed S. Darwish 7 months, 2 weeks ago
Define the CPUID_LEAF() macro for building up centralized CPUID data.
It automates defining a CPUID data repository in the form:

    struct cpuid_leaves {
	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 for each 'struct leaf_0xN_M', N is the leaf number and M is the
subleaf.

The complete C99 bitfield listings of the 'leaf_0xN_M' structures is auto
generated by the x86-cpuid-db project and is merged in parent commits at
<asm/cpuid/leaves.h>.  This avoids using ugly bitwise operations on CPUID
register output.

Let the CPUID_LEAF() macro generate an array of output storage entries
for each leaf/subleaf combination.  An array is used to accommodate
leaves which produce the same output format for a large subleaf range,
which is typical for CPUID leaves enumerating hierarchical objects;
e.g. leaf 0x4 cache topology enumeration, leaf 0xd XSAVE enumeration, and
leaf 0x12 SGX Enclave Page Cache enumeration.  In the CPUID table snippet
above, leaf 0x4 has 8 storage entries.

For each of the leaf/subleaf entries in the CPUID table, attach a
'leaf_query_info' leaf_0xN_M_info structure.  It is to be filled by the
generic scanning logic filling the CPUID table.  For now, that info
structure has one element: the number of filled slots in the leaf/subleaf
storage array.

Define 'struct cpuid_table' for representing the actual CPUID table, and
embed in it a 'struct cpuid_leaves' instance.  This way, global table
data can be later added.

Embed an instance of that 'struct cpuid_table' in the CPU capability
structure 'struct cpuinfo_x86'.  This way, centralized CPUID data can be
accessed on early boot (through boot_cpu_data) and later on a per-CPU
basis through the 'cpu_info' per-CPU capability structures.  Since
multiple code paths dealing with 'struct cpuinfo_x86' assume that it has
no embedded pointers, embedding the cpuid_table instance avoids creating
special cases for no apparent benefit.

Define entries for leaf 0x0 and leaf 0x1 in the CPUID table.  Next
commits will add generic scanning logic for filling the CPUID data.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
---
 arch/x86/include/asm/cpuid/internal_api.h | 62 +++++++++++++++++++++++
 arch/x86/include/asm/cpuid/types.h        | 61 ++++++++++++++++++++++
 arch/x86/include/asm/processor.h          |  1 +
 3 files changed, 124 insertions(+)
 create mode 100644 arch/x86/include/asm/cpuid/internal_api.h

diff --git a/arch/x86/include/asm/cpuid/internal_api.h b/arch/x86/include/asm/cpuid/internal_api.h
new file mode 100644
index 000000000000..a1321ed19679
--- /dev/null
+++ b/arch/x86/include/asm/cpuid/internal_api.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_CPUID_INTERNAL_API_H
+#define _ASM_X86_CPUID_INTERNAL_API_H
+
+/*
+ * Raw 'struct cpuid_leaves' accessors
+ */
+
+#include <asm/cpuid/types.h>
+
+/**
+ * __cpuid_get() - Get scanned CPUID output (without sanity checks)
+ * @_leaves:	&struct cpuid_leaves instance
+ * @_leaf:	Leaf number in the 0xN format
+ * @_subleaf:	Subleaf number in decimal
+ * @_idx:	@_leaf/@_subleaf query output storage index
+ *
+ * Return the scanned CPUID output in a ready to parse <cpuid/leaves.h> type:
+ * struct leaf_0xN_M, where 0xN is the token provided at @_leaf, and M is the
+ * token provided at @_subleaf.
+ */
+#define __cpuid_get(__leaves, __leaf, __subleaf, __idx)			\
+	((__leaves)->leaf_ ## __leaf ## _ ## __subleaf)[__idx]
+
+/**
+ * __cpuid_info_get() - Get @_leaf/@_subleaf CPUID query info
+ * @_leaves:	&struct cpuid_leaves instance
+ * @_leaf:	Leaf number in the 0xN format
+ * @_subleaf:	Subleaf number in decimal
+ *
+ * Return @_leaves repository scanned @_leaf/@_subleaf CPUID query info, as
+ * &struct leaf_query_info.
+ */
+#define __cpuid_info_get(__leaves, __leaf, __subleaf)			\
+	((__leaves)->leaf_ ## __leaf ## _ ## __subleaf ## _ ## info)
+
+/**
+ * cpuid_get() - Get scanned CPUID output (without sanity checks)
+ * @_leaves:	&struct cpuid_leaves instance
+ * @_leaf:	Leaf number in the 0xN format
+ *
+ * Like __cpuid_get(), but with the subleaf and output storage index assumed
+ * as zero.
+ */
+#define cpuid_get(_leaves, _leaf)					\
+	__cpuid_get(_leaves, _leaf, 0, 0)
+
+/*
+ * struct cpuid_table accessors (with sanity checks)
+ *
+ * Return requested data as a <cpuid/leaves.h> data type, or NULL if the
+ * entry is not available.
+ */
+
+#define __cpudata_cpuid_subleaf_idx(__table, __leaf, __subleaf, __idx)	\
+	((__cpuid_info_get(&((__table)->leaves), __leaf, __subleaf).nr_entries > __idx) ? \
+	 &__cpuid_get(&((__table)->leaves), __leaf, __subleaf, __idx) : NULL)
+
+#define __cpudata_cpuid_subleaf(__table, __leaf, __subleaf)		\
+	__cpudata_cpuid_subleaf_idx(__table, __leaf, __subleaf, 0)
+
+#endif /* _ASM_X86_CPUID_INTERNAL_API_H */
diff --git a/arch/x86/include/asm/cpuid/types.h b/arch/x86/include/asm/cpuid/types.h
index c95fee66e148..df115a8440bc 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/leaves.h>
+
 /*
  * Types for raw CPUID access:
  */
@@ -125,4 +127,63 @@ extern const struct leaf_0x2_table cpuid_0x2_table[256];
  */
 #define TLB_0x63_2M_4M_ENTRIES		32
 
+/*
+ * Types for centralized CPUID tables:
+ */
+
+/**
+ * struct leaf_query_info - Info for a CPUID leaf/subleaf query
+ * @nr_entries:	Number of filled entries by the CPUID scanner for leaf/subleaf
+ *
+ * Each leaf/subleaf entry in a CPUID table (struct cpuid_leaves) has this
+ * dedicated query info.
+ */
+struct leaf_query_info {
+	unsigned int		nr_entries;
+};
+
+/**
+ * __CPUID_LEAF() - Define CPUID storage entries as <cpuid/leaves.h> structures
+ * @_name:	leaf_0xN_M structure name and type as found in <cpuid/leaves.h>,
+ *		where N is the leaf and M is the subleaf.
+ * @_count:	Max storage capacity for the combined leaf/subleaf @_name entry
+ *
+ * Define an array of storage entries for each leaf/subleaf combination.
+ * Use an array to accommodate leaves which produce the same output format
+ * for a large subleaf range, which is common for hierarchical objects
+ * enumeration; e.g., leaf 0x4, 0xd, and 0x12.
+ */
+#define __CPUID_LEAF(_name, _count)				\
+	struct _name		_name[_count];			\
+	struct leaf_query_info	_name ## _ ## info
+
+/**
+ * CPUID_LEAF() - Define CPUID storage entries for @_leaf/@_subleaf
+ * @_leaf:	Leaf number in the 0xN format
+ * @_subleaf:	Subleaf number in decimal
+ * @_count:	Number of repeated storage entries for @_leaf/@_subleaf
+ */
+#define CPUID_LEAF(_leaf, _subleaf, _count)			\
+	__CPUID_LEAF(leaf_ ## _leaf ## _ ## _subleaf, _count)
+
+/*
+ * struct cpuid_leaves - CPUID leaf/subleaf output storage
+ */
+struct cpuid_leaves {
+	/*         leaf		subleaf		count */
+	CPUID_LEAF(0x0,		0,		1);
+	CPUID_LEAF(0x1,		0,		1);
+};
+
+/**
+ * struct cpuid_table - System CPUID data table
+ * @leaves:	Leaf/subleaf output storage space
+ *
+ * struct cpuinfo_x86 embeds an instance of this table so that CPUID
+ * data can be extracted using a CPU capability structure reference.
+ */
+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 5d2f7e5aff26..df85dab402b8 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -165,6 +165,7 @@ struct cpuinfo_x86 {
 	char			x86_vendor_id[16];
 	char			x86_model_id[64];
 	struct cpuinfo_topology	topo;
+	struct cpuid_table	cpuid_table;
 	/* in KB - valid for CPUS which support this call: */
 	unsigned int		x86_cache_size;
 	int			x86_cache_alignment;	/* In bytes */
-- 
2.49.0
Re: [PATCH v1 04/26] x86/cpuid: Introduce centralized CPUID data
Posted by Sohil Mehta 7 months ago
On 5/5/2025 10:04 PM, Ahmed S. Darwish wrote:
> Define the CPUID_LEAF() macro for building up centralized CPUID data.
> It automates defining a CPUID data repository in the form:
> 
>     struct cpuid_leaves {
> 	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 for each 'struct leaf_0xN_M', N is the leaf number and M is the
> subleaf.
> 

I am finding the structure names a bit confusing. Can we make it
slightly more descriptive since they are directly used in common code?

How about struct leaf_0xN_sl_M or struct leaf_0xN_subl_M?

The actual struct names would be:
leaf_0x1_sl_0 or leaf_0x1_subl_0
leaf_0x4_sl_0 or leaf_0x4_subl_0

The variable names can obviously be simpler based on usage and context.

> The complete C99 bitfield listings of the 'leaf_0xN_M' structures is auto
> generated by the x86-cpuid-db project and is merged in parent commits at
> <asm/cpuid/leaves.h>.  This avoids using ugly bitwise operations on CPUID
> register output.
> 
> Let the CPUID_LEAF() macro generate an array of output storage entries
> for each leaf/subleaf combination.  An array is used to accommodate
> leaves which produce the same output format for a large subleaf range,
> which is typical for CPUID leaves enumerating hierarchical objects;
> e.g. leaf 0x4 cache topology enumeration, leaf 0xd XSAVE enumeration, and
> leaf 0x12 SGX Enclave Page Cache enumeration.  In the CPUID table snippet
> above, leaf 0x4 has 8 storage entries.
> 
> For each of the leaf/subleaf entries in the CPUID table, attach a
> 'leaf_query_info' leaf_0xN_M_info structure.  It is to be filled by the
> generic scanning logic filling the CPUID table.  For now, that info
> structure has one element: the number of filled slots in the leaf/subleaf
> storage array.
> 
> Define 'struct cpuid_table' for representing the actual CPUID table, and
> embed in it a 'struct cpuid_leaves' instance.  This way, global table
> data can be later added.
> 
> Embed an instance of that 'struct cpuid_table' in the CPU capability
> structure 'struct cpuinfo_x86'.  This way, centralized CPUID data can be
> accessed on early boot (through boot_cpu_data) and later on a per-CPU
> basis through the 'cpu_info' per-CPU capability structures.  Since
> multiple code paths dealing with 'struct cpuinfo_x86' assume that it has
> no embedded pointers, embedding the cpuid_table instance avoids creating
> special cases for no apparent benefit.
> 
> Define entries for leaf 0x0 and leaf 0x1 in the CPUID table.  Next
> commits will add generic scanning logic for filling the CPUID data.
> 

Avoid using "next commits". How about:

Generic scanning logic for filling the CPUID data will be added later.

> Suggested-by: Thomas Gleixner <tglx@linutronix.de>
> Signed-off-by: Ahmed S. Darwish <darwi@linutronix.de>
Re: [PATCH v1 04/26] x86/cpuid: Introduce centralized CPUID data
Posted by Ahmed S. Darwish 7 months ago
On Tue, 13 May 2025, Sohil Mehta wrote:
>
> I am finding the structure names a bit confusing. Can we make it
> slightly more descriptive since they are directly used in common code?
>
> How about struct leaf_0xN_sl_M or struct leaf_0xN_subl_M?
>
> The actual struct names would be:
> leaf_0x1_sl_0 or leaf_0x1_subl_0
> leaf_0x4_sl_0 or leaf_0x4_subl_0
>

The problem is that at the call sites, even with abbreviated variable
names, the lines are already wide.  Adding "sl_" makes things worse.

For example, at patch 23/26 ("x86/cacheinfo: Use scanned
CPUID(0x80000005) and CPUID(0x80000006)"), we have:

	const struct leaf_0x80000005_0 *el5 = cpudata_cpuid_index(c, 0x80000005, index);
	const struct leaf_0x80000006_0 *el6 = cpudata_cpuid_index(c, 0x80000006, index);

Making that even wider with an "sl_":

	const struct leaf_0x80000005_sl_0 *el5 = cpudata_cpuid_index(c, 0x80000005, index);
	const struct leaf_0x80000006_sl_0 *el6 = cpudata_cpuid_index(c, 0x80000006, index);

or "subl_":

	const struct leaf_0x80000005_subl_0 *el5 = cpudata_cpuid_index(c, 0x80000005, index);
	const struct leaf_0x80000006_subl_0 *el6 = cpudata_cpuid_index(c, 0x80000006, index);

makes everything overly verbose, without IMHO much benefit.

I'll sleep over this a bit before sending v2.

>
> Avoid using "next commits". How about:
>
> Generic scanning logic for filling the CPUID data will be added later.
>

Makes sense, will do.

Thanks!
~ Ahmed
Re: [PATCH v1 04/26] x86/cpuid: Introduce centralized CPUID data
Posted by Sohil Mehta 7 months ago
On 5/15/2025 2:23 PM, Ahmed S. Darwish wrote:
> On Tue, 13 May 2025, Sohil Mehta wrote:
>>
>> I am finding the structure names a bit confusing. Can we make it
>> slightly more descriptive since they are directly used in common code?
>>
>> How about struct leaf_0xN_sl_M or struct leaf_0xN_subl_M?
>>
>> The actual struct names would be:
>> leaf_0x1_sl_0 or leaf_0x1_subl_0
>> leaf_0x4_sl_0 or leaf_0x4_subl_0
>>
> 
> The problem is that at the call sites, even with abbreviated variable
> names, the lines are already wide.  Adding "sl_" makes things worse.
> 
> For example, at patch 23/26 ("x86/cacheinfo: Use scanned
> CPUID(0x80000005) and CPUID(0x80000006)"), we have:
> 
> 	const struct leaf_0x80000005_0 *el5 = cpudata_cpuid_index(c, 0x80000005, index);
> 	const struct leaf_0x80000006_0 *el6 = cpudata_cpuid_index(c, 0x80000006, index);
> 
> Making that even wider with an "sl_":
> 
> 	const struct leaf_0x80000005_sl_0 *el5 = cpudata_cpuid_index(c, 0x80000005, index);
> 	const struct leaf_0x80000006_sl_0 *el6 = cpudata_cpuid_index(c, 0x80000006, index);
> 
> or "subl_":
> 
> 	const struct leaf_0x80000005_subl_0 *el5 = cpudata_cpuid_index(c, 0x80000005, index);
> 	const struct leaf_0x80000006_subl_0 *el6 = cpudata_cpuid_index(c, 0x80000006, index);
> 
> makes everything overly verbose, without IMHO much benefit.
> 

It does make it more verbose but things can get harder to read once the
subleaf numbers start going higher. Example (actual cpuid values):

leaf_0x4_3
leaf_0x10_1
leaf_0xd_11
leaf_0x1d_1

I don't have a better alternative, so I'll leave it up to you.

> I'll sleep over this a bit before sending v2.
> 

Another thing to ponder over would be the combination of hexadecimal and
decimal in the 0xN_M naming scheme. The "cpuid" tool uses hex for
printing the subleaf, but the Intel spec describes CPUID using decimals.

Leaving it as decimal is probably fine.

Sohil