]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - arch/x86/kernel/cpu/common.c
x86/cpu: Fix migration safety with X86_BUG_NULL_SEL
[mirror_ubuntu-jammy-kernel.git] / arch / x86 / kernel / cpu / common.c
index b3410f1ac21754c9ff94338a71da12e812d65686..58b1416c05da4cf50273a6505370b8ddb0ae10ca 100644 (file)
@@ -1396,9 +1396,8 @@ void __init early_cpu_init(void)
        early_identify_cpu(&boot_cpu_data);
 }
 
-static void detect_null_seg_behavior(struct cpuinfo_x86 *c)
+static bool detect_null_seg_behavior(void)
 {
-#ifdef CONFIG_X86_64
        /*
         * Empirically, writing zero to a segment selector on AMD does
         * not clear the base, whereas writing zero to a segment
@@ -1419,10 +1418,43 @@ static void detect_null_seg_behavior(struct cpuinfo_x86 *c)
        wrmsrl(MSR_FS_BASE, 1);
        loadsegment(fs, 0);
        rdmsrl(MSR_FS_BASE, tmp);
-       if (tmp != 0)
-               set_cpu_bug(c, X86_BUG_NULL_SEG);
        wrmsrl(MSR_FS_BASE, old_base);
-#endif
+       return tmp == 0;
+}
+
+void check_null_seg_clears_base(struct cpuinfo_x86 *c)
+{
+       /* BUG_NULL_SEG is only relevant with 64bit userspace */
+       if (!IS_ENABLED(CONFIG_X86_64))
+               return;
+
+       /* Zen3 CPUs advertise Null Selector Clears Base in CPUID. */
+       if (c->extended_cpuid_level >= 0x80000021 &&
+           cpuid_eax(0x80000021) & BIT(6))
+               return;
+
+       /*
+        * CPUID bit above wasn't set. If this kernel is still running
+        * as a HV guest, then the HV has decided not to advertize
+        * that CPUID bit for whatever reason.  For example, one
+        * member of the migration pool might be vulnerable.  Which
+        * means, the bug is present: set the BUG flag and return.
+        */
+       if (cpu_has(c, X86_FEATURE_HYPERVISOR)) {
+               set_cpu_bug(c, X86_BUG_NULL_SEG);
+               return;
+       }
+
+       /*
+        * Zen2 CPUs also have this behaviour, but no CPUID bit.
+        * 0x18 is the respective family for Hygon.
+        */
+       if ((c->x86 == 0x17 || c->x86 == 0x18) &&
+           detect_null_seg_behavior())
+               return;
+
+       /* All the remaining ones are affected */
+       set_cpu_bug(c, X86_BUG_NULL_SEG);
 }
 
 static void generic_identify(struct cpuinfo_x86 *c)
@@ -1458,8 +1490,6 @@ static void generic_identify(struct cpuinfo_x86 *c)
 
        get_model_name(c); /* Default name */
 
-       detect_null_seg_behavior(c);
-
        /*
         * ESPFIX is a strange bug.  All real CPUs have it.  Paravirt
         * systems that run Linux at CPL > 0 may or may not have the