[PATCH v1 0/5] x86/cpu: Refactor identify_cpu()

2 views
Skip to first unread message

Ihor Solodrai

unread,
Jul 3, 2026, 8:21:09 PMJul 3
to Borislav Petkov, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
This series refactors identify_cpu() machinery on x86 and then
implements a fix for a bug when cpu capabilities are temporarily
cleared within an interruptable window. For more details and
discussion see the relevant bug report [1].

Suggested-by: Borislav Petkov <b...@alien8.de>
Signed-off-by: Ihor Solodrai <ihor.s...@linux.dev>

[1] https://lore.kernel.org/bpf/20260610175651.647...@linux.dev/

Ihor Solodrai (5):
x86/cpu: Factor init_cpu_info() out of identify_cpu()
x86/cpu: Inline generic_identify() into identify_cpu()
x86/cpu: Introduce identify_cpu_32() helper
x86/cpu: Set X86_BUG_ESPFIX in identify_cpu_32()
x86/cpu: Don't reset boot CPU cpuinfo in identify_cpu()

arch/x86/kernel/cpu/common.c | 114 +++++++++++++++++++----------------
1 file changed, 61 insertions(+), 53 deletions(-)

--
2.54.0

Ihor Solodrai

unread,
Jul 3, 2026, 8:21:14 PMJul 3
to Borislav Petkov, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
identify_cpu() begins by resetting the struct cpuinfo_x86 fields to
their unknown/default values and clearing the capability arrays with
memset().

Move that reset into a new helper: init_cpu_info(). This is
preparation for letting the callers decide whether the reset is
needed.

Signed-off-by: Ihor Solodrai <ihor.s...@linux.dev>
---
arch/x86/kernel/cpu/common.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index a3df21d26460..505ce329e821 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1993,14 +1993,8 @@ static void generic_identify(struct cpuinfo_x86 *c)
#endif
}

-/*
- * This does the hard work of actually picking apart the CPU stuff...
- */
-static void identify_cpu(struct cpuinfo_x86 *c)
+static void init_cpu_info(struct cpuinfo_x86 *c)
{
- int i;
-
- c->loops_per_jiffy = loops_per_jiffy;
c->x86_cache_size = 0;
c->x86_vendor = X86_VENDOR_UNKNOWN;
c->x86_model = c->x86_stepping = 0; /* So far unknown... */
@@ -2022,6 +2016,15 @@ static void identify_cpu(struct cpuinfo_x86 *c)
#ifdef CONFIG_X86_VMX_FEATURE_NAMES
memset(&c->vmx_capability, 0, sizeof(c->vmx_capability));
#endif
+}
+
+static void identify_cpu(struct cpuinfo_x86 *c)
+{
+ int i;
+
+ c->loops_per_jiffy = loops_per_jiffy;
+
+ init_cpu_info(c);

generic_identify(c);

--
2.54.0

Ihor Solodrai

unread,
Jul 3, 2026, 8:21:17 PMJul 3
to Borislav Petkov, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
generic_identify() is only ever called from identify_cpu(),
immediately after the cpuinfo reset. Fold it into identify_cpu() so
that a single function does the job for both the boot CPU and the
secondary CPUs.

Signed-off-by: Ihor Solodrai <ihor.s...@linux.dev>
---
arch/x86/kernel/cpu/common.c | 73 +++++++++++++++++-------------------
1 file changed, 34 insertions(+), 39 deletions(-)

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 505ce329e821..b9a736728a0e 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1954,16 +1954,46 @@ void check_null_seg_clears_base(struct cpuinfo_x86 *c)
set_cpu_bug(c, X86_BUG_NULL_SEG);
}

