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: <20250605192356.82250-8-darwi@linutronix.de>
Date: Thu,  5 Jun 2025 21:23:36 +0200
From: "Ahmed S. Darwish" <darwi@...utronix.de>
To: Ingo Molnar <mingo@...hat.com>,
	Borislav Petkov <bp@...en8.de>,
	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>,
	Peter Zijlstra <peterz@...radead.org>,
	Sean Christopherson <seanjc@...gle.com>,
	Sohil Mehta <sohil.mehta@...el.com>,
	Ard Biesheuvel <ardb@...nel.org>,
	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 v2 07/27] x86/cpuid: Introduce CPUID parser debugfs interface

Introduce the debugfs files 'x86/cpuid/[0-ncpus]' to dump each CPU's
cached CPUID table.  For each cached CPUID leaf/subleaf, invoke the
CPUID instruction on the target CPU and compare the hardware result
against the cached values.

Mark any mismatched cached CPUID output value with an asterisk.  This
should help with tricky bug reports in the future, if/when the cached
CPUID tables get unexpectedly out of sync with actual hardware state.  It
also simplifies the development and testing of adding new CPUID leaves to
the CPUID parser.

Note, expose cpuid_common_parse_entries[] via "cpuid_parser.h" to allow
the debugfs code to traverse and dump the parsed CPUID data.

Signed-off-by: Ahmed S. Darwish <darwi@...utronix.de>
---
 arch/x86/kernel/cpu/Makefile        |   2 +-
 arch/x86/kernel/cpu/cpuid_debugfs.c | 102 ++++++++++++++++++++++++++++
 arch/x86/kernel/cpu/cpuid_parser.c  |   7 +-
 arch/x86/kernel/cpu/cpuid_parser.h  |   3 +
 4 files changed, 110 insertions(+), 4 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 b2421cfb59ed..4e032ad851c7 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -61,7 +61,7 @@ obj-$(CONFIG_X86_LOCAL_APIC)		+= perfctr-watchdog.o
 obj-$(CONFIG_HYPERVISOR_GUEST)		+= vmware.o hypervisor.o mshyperv.o
 obj-$(CONFIG_ACRN_GUEST)		+= acrn.o
 
-obj-$(CONFIG_DEBUG_FS)			+= debugfs.o
+obj-$(CONFIG_DEBUG_FS)			+= debugfs.o cpuid_debugfs.o
 
 obj-$(CONFIG_X86_BUS_LOCK_DETECT)	+= bus_lock.o
 
