* [PATCH v7 1/3] x86/cpu: Clear feature bits disabled at compile-time
2026-02-24 12:38 [PATCH v7 0/3] x86: Capability bits fix and required bits sanity check Maciej Wieczor-Retman
@ 2026-02-24 12:40 ` Maciej Wieczor-Retman
2026-02-24 12:40 ` [PATCH v7 2/3] x86/cpu: Check if feature string is non-zero Maciej Wieczor-Retman
2026-02-24 12:40 ` [PATCH v7 3/3] x86/cpu: Do a sanity check on required feature bits Maciej Wieczor-Retman
2 siblings, 0 replies; 6+ messages in thread
From: Maciej Wieczor-Retman @ 2026-02-24 12:40 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: m.wieczorretman, Farrah Chen, Maciej Wieczor-Retman, stable,
linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
If some config options are disabled during compile time, they still are
enumerated in macros that use the x86_capability bitmask - cpu_has() or
this_cpu_has().
The features are also visible in /proc/cpuinfo even though they are not
enabled - which is contrary to what the documentation states about the
file. Examples of such feature flags are lam, fred, sgx, ibrs_enhanced,
split_lock_detect, user_shstk, avx_vnni and enqcmd.
Once the cpu_caps_cleared array is initialized with the autogenerated
disabled bitmask apply_forced_caps() will clear the corresponding bits
in boot_cpu_data.x86_capability[] and other secondary cpus'
cpu_data.x86_capability[]. Thus features disabled at compile time won't
show up in /proc/cpuinfo.
Reported-by: Farrah Chen <farrah.chen@intel.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220348
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Cc: <stable@vger.kernel.org> # 6.18.x
---
Changelog v6:
- Remove patch message portions that are not just describing the diff.
arch/x86/kernel/cpu/common.c | 3 ++-
arch/x86/tools/cpufeaturemasks.awk | 6 ++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 1c3261cae40c..9aa11224a038 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -732,7 +732,8 @@ static const char *table_lookup_model(struct cpuinfo_x86 *c)
}
/* Aligned to unsigned long to avoid split lock in atomic bitmap ops */
-__u32 cpu_caps_cleared[NCAPINTS + NBUGINTS] __aligned(sizeof(unsigned long));
+__u32 cpu_caps_cleared[NCAPINTS + NBUGINTS] __aligned(sizeof(unsigned long)) =
+ DISABLED_MASK_INITIALIZER;
__u32 cpu_caps_set[NCAPINTS + NBUGINTS] __aligned(sizeof(unsigned long));
#ifdef CONFIG_X86_32
diff --git a/arch/x86/tools/cpufeaturemasks.awk b/arch/x86/tools/cpufeaturemasks.awk
index 173d5bf2d999..b7f4e775a365 100755
--- a/arch/x86/tools/cpufeaturemasks.awk
+++ b/arch/x86/tools/cpufeaturemasks.awk
@@ -82,6 +82,12 @@ END {
}
printf " 0\t\\\n";
printf "\t) & (1U << ((x) & 31)))\n\n";
+
+ printf "\n#define %s_MASK_INITIALIZER\t\t\t\\", s;
+ printf "\n\t{\t\t\t\t\t\t\\";
+ for (i = 0; i < ncapints; i++)
+ printf "\n\t\t%s_MASK%d,\t\t\t\\", s, i;
+ printf "\n\t}\n\n";
}
printf "#endif /* _ASM_X86_CPUFEATUREMASKS_H */\n";
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v7 2/3] x86/cpu: Check if feature string is non-zero
2026-02-24 12:38 [PATCH v7 0/3] x86: Capability bits fix and required bits sanity check Maciej Wieczor-Retman
2026-02-24 12:40 ` [PATCH v7 1/3] x86/cpu: Clear feature bits disabled at compile-time Maciej Wieczor-Retman
@ 2026-02-24 12:40 ` Maciej Wieczor-Retman
2026-02-24 20:24 ` Thomas Gleixner
2026-02-24 12:40 ` [PATCH v7 3/3] x86/cpu: Do a sanity check on required feature bits Maciej Wieczor-Retman
2 siblings, 1 reply; 6+ messages in thread
From: Maciej Wieczor-Retman @ 2026-02-24 12:40 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Peter Zijlstra
Cc: m.wieczorretman, Maciej Wieczor-Retman, linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
In filter_cpuid_features, x86_cap_flags[] is read, but it's not verified
whether the string is non-zero which could lead to unwanted output.
In two more places there are open coded paths that try to retrieve a
feature string, and if there isn't one, the feature word and bit are
returned instead. While correcting filter_cpuid_features() with a helper
it's trivial to also clean up these open coded cases.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
Changelog v7:
- sizeof(buf) -> 16
- Rebase onto 7.01-rc1.
Changelog v6:
- Remove parts of the patch message that are redundant and just copy
what's visible in the diff.
- Redo the helper to use an external char buffer instead of a local
static string.
arch/x86/kernel/cpu/common.c | 26 +++++++++++++++++++++-----
arch/x86/kernel/cpu/cpuid-deps.c | 20 +++-----------------
include/linux/cpu.h | 2 ++
3 files changed, 26 insertions(+), 22 deletions(-)
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 9aa11224a038..b60269174d95 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -675,6 +675,7 @@ cpuid_dependent_features[] = {
static void filter_cpuid_features(struct cpuinfo_x86 *c, bool warn)
{
const struct cpuid_dependent_feature *df;
+ char feature_buf[16];
for (df = cpuid_dependent_features; df->feature; df++) {
@@ -697,7 +698,7 @@ static void filter_cpuid_features(struct cpuinfo_x86 *c, bool warn)
continue;
pr_warn("CPU: CPU feature %s disabled, no CPUID level 0x%x\n",
- x86_cap_flags[df->feature], df->level);
+ x86_cap_name(df->feature, feature_buf), df->level);
}
}
@@ -1634,6 +1635,7 @@ static inline bool parse_set_clear_cpuid(char *arg, bool set)
while (arg) {
bool found __maybe_unused = false;
+ char name_buf[16];
unsigned int bit;
opt = strsep(&arg, ",");
@@ -1654,10 +1656,7 @@ static inline bool parse_set_clear_cpuid(char *arg, bool set)
setup_clear_cpu_cap(bit);
}
/* empty-string, i.e., ""-defined feature flags */
- if (!x86_cap_flags[bit])
- pr_cont(" %d:%d\n", bit >> 5, bit & 31);
- else
- pr_cont(" %s\n", x86_cap_flags[bit]);
+ pr_cont(" %s\n", x86_cap_name(bit, name_buf));
taint++;
}
@@ -1980,6 +1979,23 @@ static void generic_identify(struct cpuinfo_x86 *c)
#endif
}
+const char *x86_cap_name(unsigned int bit, char *buf)
+{
+ unsigned int word = bit >> 5;
+ const char *name = NULL;
+
+ if (likely(word < NCAPINTS))
+ name = x86_cap_flags[bit];
+ else if (likely(word < NCAPINTS + NBUGINTS))
+ name = x86_bug_flags[bit - 32 * NCAPINTS];
+
+ if (name)
+ return name;
+
+ snprintf(buf, 16, "%u:%u", word, bit & 31);
+ return buf;
+}
+
/*
* This does the hard work of actually picking apart the CPU stuff...
*/
diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c
index 146f6f8b0650..7fad18e7d2eb 100644
--- a/arch/x86/kernel/cpu/cpuid-deps.c
+++ b/arch/x86/kernel/cpu/cpuid-deps.c
@@ -2,6 +2,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/cpu.h>
#include <asm/cpufeature.h>
struct cpuid_dep {
@@ -156,21 +157,6 @@ void setup_clear_cpu_cap(unsigned int feature)
do_clear_cpu_cap(NULL, feature);
}
-/*
- * Return the feature "name" if available, otherwise return
- * the X86_FEATURE_* numerals to make it easier to identify
- * the feature.
- */
-static const char *x86_feature_name(unsigned int feature, char *buf)
-{
- if (x86_cap_flags[feature])
- return x86_cap_flags[feature];
-
- snprintf(buf, 16, "%d*32+%2d", feature / 32, feature % 32);
-
- return buf;
-}
-
void check_cpufeature_deps(struct cpuinfo_x86 *c)
{
char feature_buf[16], depends_buf[16];
@@ -185,8 +171,8 @@ void check_cpufeature_deps(struct cpuinfo_x86 *c)
*/
pr_warn_once("x86 CPU feature dependency check failure: CPU%d has '%s' enabled but '%s' disabled. Kernel might be fine, but no guarantees.\n",
smp_processor_id(),
- x86_feature_name(d->feature, feature_buf),
- x86_feature_name(d->depends, depends_buf));
+ x86_cap_name(d->feature, feature_buf),
+ x86_cap_name(d->depends, depends_buf));
}
}
}
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 8239cd95a005..acd3f0f4867a 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -233,4 +233,6 @@ int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *sta
int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status);
int arch_lock_indir_br_lp_status(struct task_struct *t, unsigned long status);
+const char *x86_cap_name(unsigned int bit, char *buf);
+
#endif /* _LINUX_CPU_H_ */
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v7 3/3] x86/cpu: Do a sanity check on required feature bits
2026-02-24 12:38 [PATCH v7 0/3] x86: Capability bits fix and required bits sanity check Maciej Wieczor-Retman
2026-02-24 12:40 ` [PATCH v7 1/3] x86/cpu: Clear feature bits disabled at compile-time Maciej Wieczor-Retman
2026-02-24 12:40 ` [PATCH v7 2/3] x86/cpu: Check if feature string is non-zero Maciej Wieczor-Retman
@ 2026-02-24 12:40 ` Maciej Wieczor-Retman
2 siblings, 0 replies; 6+ messages in thread
From: Maciej Wieczor-Retman @ 2026-02-24 12:40 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: m.wieczorretman, Maciej Wieczor-Retman, linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
After cpu identification concludes, do a sanity check by comparing the
final x86_capability bitmask with the pre-defined required feature bits.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Acked-by: H. Peter Anvin (Intel) <hpa@zytor.com>
---
Changelog v6:
- Add Peter's acked-by tag.
- Rename patch subject to imperative form.
- Add a char buffer to the x86_cap_name() call.
arch/x86/kernel/cpu/common.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index b60269174d95..cecbd0b95a15 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1996,6 +1996,37 @@ const char *x86_cap_name(unsigned int bit, char *buf)
return buf;
}
+/*
+ * As a sanity check compare the final x86_capability bitmask with the initial
+ * predefined required feature bits. In case of a mismatch emit a warning with
+ * the faulty bitmask value.
+ */
+static void verify_required_features(const struct cpuinfo_x86 *c)
+{
+ u32 missing[NCAPINTS] = REQUIRED_MASK_INITIALIZER;
+ char cap_buf[16];
+ u32 error = 0;
+ unsigned int i;
+
+ for (i = 0; i < NCAPINTS; i++) {
+ missing[i] &= ~c->x86_capability[i];
+ error |= missing[i];
+ }
+
+ if (!error)
+ return; /* All good */
+
+ /*
+ * At least one required feature is missing. Print a warning,
+ * and taint the kernel.
+ */
+ pr_warn("cpu %d: missing required feature(s):", c->cpu_index);
+ for_each_set_bit(i, (void *)missing, NCAPINTS << 5)
+ pr_cont(" %s", x86_cap_name(i, cap_buf));
+ pr_cont("\n");
+ add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+}
+
/*
* This does the hard work of actually picking apart the CPU stuff...
*/
@@ -2125,6 +2156,8 @@ static void identify_cpu(struct cpuinfo_x86 *c)
mcheck_cpu_init(c);
numa_add_cpu(smp_processor_id());
+
+ verify_required_features(c);
}
/*
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread