Skip to content

Commit ce0b15d

Browse files
committed
x86/mm: Avoid incomplete Global INVLPG flushes
The INVLPG instruction is used to invalidate TLB entries for a specified virtual address. When PCIDs are enabled, INVLPG is supposed to invalidate TLB entries for the specified address for both the current PCID *and* Global entries. (Note: Only kernel mappings set Global=1.) Unfortunately, some INVLPG implementations can leave Global translations unflushed when PCIDs are enabled. As a workaround, never enable PCIDs on affected processors. I expect there to eventually be microcode mitigations to replace this software workaround. However, the exact version numbers where that will happen are not known today. Once the version numbers are set in stone, the processor list can be tweaked to only disable PCIDs on affected processors with affected microcode. Note: if anyone wants a quick fix that doesn't require patching, just stick 'nopcid' on your kernel command-line. Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Cc: stable@vger.kernel.org
1 parent 7d8accf commit ce0b15d

File tree

1 file changed

+25
-0
lines changed

1 file changed

+25
-0
lines changed

arch/x86/mm/init.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/sched/task.h>
1010

1111
#include <asm/set_memory.h>
12+
#include <asm/cpu_device_id.h>
1213
#include <asm/e820/api.h>
1314
#include <asm/init.h>
1415
#include <asm/page.h>
@@ -261,6 +262,24 @@ static void __init probe_page_size_mask(void)
261262
}
262263
}
263264

265+
#define INTEL_MATCH(_model) { .vendor = X86_VENDOR_INTEL, \
266+
.family = 6, \
267+
.model = _model, \
268+
}
269+
/*
270+
* INVLPG may not properly flush Global entries
271+
* on these CPUs when PCIDs are enabled.
272+
*/
273+
static const struct x86_cpu_id invlpg_miss_ids[] = {
274+
INTEL_MATCH(INTEL_FAM6_ALDERLAKE ),
275+
INTEL_MATCH(INTEL_FAM6_ALDERLAKE_L ),
276+
INTEL_MATCH(INTEL_FAM6_ALDERLAKE_N ),
277+
INTEL_MATCH(INTEL_FAM6_RAPTORLAKE ),
278+
INTEL_MATCH(INTEL_FAM6_RAPTORLAKE_P),
279+
INTEL_MATCH(INTEL_FAM6_RAPTORLAKE_S),
280+
{}
281+
};
282+
264283
static void setup_pcid(void)
265284
{
266285
if (!IS_ENABLED(CONFIG_X86_64))
@@ -269,6 +288,12 @@ static void setup_pcid(void)
269288
if (!boot_cpu_has(X86_FEATURE_PCID))
270289
return;
271290

291+
if (x86_match_cpu(invlpg_miss_ids)) {
292+
pr_info("Incomplete global flushes, disabling PCID");
293+
setup_clear_cpu_cap(X86_FEATURE_PCID);
294+
return;
295+
}
296+
272297
if (boot_cpu_has(X86_FEATURE_PGE)) {
273298
/*
274299
* This can't be cr4_set_bits_and_update_boot() -- the

0 commit comments

Comments
 (0)