diff --git a/arch/x86/kernel/cpu/cpuid_debugfs.c b/arch/x86/kernel/cpu/cpuid_debugfs.c
new file mode 100644
index 000000000000..fd39305a3c99
--- /dev/null
+++ b/arch/x86/kernel/cpu/cpuid_debugfs.c
@@ -0,0 +1,102 @@
+// 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 current
+ * CPUID output on that CPU.  Mark changed entries with an asterisk.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+
+#include <asm/cpuid/api.h>
+#include <asm/percpu.h>
+#include <asm/processor.h>
+
+#include "cpuid_parser.h"
+
+static void cpuid_this_cpu(void *info)
+{
+	struct cpuid_regs *regs = info;
+
+	__cpuid(&regs->eax, &regs->ebx, &regs->ecx, &regs->edx);
+}
+
+static void
+cpuid_show_leaf(struct seq_file *m, uintptr_t cpu_id, const struct cpuid_parse_entry *entry,
+		const struct leaf_query_info *info, const struct cpuid_regs *cached)
+{
+	for (int j = 0; j < info->nr_entries; j++) {
+		u32 subleaf = entry->subleaf + j;
+		struct cpuid_regs regs = {
+			.eax = entry->leaf,
+			.ecx = subleaf,
+		};
+		int ret;
+
+		seq_printf(m, "Leaf 0x%08x, subleaf %u:\n", entry->leaf, subleaf);
+
+		ret = smp_call_function_single(cpu_id, cpuid_this_cpu, &regs, 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=0x%08x   %cEBX=0x%08x   %cECX=0x%08x   %cEDX=0x%08x\n",
+			   cached[j].eax == regs.eax ? ' ' : '*', cached[j].eax,
+			   cached[j].ebx == regs.ebx ? ' ' : '*', cached[j].ebx,
+			   cached[j].ecx == regs.ecx ? ' ' : '*', cached[j].ecx,
+			   cached[j].edx == regs.edx ? ' ' : '*', cached[j].edx);
+		seq_printf(m, "  actual:    EAX=0x%08x    EBX=0x%08x    ECX=0x%08x    EDX=0x%08x\n",
+			   regs.eax, regs.ebx, regs.ecx, regs.edx);
+	}
+}
+
+static int cpuid_debug_show(struct seq_file *m, void *p)
+{
+	uintptr_t cpu_id = (uintptr_t)m->private;
+	const struct cpuinfo_x86 *c = per_cpu_ptr(&cpu_info, cpu_id);
+
+	const struct cpuid_parse_entry *entry = cpuid_common_parse_entries;
+	const struct cpuid_leaves *leaves = &c->cpuid.leaves;
+
+	for (unsigned int i = 0; i < cpuid_common_parse_entries_size; i++, entry++) {
+		const struct leaf_query_info *qi = cpuid_leaves_query_info_p(leaves, entry->info_offs);
+		const struct cpuid_regs *qr = cpuid_leaves_query_regs_p(leaves, entry->regs_offs);
+
+		cpuid_show_leaf(m, cpu_id, entry, qi, qr);
+	}
+
+	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 = {
+	.open		= cpuid_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static __init int cpuid_init_debugfs(void)
+{
+	struct dentry *dir;
+	uintptr_t cpu_id;
+	char cpu_name[24];
+
+	dir = 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 731aab2ff9c0..e79835a09336 100644
--- a/arch/x86/kernel/cpu/cpuid_parser.c
+++ b/arch/x86/kernel/cpu/cpuid_parser.c
@@ -64,10 +64,12 @@ static bool cpuid_leaf_valid(const struct cpuid_leaves *l, unsigned int leaf)
 		cpuid_range_valid(l, leaf, CPUID_EXT_START, CPUID_EXT_END);
 }
 
-static const struct cpuid_parse_entry cpuid_common_parse_entries[] = {
+const struct cpuid_parse_entry cpuid_common_parse_entries[] = {
 	CPUID_PARSE_ENTRIES
 };
 
+const int cpuid_common_parse_entries_size = ARRAY_SIZE(cpuid_common_parse_entries);
+
 static void
 cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[], unsigned int nr_entries)
 {
@@ -95,6 +97,5 @@ cpuid_fill_table(struct cpuid_table *t, const struct cpuid_parse_entry entries[]
  */
 void cpuid_parser_scan_cpu(struct cpuinfo_x86 *c)
 {
-	cpuid_fill_table(&c->cpuid, cpuid_common_parse_entries,
-			 ARRAY_SIZE(cpuid_common_parse_entries));
+	cpuid_fill_table(&c->cpuid, cpuid_common_parse_entries, cpuid_common_parse_entries_size);
 }
diff --git a/arch/x86/kernel/cpu/cpuid_parser.h b/arch/x86/kernel/cpu/cpuid_parser.h
index f440948fc2fc..0c79919a6138 100644
--- a/arch/x86/kernel/cpu/cpuid_parser.h
+++ b/arch/x86/kernel/cpu/cpuid_parser.h
@@ -98,4 +98,7 @@ struct cpuid_parse_entry {
 	CPUID_PARSE_ENTRY(0x1,		0,		generic),			\
 	CPUID_PARSE_ENTRY(0x80000000,	0,		0x80000000),
 
+extern const struct cpuid_parse_entry cpuid_common_parse_entries[];
+extern const int cpuid_common_parse_entries_size;
+
 #endif /* _ARCH_X86_CPUID_PARSER_H */
-- 
2.49.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