-static void generic_identify(struct cpuinfo_x86 *c)
+static void init_cpu_info(struct cpuinfo_x86 *c)
{
+ c->x86_cache_size = 0;
+ c->x86_vendor = X86_VENDOR_UNKNOWN;
+ c->x86_model = c->x86_stepping = 0; /* So far unknown... */
+ c->x86_vendor_id[0] = '\0'; /* Unset */
+ c->x86_model_id[0] = '\0'; /* Unset */
+#ifdef CONFIG_X86_64
+ c->x86_clflush_size = 64;
+ c->x86_phys_bits = 36;
+ c->x86_virt_bits = 48;
+#else
+ c->cpuid_level = -1; /* CPUID not detected */
+ c->x86_clflush_size = 32;
+ c->x86_phys_bits = 32;
+ c->x86_virt_bits = 32;
+#endif
+ c->x86_cache_alignment = 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
c->extended_cpuid_level = 0;
+}
+
+static void identify_cpu(struct cpuinfo_x86 *c)
+{
+ int i;
+
+ c->loops_per_jiffy = loops_per_jiffy;
+
+ init_cpu_info(c);

if (!cpuid_feature())
identify_cpu_without_cpuid(c);

- /* cyrix could have cpuid enabled via c_identify()*/
+ /* cyrix could have cpuid enabled via c_identify() */
if (!cpuid_feature())
- return;
+ goto no_cpuid;

cpuid_scan_cpu(c);
cpu_detect(c);
@@ -1991,43 +2021,8 @@ static void generic_identify(struct cpuinfo_x86 *c)
#ifdef CONFIG_X86_32
set_cpu_bug(c, X86_BUG_ESPFIX);
#endif
-}
-
-static void init_cpu_info(struct cpuinfo_x86 *c)
-{
- c->x86_cache_size = 0;
- c->x86_vendor = X86_VENDOR_UNKNOWN;
- c->x86_model = c->x86_stepping = 0; /* So far unknown... */
- c->x86_vendor_id[0] = '\0'; /* Unset */
- c->x86_model_id[0] = '\0'; /* Unset */
-#ifdef CONFIG_X86_64
- c->x86_clflush_size = 64;
- c->x86_phys_bits = 36;
- c->x86_virt_bits = 48;
-#else
- c->cpuid_level = -1; /* CPUID not detected */
- c->x86_clflush_size = 32;
- c->x86_phys_bits = 32;
- c->x86_virt_bits = 32;
-#endif
- c->x86_cache_alignment = 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
-}
-
-static void identify_cpu(struct cpuinfo_x86 *c)
-{
- int i;
-
- c->loops_per_jiffy = loops_per_jiffy;
-
- init_cpu_info(c);
-
- generic_identify(c);

+no_cpuid:
cpu_parse_topology(c);

if (this_cpu->c_identify)
--
2.54.0

Ihor Solodrai

unread,
Jul 3, 2026, 8:21:20 PMJul 3
to Borislav Petkov, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
Both identify_boot_cpu() and identify_secondary_cpu() call enable_sep_cpu()
under a CONFIG_X86_32 #ifdef. Move that into a new identify_cpu_32() helper
guarded by IS_ENABLED(CONFIG_X86_32).

Signed-off-by: Ihor Solodrai <ihor.s...@linux.dev>
---
arch/x86/kernel/cpu/common.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index b9a736728a0e..519e495c4a14 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -2146,14 +2146,20 @@ void enable_sep_cpu(void)
}
#endif

+static void identify_cpu_32(struct cpuinfo_x86 *c)
+{
+ if (!IS_ENABLED(CONFIG_X86_32))
+ return;
+
+ enable_sep_cpu();
+}
+
static __init void identify_boot_cpu(void)
{
identify_cpu(&boot_cpu_data);
if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
pr_info("CET detected: Indirect Branch Tracking enabled\n");
-#ifdef CONFIG_X86_32
- enable_sep_cpu();
-#endif
+ identify_cpu_32(&boot_cpu_data);
cpu_detect_tlb(&boot_cpu_data);
setup_cr_pinning();

@@ -2173,9 +2179,7 @@ void identify_secondary_cpu(unsigned int cpu)
c->cpu_index = cpu;

identify_cpu(c);
-#ifdef CONFIG_X86_32
- enable_sep_cpu();
-#endif
+ identify_cpu_32(c);
x86_spec_ctrl_setup_ap();
update_srbds_msr();
if (boot_cpu_has_bug(X86_BUG_GDS))
--
2.54.0

Ihor Solodrai

unread,
Jul 3, 2026, 8:21:23 PMJul 3
to Borislav Petkov, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
X86_BUG_ESPFIX is 32-bit-only. Move setting it into
identify_cpu_32(), next to the other 32-bit-only setup.

Signed-off-by: Ihor Solodrai <ihor.s...@linux.dev>
---
arch/x86/kernel/cpu/common.c | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 519e495c4a14..8f093c6c7ccc 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -2005,23 +2005,6 @@ static void identify_cpu(struct cpuinfo_x86 *c)

get_model_name(c); /* Default name */

- /*
- * ESPFIX is a strange bug. All real CPUs have it. Paravirt
- * systems that run Linux at CPL > 0 may or may not have the
- * issue, but, even if they have the issue, there's absolutely
- * nothing we can do about it because we can't use the real IRET
- * instruction.
- *
- * NB: For the time being, only 32-bit kernels support
- * X86_BUG_ESPFIX as such. 64-bit kernels directly choose
- * whether to apply espfix using paravirt hooks. If any
- * non-paravirt system ever shows up that does *not* have the
- * ESPFIX issue, we can change this.
- */
-#ifdef CONFIG_X86_32
- set_cpu_bug(c, X86_BUG_ESPFIX);
-#endif
-
no_cpuid:
cpu_parse_topology(c);

@@ -2151,6 +2134,23 @@ static void identify_cpu_32(struct cpuinfo_x86 *c)
if (!IS_ENABLED(CONFIG_X86_32))
return;

+ /*
+ * ESPFIX is a strange bug. All real CPUs have it. Paravirt
+ * systems that run Linux at CPL > 0 may or may not have the
+ * issue, but, even if they have the issue, there's absolutely
+ * nothing we can do about it because we can't use the real IRET
+ * instruction.
+ *
+ * NB: For the time being, only 32-bit kernels support
+ * X86_BUG_ESPFIX as such. 64-bit kernels directly choose
+ * whether to apply espfix using paravirt hooks. If any
+ * non-paravirt system ever shows up that does *not* have the
+ * ESPFIX issue, we can change this.
+ */
+#ifdef CONFIG_X86_32
+ set_cpu_bug(c, X86_BUG_ESPFIX);
+#endif
+
enable_sep_cpu();
}

