mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-19 00:04:18 +00:00
Currently, the cpuid_deps[] table is only exercised when a particular feature is explicitly disabled and clear_cpu_cap() is called. However, some of these listed dependencies might already be missing during boot. These types of errors shouldn't generally happen in production environments, but they could sometimes sneak through, especially when VMs and Kconfigs are in the mix. Also, the kernel might introduce artificial dependencies between unrelated features, such as making LAM depend on LASS. Unexpected failures can occur when the kernel tries to use such features. Add a simple boot-time scan of the cpuid_deps[] table to detect the missing dependencies. One option is to disable all of such features during boot, but that may cause regressions in existing systems. For now, just warn about the missing dependencies to create awareness. As a trade-off between spamming the kernel log and keeping track of all the features that have been warned about, only warn about the first missing dependency. Any subsequent unmet dependency will only be logged after the first one has been resolved. Features are typically represented through unsigned integers within the kernel, though some of them have user-friendly names if they are exposed via /proc/cpuinfo. Show the friendlier name if available, otherwise display the X86_FEATURE_* numerals to make it easier to identify the feature. Suggested-by: Tony Luck <tony.luck@intel.com> Suggested-by: Ingo Molnar <mingo@redhat.com> Signed-off-by: Sohil Mehta <sohil.mehta@intel.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Brian Gerst <brgerst@gmail.com> Cc: Juergen Gross <jgross@suse.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Link: https://lore.kernel.org/r/20250313201608.3304135-1-sohil.mehta@intel.com
185 lines
6.3 KiB
C
185 lines
6.3 KiB
C
/* Declare dependencies between CPUIDs */
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <asm/cpufeature.h>
|
|
|
|
struct cpuid_dep {
|
|
unsigned int feature;
|
|
unsigned int depends;
|
|
};
|
|
|
|
/*
|
|
* Table of CPUID features that depend on others.
|
|
*
|
|
* This only includes dependencies that can be usefully disabled, not
|
|
* features part of the base set (like FPU).
|
|
*
|
|
* Note this all is not __init / __initdata because it can be
|
|
* called from cpu hotplug. It shouldn't do anything in this case,
|
|
* but it's difficult to tell that to the init reference checker.
|
|
*/
|
|
static const struct cpuid_dep cpuid_deps[] = {
|
|
{ X86_FEATURE_FXSR, X86_FEATURE_FPU },
|
|
{ X86_FEATURE_XSAVEOPT, X86_FEATURE_XSAVE },
|
|
{ X86_FEATURE_XSAVEC, X86_FEATURE_XSAVE },
|
|
{ X86_FEATURE_XSAVES, X86_FEATURE_XSAVE },
|
|
{ X86_FEATURE_AVX, X86_FEATURE_XSAVE },
|
|
{ X86_FEATURE_PKU, X86_FEATURE_XSAVE },
|
|
{ X86_FEATURE_MPX, X86_FEATURE_XSAVE },
|
|
{ X86_FEATURE_XGETBV1, X86_FEATURE_XSAVE },
|
|
{ X86_FEATURE_CMOV, X86_FEATURE_FXSR },
|
|
{ X86_FEATURE_MMX, X86_FEATURE_FXSR },
|
|
{ X86_FEATURE_MMXEXT, X86_FEATURE_MMX },
|
|
{ X86_FEATURE_FXSR_OPT, X86_FEATURE_FXSR },
|
|
{ X86_FEATURE_XSAVE, X86_FEATURE_FXSR },
|
|
{ X86_FEATURE_XMM, X86_FEATURE_FXSR },
|
|
{ X86_FEATURE_XMM2, X86_FEATURE_XMM },
|
|
{ X86_FEATURE_XMM3, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_XMM4_1, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_XMM4_2, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_XMM3, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_PCLMULQDQ, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_SSSE3, X86_FEATURE_XMM2, },
|
|
{ X86_FEATURE_F16C, X86_FEATURE_XMM2, },
|
|
{ X86_FEATURE_AES, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_SHA_NI, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_GFNI, X86_FEATURE_XMM2 },
|
|
{ X86_FEATURE_AVX_VNNI, X86_FEATURE_AVX },
|
|
{ X86_FEATURE_FMA, X86_FEATURE_AVX },
|
|
{ X86_FEATURE_VAES, X86_FEATURE_AVX },
|
|
{ X86_FEATURE_VPCLMULQDQ, X86_FEATURE_AVX },
|
|
{ X86_FEATURE_AVX2, X86_FEATURE_AVX, },
|
|
{ X86_FEATURE_AVX512F, X86_FEATURE_AVX, },
|
|
{ X86_FEATURE_AVX512IFMA, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512PF, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512ER, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512CD, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512DQ, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512BW, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512VL, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512VBMI, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512_VBMI2, X86_FEATURE_AVX512VL },
|
|
{ X86_FEATURE_AVX512_VNNI, X86_FEATURE_AVX512VL },
|
|
{ X86_FEATURE_AVX512_BITALG, X86_FEATURE_AVX512VL },
|
|
{ X86_FEATURE_AVX512_4VNNIW, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512_4FMAPS, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512_VPOPCNTDQ, X86_FEATURE_AVX512F },
|
|
{ X86_FEATURE_AVX512_VP2INTERSECT, X86_FEATURE_AVX512VL },
|
|
{ X86_FEATURE_CQM_OCCUP_LLC, X86_FEATURE_CQM_LLC },
|
|
{ X86_FEATURE_CQM_MBM_TOTAL, X86_FEATURE_CQM_LLC },
|
|
{ X86_FEATURE_CQM_MBM_LOCAL, X86_FEATURE_CQM_LLC },
|
|
{ X86_FEATURE_BMEC, X86_FEATURE_CQM_MBM_TOTAL },
|
|
{ X86_FEATURE_BMEC, X86_FEATURE_CQM_MBM_LOCAL },
|
|
{ X86_FEATURE_AVX512_BF16, X86_FEATURE_AVX512VL },
|
|
{ X86_FEATURE_AVX512_FP16, X86_FEATURE_AVX512BW },
|
|
{ X86_FEATURE_ENQCMD, X86_FEATURE_XSAVES },
|
|
{ X86_FEATURE_PER_THREAD_MBA, X86_FEATURE_MBA },
|
|
{ X86_FEATURE_SGX_LC, X86_FEATURE_SGX },
|
|
{ X86_FEATURE_SGX1, X86_FEATURE_SGX },
|
|
{ X86_FEATURE_SGX2, X86_FEATURE_SGX1 },
|
|
{ X86_FEATURE_SGX_EDECCSSA, X86_FEATURE_SGX1 },
|
|
{ X86_FEATURE_XFD, X86_FEATURE_XSAVES },
|
|
{ X86_FEATURE_XFD, X86_FEATURE_XGETBV1 },
|
|
{ X86_FEATURE_AMX_TILE, X86_FEATURE_XFD },
|
|
{ X86_FEATURE_SHSTK, X86_FEATURE_XSAVES },
|
|
{ X86_FEATURE_FRED, X86_FEATURE_LKGS },
|
|
{}
|
|
};
|
|
|
|
static inline void clear_feature(struct cpuinfo_x86 *c, unsigned int feature)
|
|
{
|
|
/*
|
|
* Note: This could use the non atomic __*_bit() variants, but the
|
|
* rest of the cpufeature code uses atomics as well, so keep it for
|
|
* consistency. Cleanup all of it separately.
|
|
*/
|
|
if (!c) {
|
|
clear_cpu_cap(&boot_cpu_data, feature);
|
|
set_bit(feature, (unsigned long *)cpu_caps_cleared);
|
|
} else {
|
|
clear_bit(feature, (unsigned long *)c->x86_capability);
|
|
}
|
|
}
|
|
|
|
/* Take the capabilities and the BUG bits into account */
|
|
#define MAX_FEATURE_BITS ((NCAPINTS + NBUGINTS) * sizeof(u32) * 8)
|
|
|
|
static void do_clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
|
|
{
|
|
DECLARE_BITMAP(disable, MAX_FEATURE_BITS);
|
|
const struct cpuid_dep *d;
|
|
bool changed;
|
|
|
|
if (WARN_ON(feature >= MAX_FEATURE_BITS))
|
|
return;
|
|
|
|
if (boot_cpu_has(feature))
|
|
WARN_ON(alternatives_patched);
|
|
|
|
clear_feature(c, feature);
|
|
|
|
/* Collect all features to disable, handling dependencies */
|
|
memset(disable, 0, sizeof(disable));
|
|
__set_bit(feature, disable);
|
|
|
|
/* Loop until we get a stable state. */
|
|
do {
|
|
changed = false;
|
|
for (d = cpuid_deps; d->feature; d++) {
|
|
if (!test_bit(d->depends, disable))
|
|
continue;
|
|
if (__test_and_set_bit(d->feature, disable))
|
|
continue;
|
|
|
|
changed = true;
|
|
clear_feature(c, d->feature);
|
|
}
|
|
} while (changed);
|
|
}
|
|
|
|
void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
|
|
{
|
|
do_clear_cpu_cap(c, feature);
|
|
}
|
|
|
|
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];
|
|
const struct cpuid_dep *d;
|
|
|
|
for (d = cpuid_deps; d->feature; d++) {
|
|
if (cpu_has(c, d->feature) && !cpu_has(c, d->depends)) {
|
|
/*
|
|
* Only warn about the first unmet dependency on the
|
|
* first CPU where it is encountered to avoid spamming
|
|
* the kernel log.
|
|
*/
|
|
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));
|
|
}
|
|
}
|
|
}
|