93022482b2
Code in v6.9 arch/x86/kernel/smpboot.c was changed by commit4db64279bc
("x86/cpu: Switch to new Intel CPU model defines") from: static const struct x86_cpu_id intel_cod_cpu[] = { X86_MATCH_INTEL_FAM6_MODEL(HASWELL_X, 0), /* COD */ X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, 0), /* COD */ X86_MATCH_INTEL_FAM6_MODEL(ANY, 1), /* SNC */ <--- 443 {} }; static bool match_llc(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o) { const struct x86_cpu_id *id = x86_match_cpu(intel_cod_cpu); to: static const struct x86_cpu_id intel_cod_cpu[] = { X86_MATCH_VFM(INTEL_HASWELL_X, 0), /* COD */ X86_MATCH_VFM(INTEL_BROADWELL_X, 0), /* COD */ X86_MATCH_VFM(INTEL_ANY, 1), /* SNC */ {} }; static bool match_llc(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o) { const struct x86_cpu_id *id = x86_match_cpu(intel_cod_cpu); On an Intel CPU with SNC enabled this code previously matched the rule on line 443 to avoid printing messages about insane cache configuration. The new code did not match any rules. Expanding the macros for the intel_cod_cpu[] array shows that the old is equivalent to: static const struct x86_cpu_id intel_cod_cpu[] = { [0] = { .vendor = 0, .family = 6, .model = 0x3F, .steppings = 0, .feature = 0, .driver_data = 0 }, [1] = { .vendor = 0, .family = 6, .model = 0x4F, .steppings = 0, .feature = 0, .driver_data = 0 }, [2] = { .vendor = 0, .family = 6, .model = 0x00, .steppings = 0, .feature = 0, .driver_data = 1 }, [3] = { .vendor = 0, .family = 0, .model = 0x00, .steppings = 0, .feature = 0, .driver_data = 0 } } while the new code expands to: static const struct x86_cpu_id intel_cod_cpu[] = { [0] = { .vendor = 0, .family = 6, .model = 0x3F, .steppings = 0, .feature = 0, .driver_data = 0 }, [1] = { .vendor = 0, .family = 6, .model = 0x4F, .steppings = 0, .feature = 0, .driver_data = 0 }, [2] = { .vendor = 0, .family = 0, .model = 0x00, .steppings = 0, .feature = 0, .driver_data = 1 }, [3] = { .vendor = 0, .family = 0, .model = 0x00, .steppings = 0, .feature = 0, .driver_data = 0 } } Looking at the code for x86_match_cpu(): const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match) { const struct x86_cpu_id *m; struct cpuinfo_x86 *c = &boot_cpu_data; for (m = match; m->vendor | m->family | m->model | m->steppings | m->feature; m++) { ... } return NULL; it is clear that there was no match because the ANY entry in the table (array index 2) is now the loop termination condition (all of vendor, family, model, steppings, and feature are zero). So this code was working before because the "ANY" check was looking for any Intel CPU in family 6. But fails now because the family is a wild card. So the root cause is that x86_match_cpu() has never been able to match on a rule with just X86_VENDOR_INTEL and all other fields set to wildcards. Add a new flags field to struct x86_cpu_id that has a bit set to indicate that this entry in the array is valid. Update X86_MATCH*() macros to set that bit. Change the end-marker check in x86_match_cpu() to just check the flags field for this bit. Backporter notes: The commit in Fixes is really the one that is broken: you can't have m->vendor as part of the loop termination conditional in x86_match_cpu() because it can happen - as it has happened above - that that whole conditional is 0 albeit vendor == 0 is a valid case - X86_VENDOR_INTEL is 0. However, the only case where the above happens is the SNC check added by4db64279bc
so you only need this fix if you have backported that other commit4db64279bc
("x86/cpu: Switch to new Intel CPU model defines") Fixes:644e9cbbe3
("Add driver auto probing for x86 features v4") Suggested-by: Thomas Gleixner <tglx@linutronix.de> Suggested-by: Borislav Petkov <bp@alien8.de> Signed-off-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Cc: <stable+noautosel@kernel.org> # see above Link: https://lore.kernel.org/r/20240517144312.GBZkdtAOuJZCvxhFbJ@fat_crate.local
89 lines
2.6 KiB
C
89 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <asm/cpu_device_id.h>
|
|
#include <asm/cpufeature.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/export.h>
|
|
#include <linux/slab.h>
|
|
|
|
/**
|
|
* x86_match_cpu - match current CPU again an array of x86_cpu_ids
|
|
* @match: Pointer to array of x86_cpu_ids. Last entry terminated with
|
|
* {}.
|
|
*
|
|
* Return the entry if the current CPU matches the entries in the
|
|
* passed x86_cpu_id match table. Otherwise NULL. The match table
|
|
* contains vendor (X86_VENDOR_*), family, model and feature bits or
|
|
* respective wildcard entries.
|
|
*
|
|
* A typical table entry would be to match a specific CPU
|
|
*
|
|
* X86_MATCH_VFM_FEATURE(INTEL_BROADWELL, X86_FEATURE_ANY, NULL);
|
|
*
|
|
* Fields can be wildcarded with %X86_VENDOR_ANY, %X86_FAMILY_ANY,
|
|
* %X86_MODEL_ANY, %X86_FEATURE_ANY (except for vendor)
|
|
*
|
|
* asm/cpu_device_id.h contains a set of useful macros which are shortcuts
|
|
* for various common selections. The above can be shortened to:
|
|
*
|
|
* X86_MATCH_VFM(INTEL_BROADWELL, NULL);
|
|
*
|
|
* Arrays used to match for this should also be declared using
|
|
* MODULE_DEVICE_TABLE(x86cpu, ...)
|
|
*
|
|
* This always matches against the boot cpu, assuming models and features are
|
|
* consistent over all CPUs.
|
|
*/
|
|
const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
|
|
{
|
|
const struct x86_cpu_id *m;
|
|
struct cpuinfo_x86 *c = &boot_cpu_data;
|
|
|
|
for (m = match; m->flags & X86_CPU_ID_FLAG_ENTRY_VALID; m++) {
|
|
if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor)
|
|
continue;
|
|
if (m->family != X86_FAMILY_ANY && c->x86 != m->family)
|
|
continue;
|
|
if (m->model != X86_MODEL_ANY && c->x86_model != m->model)
|
|
continue;
|
|
if (m->steppings != X86_STEPPING_ANY &&
|
|
!(BIT(c->x86_stepping) & m->steppings))
|
|
continue;
|
|
if (m->feature != X86_FEATURE_ANY && !cpu_has(c, m->feature))
|
|
continue;
|
|
return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(x86_match_cpu);
|
|
|
|
static const struct x86_cpu_desc *
|
|
x86_match_cpu_with_stepping(const struct x86_cpu_desc *match)
|
|
{
|
|
struct cpuinfo_x86 *c = &boot_cpu_data;
|
|
const struct x86_cpu_desc *m;
|
|
|
|
for (m = match; m->x86_family | m->x86_model; m++) {
|
|
if (c->x86_vendor != m->x86_vendor)
|
|
continue;
|
|
if (c->x86 != m->x86_family)
|
|
continue;
|
|
if (c->x86_model != m->x86_model)
|
|
continue;
|
|
if (c->x86_stepping != m->x86_stepping)
|
|
continue;
|
|
return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool x86_cpu_has_min_microcode_rev(const struct x86_cpu_desc *table)
|
|
{
|
|
const struct x86_cpu_desc *res = x86_match_cpu_with_stepping(table);
|
|
|
|
if (!res || res->x86_microcode_rev > boot_cpu_data.microcode)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
EXPORT_SYMBOL_GPL(x86_cpu_has_min_microcode_rev);
|