--
2.54.0

Ihor Solodrai

unread,
Jul 3, 2026, 8:21:27 PMJul 3
to Borislav Petkov, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
identify_cpu() used to rebuild c->x86_capability from scratch:
init_cpu_info() memsets the capability array to zero and
identify_cpu() then re-reads CPUID. On the boot CPU identify_cpu()
runs after local_irq_enable() and before alternatives are patched. An
interrupt delivered in the window between the memset() and
get_cpu_cap() therefore will observe cleared x86_capability [1].

However the boot CPU's capabilities have already been scanned, with
interrupts off, by early_identify_cpu(). So move the init_cpu_info()
call out of identify_cpu() into its callers, and skip it for the boot
CPU on 64-bit.

Secondary CPUs call init_cpu_info() then identify_cpu() from
identify_secondary_cpu(), with interrupts off, exactly as
before. 32-bit still resets in identify_boot_cpu(): it relies on the
reset for the no-CPUID cpuid_level default.

[1] https://lore.kernel.org/bpf/20260610175651.647...@linux.dev/

Signed-off-by: Ihor Solodrai <ihor.s...@linux.dev>
---
arch/x86/kernel/cpu/common.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 8f093c6c7ccc..cd3d7c37c174 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1986,8 +1986,6 @@ static void identify_cpu(struct cpuinfo_x86 *c)

c->loops_per_jiffy = loops_per_jiffy;

- init_cpu_info(c);
-
if (!cpuid_feature())
identify_cpu_without_cpuid(c);

@@ -2156,6 +2154,13 @@ static void identify_cpu_32(struct cpuinfo_x86 *c)

static __init void identify_boot_cpu(void)
{
+ /*
+ * The boot CPU's capabilities were already scanned in early_identify_cpu().
+ * However 32-bit still needs to init_cpu_info() here for the no-CPUID
+ * cpuid_level default.
+ */
+ if (IS_ENABLED(CONFIG_X86_32))
+ init_cpu_info(&boot_cpu_data);
identify_cpu(&boot_cpu_data);
if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
pr_info("CET detected: Indirect Branch Tracking enabled\n");
@@ -2178,6 +2183,7 @@ void identify_secondary_cpu(unsigned int cpu)
*c = boot_cpu_data;
c->cpu_index = cpu;

+ init_cpu_info(c);
identify_cpu(c);
identify_cpu_32(c);
x86_spec_ctrl_setup_ap();
--
2.54.0

Ihor Solodrai

unread,
1:24 PM (9 hours ago) 1:24 PM
to Borislav Petkov, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
On 7/3/26 5:20 PM, Ihor Solodrai wrote:
> This series refactors identify_cpu() machinery on x86 and then
> implements a fix for a bug when cpu capabilities are temporarily
> cleared within an interruptable window. For more details and
> discussion see the relevant bug report [1].
>
> Suggested-by: Borislav Petkov <b...@alien8.de>
> Signed-off-by: Ihor Solodrai <ihor.s...@linux.dev>
>
> [1] https://lore.kernel.org/bpf/20260610175651.647...@linux.dev/
>
> Ihor Solodrai (5):
> x86/cpu: Factor init_cpu_info() out of identify_cpu()
> x86/cpu: Inline generic_identify() into identify_cpu()
> x86/cpu: Introduce identify_cpu_32() helper
> x86/cpu: Set X86_BUG_ESPFIX in identify_cpu_32()
> x86/cpu: Don't reset boot CPU cpuinfo in identify_cpu()

Hi Boris, everyone.

Bumping the thread as it's been a couple of weeks.

Please take a look when you get a chance. Thanks!

Borislav Petkov

unread,
2:13 PM (8 hours ago) 2:13 PM
to Ihor Solodrai, Dave Hansen, Ingo Molnar, Thomas Gleixner, Alexei Starovoitov, Andrii Nakryiko, Andrey Ryabinin, Andrew Morton, H . Peter Anvin, Andrey Konovalov, b...@vger.kernel.org, kerne...@meta.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org
On Tue, Jul 21, 2026 at 10:23:25AM -0700, Ihor Solodrai wrote:
> Bumping the thread as it's been a couple of weeks.

You don't have to - it is on the TODO list.

> Please take a look when you get a chance. Thanks!

There's this thing called fires which need to be handled yesterday and we're
all drowning in them. So yeah, I know, but pls be patient.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Reply all
Reply to author
Forward
0 new messages