From nobody Sat Oct 4 16:09:11 2025 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EC7022D1F5E for ; Fri, 15 Aug 2025 07:05:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755241550; cv=none; b=Z6B8r01U7cMBh8ipdOaCnLz3HFdOUTgUX1PGE/jX8ICq34V/Yt1qCFzqv7Yx6SAED6ytZxePMQK3+oms8B4MwFvccIHLwznNoNDCK+RgeTbrLAyE7MXFSP3xyfWl+8YuAbAkfvwW63vYHia4lXQi5BPHugZREl/uDp5DwpQlzSg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755241550; c=relaxed/simple; bh=KjXnRCgG+3JjYSr6lZ6UGTlRo7eGOmxnmQ+my1w1n+E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IFXs7qIUYslGLGImGqj+cpZWmsG5rb6FfVI9D1kVTczMk1frw0Zh+O7Y5VWimvb52THIZK8Tz34kzVJH44+W7o9cX0UBfaLB60XOC/WXXw/GI3x9UO7AseUOvd0uV9txND24/h+s7I1DuvQEt5PK+JKmmvMBUGONMmgQqXKRsXE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=o8Yae4uF; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=2Iuo87uj; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="o8Yae4uF"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="2Iuo87uj" From: "Ahmed S. Darwish" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1755241547; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=44qPXyYhlclQe+A2g0zv80/SvaG5TaAv4fQg2ML/Ylw=; b=o8Yae4uFLfTjP0mXXIbwv2sid1oGZCpAHHbOci6W26BUIwbDR7sy5jHqjJQhXazPzHr9q4 R8KD94ZGp5VW/678ZaFQIHVDTZONbRLXaZv89aEbbdBdKlXRJ9XtRCJhV6EMCRd/E5FU2c n4boryXxCDuDVanUNFLCL9hXCMHXvY3zNfeBCcN8s3/XLRLgh973ewt/UMU9riai2RKIpU cP1aPCejozESxKg0SxNPbo5oTM6gEcrXUTzuaEg17eCvGgOKUfo+7eGk6uvqc9VN1/dwjN AsHUmONRQF+O3itVQo1NvWYnJlLd0lEfe6wdgJfCHAy9IIfv+1uAZK91b20/3A== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1755241547; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=44qPXyYhlclQe+A2g0zv80/SvaG5TaAv4fQg2ML/Ylw=; b=2Iuo87ujOTusjDqd3HHzQz+awjtgwr24gEzHCTFHtZK3TMjCF5EVClvJQNtHgazucLaXtq 7f20S5aO7swN00AA== To: Borislav Petkov , Ingo Molnar , Dave Hansen Cc: Thomas Gleixner , Andrew Cooper , "H. Peter Anvin" , David Woodhouse , Sean Christopherson , Peter Zijlstra , Sohil Mehta , John Ogness , x86@kernel.org, x86-cpuid@lists.linux.dev, LKML , "Ahmed S. Darwish" Subject: [PATCH v4 14/34] x86/cpuid: Introduce a CPUID leaf x86 vendor table Date: Fri, 15 Aug 2025 09:02:07 +0200 Message-ID: <20250815070227.19981-15-darwi@linutronix.de> In-Reply-To: <20250815070227.19981-1-darwi@linutronix.de> References: <20250815070227.19981-1-darwi@linutronix.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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) =20 /* 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 @@ =20 #include "cpuid_parser.h" =20 +static const struct cpuid_vendor_entry cpuid_vendor_entries[] =3D { + CPUID_VENDOR_ENTRIES +}; + /* * Leaf read functions: */ @@ -55,10 +59,24 @@ static void cpuid_read_0x80000000(const struct cpuid_pa= rse_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 parse= d. */ =20 -static const struct cpuid_parse_entry cpuid_parse_entries[] =3D { - CPUID_PARSE_ENTRIES +static const struct cpuid_parse_entry cpuid_early_parse_entries[] =3D { + CPUID_EARLY_PARSE_ENTRIES +}; + +static const struct cpuid_parse_entry cpuid_common_parse_entries[] =3D { + CPUID_COMMON_PARSE_ENTRIES +}; + +static const struct { + const struct cpuid_parse_entry *table; + int nr_entries; +} cpuid_parser_phases[] =3D { + { cpuid_early_parse_entries, ARRAY_SIZE(cpuid_early_parse_entries) }, + { cpuid_common_parse_entries, ARRAY_SIZE(cpuid_common_parse_entries) }, }; =20 /* @@ -89,6 +107,32 @@ static bool cpuid_leaf_in_range(const struct cpuid_tabl= e *t, unsigned int leaf) cpuid_range_valid(t, leaf, CPUID_EXT_START, CPUID_EXT_END); } =20 +static bool cpuid_leaf_matches_vendor(unsigned int leaf, u8 cpu_vendor) +{ + const struct cpuid_parse_entry *p =3D cpuid_early_parse_entries; + const struct cpuid_vendor_entry *v =3D cpuid_vendor_entries; + + /* Leaves in the early boot parser table are vendor agnostic */ + for (int i =3D 0; i < ARRAY_SIZE(cpuid_early_parse_entries); i++, p++) + if (p->leaf =3D=3D leaf) + return true; + + /* Leaves in the vendor table must pass a CPU vendor check */ + for (int i =3D 0; i < ARRAY_SIZE(cpuid_vendor_entries); i++, v++) { + if (v->leaf !=3D leaf) + continue; + + for (unsigned int j =3D 0; j < v->nvendors; j++) + if (cpu_vendor =3D=3D 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 ent= ries[], 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; =20 + if (!cpuid_leaf_matches_vendor(entry->leaf, boot_cpu_data.x86_vendor)) + continue; + WARN_ON_ONCE(output.info->nr_entries !=3D 0); entry->read(entry, &output); } } =20 -/* - * 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. Si= nce all CPUID - * instructions are invoked locally, this must be called on the CPU associ= ated 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 =3D early_boot ? 1 : ARRAY_SIZE(cpuid_parser_phases); struct cpuid_table *table =3D &c->cpuid; =20 /* - * For correctness, clear the CPUID table first. + * After early boot, clear the CPUID table first. * * This is due to the CPUID parser APIs at 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 mus= t thus be * also cleared. */ - memset(table, 0, sizeof(*table)); + if (!early_boot) + memset(table, 0, sizeof(*table)); + + for (int i =3D 0; i < nphases; i++) + cpuid_fill_table(table, cpuid_parser_phases[i].table, cpuid_parser_phase= s[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. Sin= ce all CPUID + * instructions are invoked locally, this must be run on the CPU associate= d with @c. + * + * cpuid_parser_early_scan_cpu() must've been called, at least once, befor= ehand. + */ +void cpuid_parser_scan_cpu(struct cpuinfo_x86 *c) +{ + __cpuid_parser_scan_cpu(c, false); +} =20 - cpuid_fill_table(table, cpuid_parse_entries, ARRAY_SIZE(cpuid_common_pars= e_entries)); +/** + * cpuid_parser_early_scan_cpu() - Populate primary CPU's CPUID table on e= arly 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 t= he 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 tabl= e. + */ +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: */ =20 -#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. T= he + * 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 matchi= ng + * vendor entry, are treated by the CPUID parser as valid for all x86 vend= ors. + */ +#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 =3D _leaf, \ + .vendors =3D { __VA_ARGS__ }, \ + .nvendors =3D (sizeof((u8[]){__VA_ARGS__})/sizeof(u8)), \ + } + +#define CPUID_VENDOR_ENTRIES \ + /* Leaf Vendor list */ \ =20 #endif /* _ARCH_X86_CPUID_PARSER_H */ --=20 2.50.1