]> xenbits.xensource.com Git - people/tklengyel/xen.git/commitdiff
amd/msr: implement VIRT_SPEC_CTRL for HVM guests using legacy SSBD
authorRoger Pau Monné <roger.pau@citrix.com>
Mon, 15 Aug 2022 07:58:55 +0000 (09:58 +0200)
committerGeorge Dunlap <george.dunlap@citrix.com>
Fri, 19 Aug 2022 19:23:43 +0000 (20:23 +0100)
Expose VIRT_SSBD to guests if the hardware supports setting SSBD in
the LS_CFG MSR (a.k.a. non-architectural way). Different AMD CPU
families use different bits in LS_CFG, so exposing VIRT_SPEC_CTRL.SSBD
allows for an unified way of exposing SSBD support to guests on AMD
hardware that's compatible migration wise, regardless of what
underlying mechanism is used to set SSBD.

Note that on AMD Family 17h and Hygon Family 18h processors the value
of SSBD in LS_CFG is shared between threads on the same core, so
there's extra logic in order to synchronize the value and have SSBD
set as long as one of the threads in the core requires it to be set.
Such logic also requires extra storage for each thread state, which is
allocated at initialization time.

Do the context switching of the SSBD selection in LS_CFG between
hypervisor and guest in the same handler that's already used to switch
the value of VIRT_SPEC_CTRL.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Henry Wang <Henry.Wang@arm.com>
Re-commited with a tag removed.

Signed-off-by: George Dunlap <george.dunlap@citrix.com>
CHANGELOG.md
xen/arch/x86/cpu/amd.c
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/include/asm/amd.h
xen/arch/x86/spec_ctrl.c

index a87a193e1536243855a79b18709c6a202e2016a0..5e4bae5f3553518b71ef38208967667e536d25ab 100644 (file)
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 ### Added / support upgraded
  - IOMMU superpage support on x86, affecting PV guests as well as HVM/PVH ones
    when they don't share page tables with the CPU (HAP / EPT / NPT).
+ - Support VIRT_SSBD feature for HVM guests on AMD.
 
 ### Removed / support downgraded
  - dropped support for the (x86-only) "vesa-mtrr" and "vesa-remap" command line options
index d5f8e5e899cc8a2f432fd23e13e25f16f8878ca9..98c52d0686facacda954ef86595fae4f574f3aeb 100644 (file)
@@ -48,6 +48,7 @@ boolean_param("allow_unsafe", opt_allow_unsafe);
 
 /* Signal whether the ACPI C1E quirk is required. */
 bool __read_mostly amd_acpi_c1e_quirk;
+bool __ro_after_init amd_legacy_ssbd;
 
 static inline int rdmsr_amd_safe(unsigned int msr, unsigned int *lo,
                                 unsigned int *hi)
@@ -685,23 +686,10 @@ void amd_init_lfence(struct cpuinfo_x86 *c)
  * Refer to the AMD Speculative Store Bypass whitepaper:
  * https://developer.amd.com/wp-content/resources/124441_AMD64_SpeculativeStoreBypassDisable_Whitepaper_final.pdf
  */
-void amd_init_ssbd(const struct cpuinfo_x86 *c)
+static bool set_legacy_ssbd(const struct cpuinfo_x86 *c, bool enable)
 {
        int bit = -1;
 
-       if (cpu_has_ssb_no)
-               return;
-
-       if (cpu_has_amd_ssbd) {
-               /* Handled by common MSR_SPEC_CTRL logic */
-               return;
-       }
-
-       if (cpu_has_virt_ssbd) {
-               wrmsrl(MSR_VIRT_SPEC_CTRL, opt_ssbd ? SPEC_CTRL_SSBD : 0);
-               return;
-       }
-
        switch (c->x86) {
        case 0x15: bit = 54; break;
        case 0x16: bit = 33; break;
@@ -715,20 +703,119 @@ void amd_init_ssbd(const struct cpuinfo_x86 *c)
                if (rdmsr_safe(MSR_AMD64_LS_CFG, val) ||
                    ({
                            val &= ~mask;
-                           if (opt_ssbd)
+                           if (enable)
                                    val |= mask;
                            false;
                    }) ||
                    wrmsr_safe(MSR_AMD64_LS_CFG, val) ||
                    ({
                            rdmsrl(MSR_AMD64_LS_CFG, val);
-                           (val & mask) != (opt_ssbd * mask);
+                           (val & mask) != (enable * mask);
                    }))
                        bit = -1;
        }
 
-       if (bit < 0)
+       return bit >= 0;
+}
+
+void amd_init_ssbd(const struct cpuinfo_x86 *c)
+{
+       if (cpu_has_ssb_no)
+               return;
+
+       if (cpu_has_amd_ssbd) {
+               /* Handled by common MSR_SPEC_CTRL logic */
+               return;
+       }
+
+       if (cpu_has_virt_ssbd) {
+               wrmsrl(MSR_VIRT_SPEC_CTRL, opt_ssbd ? SPEC_CTRL_SSBD : 0);
+               return;
+       }
+
+       if (!set_legacy_ssbd(c, opt_ssbd)) {
                printk_once(XENLOG_ERR "No SSBD controls available\n");
+               if (amd_legacy_ssbd)
+                       panic("CPU feature mismatch: no legacy SSBD\n");
+       } else if (c == &boot_cpu_data)
+               amd_legacy_ssbd = true;
+}
+
+static struct ssbd_ls_cfg {
+    bool locked;
+    unsigned int count;
+} __cacheline_aligned *ssbd_ls_cfg;
+static unsigned int __ro_after_init ssbd_max_cores;
+#define AMD_FAM17H_MAX_SOCKETS 2
+
+bool __init amd_setup_legacy_ssbd(void)
+{
+       unsigned int i;
+
+       if ((boot_cpu_data.x86 != 0x17 && boot_cpu_data.x86 != 0x18) ||
+           boot_cpu_data.x86_num_siblings <= 1)
+               return true;
+
+       /*
+        * One could be forgiven for thinking that c->x86_max_cores is the
+        * correct value to use here.
+        *
+        * However, that value is derived from the current configuration, and
+        * c->cpu_core_id is sparse on all but the top end CPUs.  Derive
+        * max_cpus from ApicIdCoreIdSize which will cover any sparseness.
+        */
+       if (boot_cpu_data.extended_cpuid_level >= 0x80000008) {
+               ssbd_max_cores = 1u << MASK_EXTR(cpuid_ecx(0x80000008), 0xf000);
+               ssbd_max_cores /= boot_cpu_data.x86_num_siblings;
+       }
+       if (!ssbd_max_cores)
+               return false;
+
+       ssbd_ls_cfg = xzalloc_array(struct ssbd_ls_cfg,
+                                   ssbd_max_cores * AMD_FAM17H_MAX_SOCKETS);
+       if (!ssbd_ls_cfg)
+               return false;
+
+       if (opt_ssbd)
+               for (i = 0; i < ssbd_max_cores * AMD_FAM17H_MAX_SOCKETS; i++)
+                       /* Set initial state, applies to any (hotplug) CPU. */
+                       ssbd_ls_cfg[i].count = boot_cpu_data.x86_num_siblings;
+
+       return true;
+}
+
+/*
+ * Executed from GIF==0 context: avoid using BUG/ASSERT or other functionality
+ * that relies on exceptions as those are not expected to run in GIF==0
+ * context.
+ */
+void amd_set_legacy_ssbd(bool enable)
+{
+       const struct cpuinfo_x86 *c = &current_cpu_data;
+       struct ssbd_ls_cfg *status;
+
+       if ((c->x86 != 0x17 && c->x86 != 0x18) || c->x86_num_siblings <= 1) {
+               set_legacy_ssbd(c, enable);
+               return;
+       }
+
+       status = &ssbd_ls_cfg[c->phys_proc_id * ssbd_max_cores +
+                             c->cpu_core_id];
+
+       /*
+        * Open code a very simple spinlock: this function is used with GIF==0
+        * and different IF values, so would trigger the checklock detector.
+        * Instead of trying to workaround the detector, use a very simple lock
+        * implementation: it's better to reduce the amount of code executed
+        * with GIF==0.
+        */
+       while (test_and_set_bool(status->locked))
+               cpu_relax();
+       status->count += enable ? 1 : -1;
+       if (enable ? status->count == 1 : !status->count)
+               set_legacy_ssbd(c, enable);
+       barrier();
+       write_atomic(&status->locked, false);
 }
 
 /*
index 53ce2edd358fc66cfb7129745e506866e4753a8b..1aeaabcb13fcbd343f731fa89c09ab74a59f47ac 100644 (file)
@@ -3126,6 +3126,8 @@ void vmexit_virt_spec_ctrl(void)
 
     if ( cpu_has_virt_ssbd )
         wrmsr(MSR_VIRT_SPEC_CTRL, val, 0);
+    else
+        amd_set_legacy_ssbd(val);
 }
 
 /* Called with GIF=0. */
@@ -3138,6 +3140,8 @@ void vmentry_virt_spec_ctrl(void)
 
     if ( cpu_has_virt_ssbd )
         wrmsr(MSR_VIRT_SPEC_CTRL, val, 0);
+    else
+        amd_set_legacy_ssbd(val);
 }
 
 /*
index a82382e6bfc9b3c6c1483ce4e492f460d737f300..6a42f6854222d5719b5110aba9dcdf131d3c531e 100644 (file)
@@ -151,4 +151,8 @@ void check_enable_amd_mmconf_dmi(void);
 extern bool amd_acpi_c1e_quirk;
 void amd_check_disable_c1e(unsigned int port, u8 value);
 
+extern bool amd_legacy_ssbd;
+bool amd_setup_legacy_ssbd(void);
+void amd_set_legacy_ssbd(bool enable);
+
 #endif /* __AMD_H__ */
index ec44205309fc56604335f5649206fe4ecc2e5975..4e53056624a8f1da51f3255073ec7f02aacb6e2d 100644 (file)
@@ -22,6 +22,7 @@
 #include <xen/param.h>
 #include <xen/warning.h>
 
+#include <asm/amd.h>
 #include <asm/hvm/svm/svm.h>
 #include <asm/microcode.h>
 #include <asm/msr.h>
@@ -1244,7 +1245,8 @@ void __init init_speculation_mitigations(void)
     }
 
     /* Support VIRT_SPEC_CTRL.SSBD if AMD_SSBD is not available. */
-    if ( opt_msr_sc_hvm && !cpu_has_amd_ssbd && cpu_has_virt_ssbd )
+    if ( opt_msr_sc_hvm && !cpu_has_amd_ssbd &&
+         (cpu_has_virt_ssbd || (amd_legacy_ssbd && amd_setup_legacy_ssbd())) )
         setup_force_cpu_cap(X86_FEATURE_VIRT_SC_MSR_HVM);
 
     /* Figure out default_xen_spec_ctrl. */