mirror of
https://github.com/torvalds/linux.git
synced 2026-06-04 04:23:35 +02:00
Booting with "nopcid" clears X86_FEATURE_PCID and keeps CR4.PCIDE from being
set to one. On AMD CPUs that support INVLPGB, broadcast TLB flushing remains
enabled.
There are two checks that decide whether the global ASID code runs,
mm_global_asid() and consider_global_asid(), that key off of the
X86_FEATURE_INVLPGB feature. Once an mm becomes active on more than three
CPUs, consider_global_asid() assigns it a global ASID, after which
flush_tlb_mm_range() takes the broadcast_tlb_flush() path using a non-zero
PCID. Issuing an INVLPGB with a non-zero PCID while CR4.PCIDE is not set
results in a #GP:
Oops: general protection fault, kernel NULL pointer dereference 0x1: 0000 [#1] SMP NOPTI
CPU: 158 UID: 0 PID: 3119 Comm: snap Not tainted 7.1.0-rc3 #1 PREEMPT(full)
Hardware name: ...
RIP: 0010:broadcast_tlb_flush
Code: ... 89 da 48 83 c8 07 <0f> 01 fe eb 08 cc cc cc ...
Call Trace:
<TASK>
flush_tlb_mm_range
ptep_clear_flush
wp_page_copy
? _raw_spin_unlock
__handle_mm_fault
handle_mm_fault
do_user_addr_fault
exc_page_fault
asm_exc_page_fault
All processors that support broadcast TLB invalidation also have PCID support,
so it is only the "nopcid" scenario that is of concern. In this situation just
disable the broadcast TLB support using the CPUID dependency support by making
X86_FEATURE_INVLPGB dependent on X86_FEATURE_PCID.
[ bp: Massage commit message. ]
Fixes: 4afeb0ed17 ("x86/mm: Enable broadcast TLB invalidation for multi-threaded processes")
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Rik van Riel <riel@surriel.com>
Cc: <stable@kernel.org>
Link: https://patch.msgid.link/b915acfd63e8b2a094fdeb8dc608738072518764.1779296450.git.thomas.lendacky@amd.com
194 lines
6.7 KiB
C
194 lines
6.7 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_APX, 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_SDCIAE, X86_FEATURE_CAT_L3 },
|
|
{ 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_EUPDATESVN, 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_AMX_FP16, X86_FEATURE_AMX_TILE },
|
|
{ X86_FEATURE_AMX_BF16, X86_FEATURE_AMX_TILE },
|
|
{ X86_FEATURE_AMX_INT8, X86_FEATURE_AMX_TILE },
|
|
{ X86_FEATURE_SHSTK, X86_FEATURE_XSAVES },
|
|
{ X86_FEATURE_FRED, X86_FEATURE_LKGS },
|
|
{ X86_FEATURE_SPEC_CTRL_SSBD, X86_FEATURE_SPEC_CTRL },
|
|
{ X86_FEATURE_LASS, X86_FEATURE_SMAP },
|
|
{ X86_FEATURE_INVLPGB, X86_FEATURE_PCID },
|
|
{}
|
|
};
|
|
|
|
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));
|
|
}
|
|
}
|
|
}
|