[PATCH v12 3/4] x86/cpu: Do a sanity check on required feature bits
From: Maciej Wieczor-Retman
Date: Fri Mar 27 2026 - 11:14:47 EST
From: Maciej Wieczor-Retman <maciej.wieczor-retman@xxxxxxxxx>
After CPU identification concludes, do a sanity check by comparing the
final x86_capability bitmask with the pre-defined required feature bits.
The NCAPINTS + 1 size of the required_features[] array results from
for_each_set_bit() parsing data in 64-bit chunks. In case NCAPINTS is an
odd number it will still reserve and zero out a 64-bit aligned chunk of
memory which will prevent false positives due to unaligned accesses.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@xxxxxxxxx>
---
Changelog v12:
- Redo the function into a one loop system instead of two loops
according to Pawan Gupta's suggestion.
- Add a paragraph to the patch message about the NCAPINTS + 1 array
size.
Changelog v11:
- Use DECLARE_BITMAP() on missing[] and recast to a u32 pointer for
iterating. Do it to avoid unaligned memory accesses when later using
for_each_set_bit() if NCAPINTS is going to ever become an odd number.
missing[] was previously an u32 array and for_each_set_bit() works on
unsigned long chunks of memory.
- Add a paragraph about the above to the patch message.
- Remove Peter's acked-by due to more changes.
Changelog v10:
- Shorten the comment before the sanity check.
- cpu -> CPU in the warning.
- NCAPINTS << 5 -> NCAPINTS * 32
Changelog v9:
- REQUIRED_MASK_INITIALIZER -> REQUIRED_MASK_INIT
- Redo the comments.
- Fix reverse xmas order.
- Inside for_each_set_bit: (void *) -> (unsigned long *).
- 16 -> X86_CAP_BUF_SIZE.
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 | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 7cfd124b3fbf..8ad0da7012dd 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -2003,6 +2003,32 @@ const char *x86_feature_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.
+ */
+static void verify_required_features(const struct cpuinfo_x86 *c)
+{
+ u32 required_features[NCAPINTS + 1] = REQUIRED_MASK_INIT;
+ char cap_buf[X86_NAMELESS_FEAT_BUFLEN];
+ int i, error = 0;
+
+ for_each_set_bit(i, (unsigned long *)required_features, NCAPINTS * 32) {
+ if (test_bit(i, (unsigned long *)c->x86_capability))
+ continue;
+ if (!error)
+ pr_warn("CPU %d: missing required feature(s):", c->cpu_index);
+ pr_cont(" %s", x86_feature_name(i, cap_buf));
+ error = 1;
+ }
+
+ if (!error)
+ return;
+
+ 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...
*/
@@ -2132,6 +2158,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