lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250815070227.19981-15-darwi@linutronix.de>
Date: Fri, 15 Aug 2025 09:02:07 +0200
From: "Ahmed S. Darwish" <darwi@...utronix.de>
To: Borislav Petkov <bp@...en8.de>,
	Ingo Molnar <mingo@...hat.com>,
	Dave Hansen <dave.hansen@...ux.intel.com>
Cc: Thomas Gleixner <tglx@...utronix.de>,
	Andrew Cooper <andrew.cooper3@...rix.com>,
	"H. Peter Anvin" <hpa@...or.com>,
	David Woodhouse <dwmw2@...radead.org>,
	Sean Christopherson <seanjc@...gle.com>,
	Peter Zijlstra <peterz@...radead.org>,
	Sohil Mehta <sohil.mehta@...el.com>,
	John Ogness <john.ogness@...utronix.de>,
	x86@...nel.org,
	x86-cpuid@...ts.linux.dev,
	LKML <linux-kernel@...r.kernel.org>,
	"Ahmed S. Darwish" <darwi@...utronix.de>
Subject: [PATCH v4 14/34] x86/cpuid: Introduce a CPUID leaf x86 vendor table

For the CPUID parser, introduce a table listing vendor-specific CPUID
leaves. Not all CPUID leaves should be queried on all x86 vendors, so the
parser will only enumerate such leaves if the boot machine's x86 vendor
is listed as supported.

This provides the following benefits:

  (a) Even when a CPUID leaf falls within the CPU's standard or extended
  maximum leaf range, querying architecturally unsupported and reserved
  CPUID leaves may trigger new kernel boot behaviors or subtle bugs,
  especially on legacy machines.

  (b) Associating x86 vendor information with CPUID leaves will enable
  the CPUID parser to emit (lightweight) error messages when malformed
  CPUID leaf output is detected.  This is due to the parser now being
  more certain that the queried leaf is valid on the machine.

  (c) Attaching x86 vendor information to CPUID leaves will relieve
  call-sites, especially drivers, from ugly x86 vendor checks before
  querying a CPUID leaf.  If the CPUID parsers API like cpuid_leaf() or
  cpuid_subleaf() return NULL, it willy simply implies the leaf is
  simply unavailable (or should not be queried) on the current machine.

Split the CPUID parsing table into an "early boot" table and a standard
one.  The early boot phase parses only CPUID(0x0) and CPUID(0x1), where
they will be needed to identify the CPU's x86 vendor.

Once the kernel saves the vendor info to the CPU's capability structure,
invoke the CPUID parser again to parse the rest of the CPUID leaves.  In
that second phase, the parser assumes that "boot_cpu_data.x86_vendor" is
valid and uses it for CPUID leaf x86 vendor validity checks.

For each vendor-specific CPUID leaf, build its list of matching x86
vendors using CPP varargs.  Encoding this as bitflags was not doable,
since the x86 vendor IDs are just raw monotonic numbers from 0 (Intel) to
11 (Vortex).

Keep the CPUID parser's vendor-specific leaf table empty for now.  Leaves
like CPUID(0x2), CPUID(0x4), CPUID(0x16), and CPUID(0x8000001d) will be
added to the vendor table once their support is actually added to the
parser.

Signed-off-by: Ahmed S. Darwish <darwi@...utronix.de>
---
 arch/x86/kernel/cpu/common.c       |   3 +-
 arch/x86/kernel/cpu/cpuid_parser.c | 108 ++++++++++++++++++++++++-----
 arch/x86/kernel/cpu/cpuid_parser.h |  51 +++++++++++++-
 3 files changed, 142 insertions(+), 20 deletions(-)

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 6b5a4dd2f33e..048b285e7741 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1725,9 +1725,10 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
 
 	/* cyrix could have cpuid enabled via c_identify()*/
 	if (cpuid_feature()) {
-		cpuid_parser_scan_cpu(c);
+		cpuid_parser_early_scan_cpu(c);
 		cpu_detect(c);
 		get_cpu_vendor(c);
+		cpuid_parser_scan_cpu(c);
 		intel_unlock_cpuid_leafs(c);
 		get_cpu_cap(c);
 		setup_force_cpu_cap(X86_FEATURE_CPUID);
diff --git a/arch/x86/kernel/cpu/cpuid_parser.c b/arch/x86/kernel/cpu/cpuid_parser.c
index be9c8571f886..84d70a432212 100644
--- a/arch/x86/kernel/cpu/cpuid_parser.c
+++ b/arch/x86/kernel/cpu/cpuid_parser.c
@@ -12,6 +12,10 @@
 
 #include "cpuid_parser.h"
 
+static const struct cpuid_vendor_entry cpuid_vendor_entries[] = {
+	CPUID_VENDOR_ENTRIES
+};
+
 /*
  * Leaf read functions:
  */
@@ -55,10 +59,24 @@ static void cpuid_read_0x80000000(const struct cpuid_parse_entry *e, struct cpui
  *
  * Since these tables reference the leaf read functions above, they must be
  * defined afterwards.
+ *
+ * At early boot, only leaves at CPUID_EARLY_PARSE_ENTRIES should be parsed.
  */
 
-static const struct cpuid_parse_entry cpuid_parse_entries[] = {
-	CPUID_PARSE_ENTRIES
+static const struct cpuid_parse_entry cpuid_early_parse_entries[] = {
+	CPUID_EARLY_PARSE_ENTRIES
+};
+
+static const struct cpuid_parse_entry cpuid_common_parse_entries[] = {
+	CPUID_COMMON_PARSE_ENTRIES
+};
+
+static const struct {
+	const struct cpuid_parse_entry	*table;
+	int				nr_entries;
+} cpuid_parser_phases[] = {
+	{ cpuid_early_parse_entries,	ARRAY_SIZE(cpuid_early_parse_entries)	},
+	{ cpuid_common_parse_entries,	ARRAY_SIZE(cpuid_common_parse_entries)	},
 };
 
 /*
@@ -89,6 +107,32 @@ static bool cpuid_leaf_in_range(const struct cpuid_table *t, unsigned int leaf)
 	       cpuid_range_valid(t, leaf, CPUID_EXT_START, CPUID_EXT_END);
 }
 
+static bool cpuid_leaf_matches_vendor(unsigned int leaf, u8 cpu_vendor)
+{
+	const struct cpuid_parse_entry *p = cpuid_early_parse_entries;
+	const struct cpuid_vendor_entry *v = cpuid_vendor_entries;
+
+	/* Leaves in the early boot parser table are vendor agnostic */
+	for (int i = 0; i < ARRAY_SIZE(cpuid_early_parse_entries); i++, p++)
+		if (p->leaf == leaf)
+			return true;
+
+	/* Leaves in the vendor table must pass a CPU vendor check */
+	for (int i = 0; i < ARRAY_SIZE(cpuid_vendor_entries); i++, v++) {
+		if (v->leaf != leaf)
+			continue;
+
+		for (unsigned int j = 0; j < v->nvendors; j++)
+			if (cpu_vendor == v->vendors[j])
+				return true;
+
+		return false;
+	}
+
+	/* Remaining leaves are vendor agnostic */
+	return true;
+}
+
 static void
 cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[], unsigned int nr_entries)
 {
@@ -103,28 +147,21 @@ cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[]
 		if (!cpuid_leaf_in_range(t, entry->leaf))
 			continue;
 
+		if (!cpuid_leaf_matches_vendor(entry->leaf, boot_cpu_data.x86_vendor))
+ 			continue;
+
 		WARN_ON_ONCE(output.info->nr_entries != 0);
 		entry->read(entry, &output);
 	}
 }
 
-/*
- * Exported APIs:
- */
-
-/**
- * cpuid_parser_scan_cpu() - Populate current CPU's CPUID table
- * @c:		CPU capability structure associated with the current CPU
- *
- * Populate the CPUID table embedded within @c with parsed CPUID data.  Since all CPUID
- * instructions are invoked locally, this must be called on the CPU associated with @c.
- */
-void cpuid_parser_scan_cpu(struct cpuinfo_x86 *c)
+static void __cpuid_parser_scan_cpu(struct cpuinfo_x86 *c, bool early_boot)
 {
+	int nphases = early_boot ? 1 : ARRAY_SIZE(cpuid_parser_phases);
 	struct cpuid_table *table = &c->cpuid;
 
 	/*
-	 * For correctness, clear the CPUID table first.
+	 * After early boot, clear the CPUID table first.
 	 *
 	 * This is due to the CPUID parser APIs at <asm/cpuid/api.h> using leaf->nr_entries
 	 * as a leaf validity check: non-zero means that the CPUID leaf's cached output is
@@ -140,7 +177,44 @@ void cpuid_parser_scan_cpu(struct cpuinfo_x86 *c)
 	 * parsed (leaving stale leaf "nr_entries" fields behind.)  The table must thus be
 	 * also cleared.
 	 */
-	memset(table, 0, sizeof(*table));
+	if (!early_boot)
+		memset(table, 0, sizeof(*table));
+
+	for (int i = 0; i < nphases; i++)
+		cpuid_fill_table(table, cpuid_parser_phases[i].table, cpuid_parser_phases[i].nr_entries);
+}
+
+/*
+ * Exported APIs:
+ */
+
+/**
+ * cpuid_parser_scan_cpu() - Populate the current CPU's CPUID table
+ * @c:		CPU capability structure for the current CPU
+ *
+ * Populate the CPUID table embedded within @c with parsed CPUID data.	Since all CPUID
+ * instructions are invoked locally, this must be run on the CPU associated with @c.
+ *
+ * cpuid_parser_early_scan_cpu() must've been called, at least once, beforehand.
+ */
+void cpuid_parser_scan_cpu(struct cpuinfo_x86 *c)
+{
+	__cpuid_parser_scan_cpu(c, false);
+}
 
