]> git.itanic.dy.fi Git - linux-stable/commitdiff
x86/mm: Avoid incomplete Global INVLPG flushes
authorDave Hansen <dave.hansen@linux.intel.com>
Tue, 16 May 2023 19:24:25 +0000 (12:24 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 May 2023 12:55:30 +0000 (13:55 +0100)
commit ce0b15d11ad837fbacc5356941712218e38a0a83 upstream.

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
Signed-off-by: Daniel Sneddon <daniel.sneddon@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/include/asm/intel-family.h
arch/x86/mm/init.c

index fc12d970a07c04e2202bb2b47184b83af60cbc1e..d975c60f863a289cc71d999c49b36fd0a0a9359e 100644 (file)
 
 #define INTEL_FAM6_ALDERLAKE           0x97    /* Golden Cove / Gracemont */
 #define INTEL_FAM6_ALDERLAKE_L         0x9A    /* Golden Cove / Gracemont */
+#define INTEL_FAM6_ALDERLAKE_N         0xBE
+
+#define INTEL_FAM6_RAPTORLAKE          0xB7
+#define INTEL_FAM6_RAPTORLAKE_P                0xBA
+#define INTEL_FAM6_RAPTORLAKE_S                0xBF
 
 #define INTEL_FAM6_LUNARLAKE_M         0xBD
 
index 0e3667e529abbe9bc638279cd66801a6a3217eb6..34a08f6a528e9962384858ede7b3e75e28414fd5 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/sched/task.h>
 
 #include <asm/set_memory.h>
+#include <asm/cpu_device_id.h>
 #include <asm/e820/api.h>
 #include <asm/init.h>
 #include <asm/page.h>
@@ -261,6 +262,24 @@ static void __init probe_page_size_mask(void)
        }
 }
 
+#define INTEL_MATCH(_model) { .vendor  = X86_VENDOR_INTEL,     \
+                             .family  = 6,                     \
+                             .model = _model,                  \
+                           }
+/*
+ * INVLPG may not properly flush Global entries
+ * on these CPUs when PCIDs are enabled.
+ */
+static const struct x86_cpu_id invlpg_miss_ids[] = {
+       INTEL_MATCH(INTEL_FAM6_ALDERLAKE   ),
+       INTEL_MATCH(INTEL_FAM6_ALDERLAKE_L ),
+       INTEL_MATCH(INTEL_FAM6_ALDERLAKE_N ),
+       INTEL_MATCH(INTEL_FAM6_RAPTORLAKE  ),
+       INTEL_MATCH(INTEL_FAM6_RAPTORLAKE_P),
+       INTEL_MATCH(INTEL_FAM6_RAPTORLAKE_S),
+       {}
+};
+
 static void setup_pcid(void)
 {
        if (!IS_ENABLED(CONFIG_X86_64))
@@ -269,6 +288,12 @@ static void setup_pcid(void)
        if (!boot_cpu_has(X86_FEATURE_PCID))
                return;
 
+       if (x86_match_cpu(invlpg_miss_ids)) {
+               pr_info("Incomplete global flushes, disabling PCID");
+               setup_clear_cpu_cap(X86_FEATURE_PCID);
+               return;
+       }
+
        if (boot_cpu_has(X86_FEATURE_PGE)) {
                /*
                 * This can't be cr4_set_bits_and_update_boot() -- the