From nobody Fri Jun 12 17:18:01 2026 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 F174C18E025; Wed, 13 May 2026 16:24:25 +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=1778689467; cv=none; b=aLj1zVz6sr8uwoA3pb5Qpg65GumDsc8owRhY+oFDxGBN5Yac5kQLbUv9/kHzGeDYVZA6ZDo3/3WQbIJ+FgPKL3V5dthz1m7ve0yY4aJU3xiiqyMOlcLHVUwP0MZLOZ0OhVFtkG2KFUVQzYw22oa726qVjFRSILQJL+0hwyV4b50= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778689467; c=relaxed/simple; bh=10qoTVP3HYG4ffsK+MLmMAuYLsiMK+vp0k9R9iGlfPI=; h=Date:From:To:Subject:Cc:MIME-Version:Message-ID:Content-Type; b=qtCBrfb8Fr45TUB8zjD+wYp203jIj7ZM/9CIExVcq0nn+9zvQnXzoONEzX1gIHIquk8idSGAm7zM3mP7QaxUqpdiJcjb9Mvt5I3rKjfhT5mXCDeRRyKIqvJ0w7oYXodaHGEE2fNgJNAFjtbHJBPobe3ahSuQUmiDOu2xOZ3jLIU= 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=X0xEza/g; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=PkZAjJzY; 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="X0xEza/g"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="PkZAjJzY" Date: Wed, 13 May 2026 16:24:23 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1778689464; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=wsaXXnM57BtwqF3zL1em9NGx0ddUUZymDUHNsMKrQPA=; b=X0xEza/gTBxS976QU/ZTKaGBAlxxhgg7lCtWia9jDfzEsIIQkChyabT0JG0zkV9IvEVJqE 6H0fYmBxm6SLo2jrzkbAb/qfTLsBqgELaega8iRZXcii/FHRzWhTygqY2bLplDg0d715Pa TOOZbmv6iYZbqf1V4EaY0ePxGoEqxW3uM7jFYMXzCDN/azyVKQXAcHVFPC9ISAOH1CfFI+ X8OHgoYhE/kSnNv7WXVa7W3CdGBdN/kgAHGk4485pqnj77KTLASrdW6iPyTw0VatrLMkVK V3HcyVGqVR95h9ckJQj411nIoV69G9lSetNacUxdPEDj55cCJanCVC1ob3p5PA== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1778689464; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=wsaXXnM57BtwqF3zL1em9NGx0ddUUZymDUHNsMKrQPA=; b=PkZAjJzYAM7RYhmS0NW2BRyBnpOxivf5cHrWiHjPy89+iN/liM5sIaP7U+IIk2iLi4VE8x cIvtDRZ+/lMgFWAQ== From: "tip-bot2 for Ahmed S. Darwish" Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: x86/cpu] x86/cpuid: Introduce a centralized CPUID parser Cc: Thomas Gleixner , "Ahmed S. Darwish" , "Borislav Petkov (AMD)" , x86@kernel.org, linux-kernel@vger.kernel.org Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <177868946308.188840.18400575006267953502.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the x86/cpu branch of tip: Commit-ID: fa6dcbc69ad495eeea315870278100a554a4ca18 Gitweb: https://git.kernel.org/tip/fa6dcbc69ad495eeea315870278100a55= 4a4ca18 Author: Ahmed S. Darwish AuthorDate: Fri, 27 Mar 2026 03:15:23 +01:00 Committer: Borislav Petkov (AMD) CommitterDate: Mon, 11 May 2026 16:05:50 +02:00 x86/cpuid: Introduce a centralized CPUID parser Introduce a CPUID parser for populating the system's CPUID tables. Since accessing a leaf within the CPUID table requires compile time tokenization, split the parser into two stages: (a) Compile-time macros for tokenizing the leaf/subleaf offsets within the CPUID table. (b) Generic runtime code to fill the CPUID data, using a parsing table which collects these compile-time offsets. For actual CPUID output parsing, support both generic and leaf-specific read functions. To ensure CPUID data early availability, invoke the parser during early boot, early Xen boot, and at early secondary CPUs bring up. Provide call site APIs to refresh a single leaf, or a leaf range, within the CPUID tables. This is for sites issuing MSR writes that partially change the CPU's CPUID layout. Doing full CPUID table rescans in such cases will be destructive since the CPUID tables will host all of the kernel's X86_FEATURE flags at a later stage. Suggested-by: Thomas Gleixner Signed-off-by: Ahmed S. Darwish Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/all/20260327021645.555257-1-darwi@linutronix.= de --- arch/x86/include/asm/cpuid/api.h | 9 +- arch/x86/kernel/cpu/Makefile | 1 +- arch/x86/kernel/cpu/common.c | 5 +- arch/x86/kernel/cpu/cpuid_parser.c | 182 ++++++++++++++++++++++++++++- arch/x86/kernel/cpu/cpuid_parser.h | 120 ++++++++++++++++++- arch/x86/xen/enlighten.c | 3 +- arch/x86/xen/enlighten_pv.c | 1 +- 7 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 arch/x86/kernel/cpu/cpuid_parser.c create mode 100644 arch/x86/kernel/cpu/cpuid_parser.h diff --git a/arch/x86/include/asm/cpuid/api.h b/arch/x86/include/asm/cpuid/= api.h index b868902..82eddfa 100644 --- a/arch/x86/include/asm/cpuid/api.h +++ b/arch/x86/include/asm/cpuid/api.h @@ -7,6 +7,7 @@ #include #include =20 +#include #include =20 /* @@ -527,4 +528,12 @@ static inline bool cpuid_amd_hygon_has_l3_cache(void) __cpuid_table_nr_filled_subleaves(&(_cpuinfo)->cpuid, _leaf, n); \ }) =20 +/* + * CPUID parser exported APIs: + */ + +void cpuid_scan_cpu(struct cpuinfo_x86 *c); +void cpuid_refresh_leaf(struct cpuinfo_x86 *c, u32 leaf); +void cpuid_refresh_range(struct cpuinfo_x86 *c, u32 start, u32 end); + #endif /* _ASM_X86_CPUID_API_H */ diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 2f8a58e..d2e8a84 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -19,6 +19,7 @@ KCSAN_SANITIZE_common.o :=3D n =20 obj-y :=3D cacheinfo.o scattered.o obj-y +=3D topology_common.o topology_ext.o topology_amd.o +obj-y +=3D cpuid_parser.o obj-y +=3D common.o obj-y +=3D rdrand.o obj-y +=3D match.o diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 897145f..a3df21d 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1784,6 +1784,7 @@ static void __init cpu_parse_early_param(void) static void __init early_identify_cpu(struct cpuinfo_x86 *c) { memset(&c->x86_capability, 0, sizeof(c->x86_capability)); + memset(&c->cpuid, 0, sizeof(c->cpuid)); c->extended_cpuid_level =3D 0; =20 if (!cpuid_feature()) @@ -1791,6 +1792,7 @@ static void __init early_identify_cpu(struct cpuinfo_= x86 *c) =20 /* cyrix could have cpuid enabled via c_identify()*/ if (cpuid_feature()) { + cpuid_scan_cpu(c); cpu_detect(c); get_cpu_vendor(c); intel_unlock_cpuid_leafs(c); @@ -1963,8 +1965,8 @@ static void generic_identify(struct cpuinfo_x86 *c) if (!cpuid_feature()) return; =20 + cpuid_scan_cpu(c); cpu_detect(c); - get_cpu_vendor(c); intel_unlock_cpuid_leafs(c); get_cpu_cap(c); @@ -2016,6 +2018,7 @@ static void identify_cpu(struct cpuinfo_x86 *c) #endif c->x86_cache_alignment =3D c->x86_clflush_size; memset(&c->x86_capability, 0, sizeof(c->x86_capability)); + memset(&c->cpuid, 0, sizeof(c->cpuid)); #ifdef CONFIG_X86_VMX_FEATURE_NAMES memset(&c->vmx_capability, 0, sizeof(c->vmx_capability)); #endif diff --git a/arch/x86/kernel/cpu/cpuid_parser.c b/arch/x86/kernel/cpu/cpuid= _parser.c new file mode 100644 index 0000000..898b0c4 --- /dev/null +++ b/arch/x86/kernel/cpu/cpuid_parser.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CPUID parser; for populating the system's CPUID tables. + */ + +#include + +#include +#include + +#include "cpuid_parser.h" + +/* Clear a single CPUID table entry */ +static void cpuid_clear(const struct cpuid_parse_entry *e, const struct cp= uid_read_output *output) +{ + struct cpuid_regs *regs =3D output->regs; + + for (int i =3D 0; i < e->maxcnt; i++, regs++) + memset(regs, 0, sizeof(*regs)); + + memset(output->info, 0, sizeof(*output->info)); +} + +/* + * Leaf read functions: + */ + +/* + * Default CPUID read function + * Satisfies the requirements stated at 'struct cpuid_parse_entry'->read(). + */ +static void +cpuid_read_generic(const struct cpuid_parse_entry *e, const struct cpuid_r= ead_output *output) +{ + struct cpuid_regs *regs =3D output->regs; + + for (int i =3D 0; i < e->maxcnt; i++, regs++, output->info->nr_entries++) + cpuid_read_subleaf(e->leaf, e->subleaf + i, regs); +} + +/* + * CPUID parser table: + */ + +static const struct cpuid_parse_entry cpuid_parse_entries[] =3D { + CPUID_PARSE_ENTRIES +}; + +/* + * Leaf-independent parser code: + */ + +static unsigned int cpuid_range_max_leaf(const struct cpuid_table *t, unsi= gned int range) +{ + const struct leaf_0x0_0 *l0 =3D __cpuid_table_subleaf(t, 0x0, 0); + + switch (range) { + case CPUID_BASE_START: return l0 ? l0->max_std_leaf : 0; + default: return 0; + } +} + +static void +__cpuid_reset_table(struct cpuid_table *t, const struct cpuid_parse_entry = entries[], + unsigned int nr_entries, unsigned int start, unsigned int end, bool = fill) +{ + const struct cpuid_parse_entry *entry =3D entries; + unsigned int range =3D CPUID_RANGE(start); + + for (unsigned int i =3D 0; i < nr_entries; i++, entry++) { + struct cpuid_read_output output =3D { + .regs =3D cpuid_table_regs_p(t, entry->regs_offs), + .info =3D cpuid_table_info_p(t, entry->info_offs), + }; + + if (entry->leaf < start || entry->leaf > end) + continue; + + cpuid_clear(entry, &output); + + /* + * Read the range's anchor leaf unconditionally so that the cached + * maximum valid leaf value is available for the remaining entries. + */ + if (fill && (entry->leaf =3D=3D range || entry->leaf <=3D cpuid_range_ma= x_leaf(t, range))) + entry->read(entry, &output); + } +} + +/* + * Zero all cached CPUID entries within [@start-@end] range. This is need= ed when + * certain operations like MSR writes induce changes to the CPU's CPUID la= yout. + */ +static void +__cpuid_zero_table(struct cpuid_table *t, const struct cpuid_parse_entry e= ntries[], + unsigned int nr_entries, unsigned int start, unsigned int end) +{ + __cpuid_reset_table(t, entries, nr_entries, start, end, false); +} + +static void +__cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry e= ntries[], + unsigned int nr_entries, unsigned int start, unsigned int end) +{ + __cpuid_reset_table(t, entries, nr_entries, start, end, true); +} + +static void +cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry ent= ries[], unsigned int nr_entries) +{ + static const struct { + unsigned int start; + unsigned int end; + } ranges[] =3D { + { CPUID_BASE_START, CPUID_BASE_END }, + }; + + for (unsigned int i =3D 0; i < ARRAY_SIZE(ranges); i++) + __cpuid_fill_table(t, entries, nr_entries, ranges[i].start, ranges[i].en= d); +} + +static void __cpuid_scan_cpu_full(struct cpuinfo_x86 *c) +{ + unsigned int nr_entries =3D ARRAY_SIZE(cpuid_parse_entries); + struct cpuid_table *table =3D &c->cpuid; + + cpuid_fill_table(table, cpuid_parse_entries, nr_entries); +} + +static void +__cpuid_scan_cpu_partial(struct cpuinfo_x86 *c, unsigned int start_leaf, u= nsigned int end_leaf) +{ + unsigned int nr_entries =3D ARRAY_SIZE(cpuid_parse_entries); + struct cpuid_table *table =3D &c->cpuid; + + __cpuid_zero_table(table, cpuid_parse_entries, nr_entries, start_leaf, en= d_leaf); + __cpuid_fill_table(table, cpuid_parse_entries, nr_entries, start_leaf, en= d_leaf); +} + +/* + * Call-site APIs: + */ + +/** + * cpuid_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. Al= l CPUID + * instructions are invoked locally, so this must be called on the CPU ass= ociated + * with @c. + */ +void cpuid_scan_cpu(struct cpuinfo_x86 *c) +{ + __cpuid_scan_cpu_full(c); +} + +/** + * cpuid_refresh_range() - Rescan a CPUID table's leaf range + * @c: CPU capability structure associated with the current CPU + * @start: Start of leaf range to be re-scanned + * @end: End of leaf range + */ +void cpuid_refresh_range(struct cpuinfo_x86 *c, u32 start, u32 end) +{ + if (WARN_ON_ONCE(start > end)) + return; + + if (WARN_ON_ONCE(CPUID_RANGE(start) !=3D CPUID_RANGE(end))) + return; + + __cpuid_scan_cpu_partial(c, start, end); +} + +/** + * cpuid_refresh_leaf() - Rescan a CPUID table's leaf + * @c: CPU capability structure associated with the current CPU + * @leaf: Leaf to be re-scanned + */ +void cpuid_refresh_leaf(struct cpuinfo_x86 *c, u32 leaf) +{ + cpuid_refresh_range(c, leaf, leaf); +} diff --git a/arch/x86/kernel/cpu/cpuid_parser.h b/arch/x86/kernel/cpu/cpuid= _parser.h new file mode 100644 index 0000000..df62730 --- /dev/null +++ b/arch/x86/kernel/cpu/cpuid_parser.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ARCH_X86_CPUID_PARSER_H +#define _ARCH_X86_CPUID_PARSER_H + +#include + +/* + * Since accessing the CPUID leaves at 'struct cpuid_leaves' require compi= le time + * tokenization, split the CPUID parser into two stages: compile time macr= os for + * tokenizing the leaf/subleaf output offsets within the table, and generi= c runtime + * code to write to the relevant CPUID leaves using such offsets. + * + * The output of the compile time macros is cached by a compile time "pars= e entry" + * table (see 'struct cpuid_parse_entry'). The runtime parser code will u= tilize + * such offsets by passing them to the cpuid_table_*_p() functions. + */ + +/* + * Compile time CPUID table offset calculations: + * + * @_leaf: CPUID leaf, in 0xN format + * @_subleaf: CPUID subleaf, in decimal format + */ + +#define __cpuid_leaves_regs_offset(_leaf, _subleaf) \ + offsetof(struct cpuid_leaves, leaf_ ## _leaf ## _ ## _subleaf) + +#define __cpuid_leaves_info_offset(_leaf, _subleaf) \ + offsetof(struct cpuid_leaves, leaf_ ## _leaf ## _ ## _subleaf ## _ ## inf= o) + +#define __cpuid_leaves_regs_maxcnt(_leaf, _subleaf) \ + ARRAY_SIZE(((struct cpuid_leaves *)NULL)->leaf_ ## _leaf ## _ ## _subleaf) + +/* + * Translation of compile time offsets to generic runtime pointers: + */ + +static inline struct cpuid_regs * +cpuid_table_regs_p(const struct cpuid_table *t, unsigned long regs_offset) +{ + return (struct cpuid_regs *)((unsigned long)(&t->leaves) + regs_offset); +} + +static inline struct leaf_parse_info * +cpuid_table_info_p(const struct cpuid_table *t, unsigned long info_offset) +{ + return (struct leaf_parse_info *)((unsigned long)(&t->leaves) + info_offs= et); +} + +/** + * struct cpuid_read_output - Output of a CPUID read operation + * @regs: Pointer to an array of CPUID outputs, where each array element c= overs the + * full EAX->EDX output range. + * @info: Pointer to query info; for saving the number of filled elements = at @regs. + * + * A CPUID parser read function like cpuid_read_generic() or cpuid_read_0x= N() uses this + * structure to save the CPUID query outputs. Actual storage for @regs an= d @info is + * provided by the read function caller, and is typically within the CPU's= CPUID table. + * + * See struct cpuid_parse_entry.read(). + */ +struct cpuid_read_output { + struct cpuid_regs *regs; + struct leaf_parse_info *info; +}; + +/** + * struct cpuid_parse_entry - CPUID parse table entry + * @leaf: Leaf number to be parsed + * @subleaf: Subleaf number to be parsed + * @regs_offs: Offset within 'struct cpuid_leaves' for saving the CPUID qu= ery output; to be + * passed to cpuid_table_regs_p(). + * @info_offs: Offset within 'struct cpuid_leaves' for saving the CPUID qu= ery parse info; to be + * passed to cpuid_table_info_p(). + * @maxcnt: Maximum number of output storage entries available for the CPU= ID query. + * @read: Read function for this entry. It must save the parsed CPUID out= put to the passed + * 'struct cpuid_read_output'->regs array of size >=3D @maxcnt. It must = set + * 'struct cpuid_read_output'->info.nr_entries to the number of CPUID out= put entries + * parsed and filled. A generic implementation is provided at cpuid_read= _generic(). + */ +struct cpuid_parse_entry { + unsigned int leaf; + unsigned int subleaf; + unsigned int regs_offs; + unsigned int info_offs; + unsigned int maxcnt; + void (*read)(const struct cpuid_parse_entry *e, const struct cpuid_read_= output *o); +}; + +#define __CPUID_PARSE_ENTRY(_leaf, _subleaf, _suffix, _reader_fn) \ + { \ + .leaf =3D _leaf, \ + .subleaf =3D _subleaf, \ + .regs_offs =3D __cpuid_leaves_regs_offset(_leaf, _suffix), \ + .info_offs =3D __cpuid_leaves_info_offset(_leaf, _suffix), \ + .maxcnt =3D __cpuid_leaves_regs_maxcnt(_leaf, _suffix), \ + .read =3D cpuid_read_ ## _reader_fn, \ + } + +/* + * CPUID_PARSE_ENTRY_N() is for parsing CPUID leaves with a subleaf range. + * Check __CPUID_LEAF() vs. CPUID_LEAF_N(). + */ + +#define CPUID_PARSE_ENTRY(_leaf, _subleaf, _reader_fn) \ + __CPUID_PARSE_ENTRY(_leaf, _subleaf, _subleaf, _reader_fn) + +#define CPUID_PARSE_ENTRY_N(_leaf, _reader_fn) \ + __CPUID_PARSE_ENTRY(_leaf, __cpuid_leaf_first_subleaf(_leaf), n, _reader_= fn) + +/* + * CPUID parser table: + */ + +#define CPUID_PARSE_ENTRIES \ + /* Leaf Subleaf Reader function */ \ + CPUID_PARSE_ENTRY ( 0x0, 0, generic ), \ + CPUID_PARSE_ENTRY ( 0x1, 0, generic ), \ + +#endif /* _ARCH_X86_CPUID_PARSER_H */ diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 23b91bf..cf061ed 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -17,7 +17,7 @@ #include #include #include -#include =20 +#include #include =20 #include "xen-ops.h" @@ -76,6 +76,7 @@ unsigned long xen_released_pages; static __ref void xen_get_vendor(void) { init_cpu_devs(); + cpuid_scan_cpu(&boot_cpu_data); cpu_detect(&boot_cpu_data); get_cpu_vendor(&boot_cpu_data); } diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index ed2d7a3..223e9a2 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -1429,6 +1429,7 @@ asmlinkage __visible void __init xen_start_kernel(str= uct start_info *si) xen_build_dynamic_phys_to_machine(); =20 /* Work out if we support NX */ + cpuid_scan_cpu(&boot_cpu_data); get_cpu_cap(&boot_cpu_data); x86_configure_nx(); =20