-	cpuid_fill_table(table, cpuid_parse_entries, ARRAY_SIZE(cpuid_common_parse_entries));
+/**
+ * cpuid_parser_early_scan_cpu() - Populate primary CPU's CPUID table on early boot
+ * @c:		CPU capability structure associated with the current CPU
+ *
+ * Populate the CPUID table embedded within @c with parsed CPUID data.
+ *
+ * This must be called at early boot, so that the boot code can identify the CPU's
+ * x86 vendor.	Only CPUID(0x0) and CPUID(0x1) are parsed.
+ *
+ * After saving the x86 vendor info in the boot CPU's capability structure,
+ * cpuid_parser_scan_cpu() must be called to complete the CPU's CPUID table.
+ */
+void __init cpuid_parser_early_scan_cpu(struct cpuinfo_x86 *c)
+{
+	__cpuid_parser_scan_cpu(c, true);
 }
diff --git a/arch/x86/kernel/cpu/cpuid_parser.h b/arch/x86/kernel/cpu/cpuid_parser.h
index c2b78badd67e..5d7a05e4b9cd 100644
--- a/arch/x86/kernel/cpu/cpuid_parser.h
+++ b/arch/x86/kernel/cpu/cpuid_parser.h
@@ -96,10 +96,57 @@ struct cpuid_parse_entry {
  * CPUID parser tables:
  */
 
-#define CPUID_PARSE_ENTRIES								\
+/*
+ * Early-boot CPUID leaves (to be parsed before x86 vendor detection)
+ *
+ * These leaves must be parsed at early boot to identify the x86 vendor. The
+ * parser treats them as universally valid across all vendors.
+ *
+ * At early boot, only leaves in this table must be parsed.  For all other
+ * leaves, the CPUID parser will assume that "boot_cpu_data.x86_vendor" is
+ * properly set beforehand.
+ *
+ * Note: If these entries are to be modified, please adapt the kernel-doc of
+ * cpuid_parser_early_scan_cpu() accordingly.
+ */
+#define CPUID_EARLY_PARSE_ENTRIES							\
 	/*		  Leaf		Subleaf		Reader function */		\
 	CPUID_PARSE_ENTRY(0x0,		0,		generic),			\
 	CPUID_PARSE_ENTRY(0x1,		0,		generic),			\
-	CPUID_PARSE_ENTRY(0x80000000,	0,		0x80000000),
+
+/*
+ * Common CPUID leaves
+ *
+ * These leaves can be parsed once basic x86 vendor detection is in place.
+ * Further vendor-agnostic leaves, which are not needed at early boot, are also
+ * listed here.
+ *
+ * For vendor-specific leaves, a matching entry must be added to the CPUID leaf
+ * vendor table later defined.	Leaves which are here, but without a matching
+ * vendor entry, are treated by the CPUID parser as valid for all x86 vendors.
+ */
+#define CPUID_COMMON_PARSE_ENTRIES							\
+	/*		  Leaf		Subleaf		Reader function */		\
+	CPUID_PARSE_ENTRY(0x80000000,	0,		0x80000000),			\
+
+/*
+ * CPUID leaf vendor table:
+ */
+
+struct cpuid_vendor_entry {
+	unsigned int	leaf;
+	u8		vendors[X86_VENDOR_NUM];
+	u8		nvendors;
+};
+
+#define CPUID_VENDOR_ENTRY(_leaf, ...)							\
+	{										\
+		.leaf		= _leaf,						\
+		.vendors	= { __VA_ARGS__ },					\
+		.nvendors	= (sizeof((u8[]){__VA_ARGS__})/sizeof(u8)),		\
+	}
+
+#define CPUID_VENDOR_ENTRIES								\
+ 	/*		   Leaf		Vendor list */					\
 
 #endif /* _ARCH_X86_CPUID_PARSER_H */
-- 
2.50.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