From nobody Thu Apr 2 20:28:10 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 45ED233120C for ; Fri, 27 Mar 2026 02:18:38 +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=1774577919; cv=none; b=Rf/xPn1k6snmpOukZOgVwdmvQKqKbOQg8aNJSDAo6RDW6kvbWZshYSvI3WZOiNDiM4lD1VzIvLjQCZv7niBg3QuBI57k057nmKACXgtJ7xq3yETmrB9rq6d600uPvqYn7jVXn32Sc/dQoHP5ImUBLKXy74i2Fr0VvzNGPWTWkUo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774577919; c=relaxed/simple; bh=3xZcFlNNGXIxppv/7LGQ6GIY6Fg/4a2a1lNmj5YQ+Pc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WBwI+oeQ2Tw8RO+p+sbLfJaJMp0JV5MAwhoiFTisXdZRKaL6UZzNxR77YfWKtK5mLjcCVqjL0p5yROo8FT8rXL9p57rdyWAo7BgIw0pD/KUpV28em2Ppe+hoKeThPUY1HXBby70bHb5k9oX28DM753gQTyfEvNImC40E94Sb+MQ= 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=xtKlO0re; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=n5Wi17bo; 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="xtKlO0re"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="n5Wi17bo" From: "Ahmed S. Darwish" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1774577917; 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=IfLCDe+ZR0T/yqyn7NUJ0n4OAIa+SwuPIoernc/hbrI=; b=xtKlO0reTVktHzPguJgKS9b6WvVPuX2/QMK6iruUliJRjfAdJdOuicUmlFdxc/9GnJ52hx 5yGhQRFIPH3ldOGbrwvYcceMX6nBCrNjPY+HPY/Er4yfWbJAwsBEp3muMCFnncGdClp+V+ f6Fe1ERVeUSAeb1zBwhMWVgLLu7Ys989jZr8uzyKe/2dbIw0WI7hIdRakJIJoaSBwrl1vW uwjp4uV51w5n/cc45/feToz7koIOCNvbLuvrLIcHW0YOrFTFhJEr4DWdkCEByc7P13tnPS cD9q1BaUSq3A+BialgEVO0kRu/udPJX1Z6UOaOf5Vkr8yTVbqj+XHYtZfoxBDg== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1774577917; 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=IfLCDe+ZR0T/yqyn7NUJ0n4OAIa+SwuPIoernc/hbrI=; b=n5Wi17bo56KZqgq6TY0K6PKjc7a5HwXcD7eyTvLnff2eHfMsgKX5H40U1RcHibIjfAur8H DB14rQfDc5VE/qCg== To: Borislav Petkov , Dave Hansen , Ingo Molnar Cc: Thomas Gleixner , Andrew Cooper , "H. Peter Anvin" , Sean Christopherson , David Woodhouse , Peter Zijlstra , Christian Ludloff , Sohil Mehta , John Ogness , x86@kernel.org, x86-cpuid@lists.linux.dev, LKML , "Ahmed S. Darwish" Subject: [PATCH v6 22/90] x86/cpuid: Introduce a parser debugfs interface Date: Fri, 27 Mar 2026 03:15:36 +0100 Message-ID: <20260327021645.555257-23-darwi@linutronix.de> In-Reply-To: <20260327021645.555257-1-darwi@linutronix.de> References: <20260327021645.555257-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" Introduce the debugfs files "x86/cpuid/[0-ncpus]" to dump each CPU's cached CPUID table. For each cached leaf/subleaf, invoke the CPUID instruction on the target CPU and compare the hardware result against its cached values. Mark any mismatched cached CPUID output value with an asterisk. This should help with tricky bug reports in the future if the cached CPUID data get unexpectedly out of sync with actual hardware state. Note, expose cpuid_phases[] via "cpuid_parser.h" to allow the debugfs code to traverse and dump parsed CPUID data. Note, this debugfs interface also simplifies the development and testing of adding new leaves to the CPUID parser. Signed-off-by: Ahmed S. Darwish --- arch/x86/kernel/cpu/Makefile | 2 +- arch/x86/kernel/cpu/cpuid_debugfs.c | 108 ++++++++++++++++++++++++++++ arch/x86/kernel/cpu/cpuid_parser.c | 9 ++- arch/x86/kernel/cpu/cpuid_parser.h | 12 ++++ 4 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 arch/x86/kernel/cpu/cpuid_debugfs.c diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index d2e8a849f180..d62e2d60a965 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -62,7 +62,7 @@ obj-$(CONFIG_HYPERVISOR_GUEST) +=3D vmware.o hypervisor.= o mshyperv.o obj-$(CONFIG_BHYVE_GUEST) +=3D bhyve.o obj-$(CONFIG_ACRN_GUEST) +=3D acrn.o =20 -obj-$(CONFIG_DEBUG_FS) +=3D debugfs.o +obj-$(CONFIG_DEBUG_FS) +=3D debugfs.o cpuid_debugfs.o =20 obj-$(CONFIG_X86_BUS_LOCK_DETECT) +=3D bus_lock.o =20 diff --git a/arch/x86/kernel/cpu/cpuid_debugfs.c b/arch/x86/kernel/cpu/cpui= d_debugfs.c new file mode 100644 index 000000000000..4bd874bffffc --- /dev/null +++ b/arch/x86/kernel/cpu/cpuid_debugfs.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CPUID parser debugfs entries: x86/cpuid/[0-ncpus] + * + * Dump each CPU's cached CPUID table and compare its values against curre= nt + * CPUID output on that CPU. Mark changed entries with an asterisk. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "cpuid_parser.h" + +static void cpuid_this_cpu(void *info) +{ + struct cpuid_regs *regs =3D info; + + __cpuid(®s->eax, ®s->ebx, ®s->ecx, ®s->edx); +} + +static void +cpuid_show_leaf(struct seq_file *m, uintptr_t cpu_id, const struct cpuid_p= arse_entry *entry, + const struct leaf_parse_info *info, const struct cpuid_regs *cached) +{ + for (int j =3D 0; j < info->nr_entries; j++) { + u32 subleaf =3D entry->subleaf + j; + struct cpuid_regs regs =3D { + .eax =3D entry->leaf, + .ecx =3D subleaf, + }; + int ret; + + seq_printf(m, "Leaf 0x%08x, subleaf %u:\n", entry->leaf, subleaf); + + ret =3D smp_call_function_single(cpu_id, cpuid_this_cpu, ®s, true); + if (ret) { + seq_printf(m, "Failed to invoke CPUID on CPU %lu: %d\n\n", cpu_id, ret); + continue; + } + + seq_printf(m, " cached: %cEAX=3D0x%08x %cEBX=3D0x%08x %cECX=3D0x%= 08x %cEDX=3D0x%08x\n", + cached[j].eax =3D=3D regs.eax ? ' ' : '*', cached[j].eax, + cached[j].ebx =3D=3D regs.ebx ? ' ' : '*', cached[j].ebx, + cached[j].ecx =3D=3D regs.ecx ? ' ' : '*', cached[j].ecx, + cached[j].edx =3D=3D regs.edx ? ' ' : '*', cached[j].edx); + seq_printf(m, " actual: EAX=3D0x%08x EBX=3D0x%08x ECX=3D0x%08x= EDX=3D0x%08x\n", + regs.eax, regs.ebx, regs.ecx, regs.edx); + } +} + +static void __cpuid_debug_show(struct seq_file *m, uintptr_t cpu_id, + const struct cpuid_parse_entry *entry, int nr_entries) +{ + const struct cpuinfo_x86 *c =3D per_cpu_ptr(&cpu_info, cpu_id); + const struct cpuid_table *t =3D &c->cpuid; + + for (int i =3D 0; i < nr_entries; i++, entry++) { + const struct leaf_parse_info *qi =3D cpuid_table_info_p(t, entry->info_o= ffs); + const struct cpuid_regs *qr =3D cpuid_table_regs_p(t, entry->regs_offs); + + cpuid_show_leaf(m, cpu_id, entry, qi, qr); + } +} + +static int cpuid_debug_show(struct seq_file *m, void *p) +{ + uintptr_t cpu_id =3D (uintptr_t)m->private; + + for (int i =3D 0; i < cpuid_nphases; i++) + __cpuid_debug_show(m, cpu_id, cpuid_phases[i].table, cpuid_phases[i].nr_= entries); + + return 0; +} + +static int cpuid_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, cpuid_debug_show, inode->i_private); +} + +static const struct file_operations cpuid_ops =3D { + .open =3D cpuid_debug_open, + .read =3D seq_read, + .llseek =3D seq_lseek, + .release =3D single_release, +}; + +static __init int cpuid_init_debugfs(void) +{ + struct dentry *dir; + uintptr_t cpu_id; + char cpu_name[24]; + + dir =3D debugfs_create_dir("cpuid", arch_debugfs_dir); + + for_each_possible_cpu(cpu_id) { + scnprintf(cpu_name, sizeof(cpu_name), "%lu", cpu_id); + debugfs_create_file(cpu_name, 0444, dir, (void *)cpu_id, &cpuid_ops); + } + + return 0; +} +late_initcall(cpuid_init_debugfs); diff --git a/arch/x86/kernel/cpu/cpuid_parser.c b/arch/x86/kernel/cpu/cpuid= _parser.c index 97b7f296df03..ab736f03051e 100644 --- a/arch/x86/kernel/cpu/cpuid_parser.c +++ b/arch/x86/kernel/cpu/cpuid_parser.c @@ -74,14 +74,13 @@ static const struct cpuid_parse_entry cpuid_common_entr= ies[] =3D { CPUID_COMMON_ENTRIES }; =20 -static const struct { - const struct cpuid_parse_entry *table; - int nr_entries; -} cpuid_phases[] =3D { +const struct cpuid_phase cpuid_phases[] =3D { { cpuid_early_entries, ARRAY_SIZE(cpuid_early_entries) }, { cpuid_common_entries, ARRAY_SIZE(cpuid_common_entries) }, }; =20 +const int cpuid_nphases =3D ARRAY_SIZE(cpuid_phases); + /* * Leaf-independent parser code: */ @@ -189,7 +188,7 @@ cpuid_fill_table(struct cpuid_table *t, const struct cp= uid_parse_entry entries[] =20 static void __cpuid_scan_cpu_full(struct cpuinfo_x86 *c, bool early_boot) { - int nphases =3D early_boot ? 1 : ARRAY_SIZE(cpuid_phases); + int nphases =3D early_boot ? 1 : cpuid_nphases; struct cpuid_table *table =3D &c->cpuid; =20 for (int i =3D 0; i < nphases; i++) diff --git a/arch/x86/kernel/cpu/cpuid_parser.h b/arch/x86/kernel/cpu/cpuid= _parser.h index a3f7dcc6c03f..8b0d44b745c5 100644 --- a/arch/x86/kernel/cpu/cpuid_parser.h +++ b/arch/x86/kernel/cpu/cpuid_parser.h @@ -149,6 +149,18 @@ struct cpuid_parse_entry { CPUID_PARSE_ENTRY ( 0x80000003, 0, generic ), \ CPUID_PARSE_ENTRY ( 0x80000004, 0, generic ), \ =20 +/* + * CPUID parser phases: + */ + +struct cpuid_phase { + const struct cpuid_parse_entry *table; + int nr_entries; +}; + +extern const struct cpuid_phase cpuid_phases[]; +extern const int cpuid_nphases; + /* * CPUID leaf vendor table: */ --=20 2.53.0