x86: Common cpuid faulting support
authorAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 11 Jan 2018 17:48:00 +0000 (17:48 +0000)
committerWei Liu <wei.liu2@citrix.com>
Tue, 16 Jan 2018 18:34:04 +0000 (18:34 +0000)
With CPUID Faulting offered to SVM guests, move Xen's faulting code to being
common rather than Intel specific.

This is necessary for nested Xen (inc. pv-shim mode) to prevent PV guests from
finding the outer HVM Xen leaves via native cpuid.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
xen/arch/x86/cpu/amd.c
xen/arch/x86/cpu/common.c
xen/arch/x86/cpu/intel.c
xen/include/asm-x86/cpuid.h
xen/include/asm-x86/processor.h

index 40c0bac80b91ed6c8093ff6f8c190e5a70423a6d..fc9677f0200aaa51e71b045dd441050c87564928 100644 (file)
@@ -198,11 +198,12 @@ static void __init noinline probe_masking_msrs(void)
 }
 
 /*
- * Context switch levelling state to the next domain.  A parameter of NULL is
- * used to context switch to the default host state (by the cpu bringup-code,
- * crash path, etc).
+ * Context switch CPUID masking state to the next domain.  Only called if
+ * CPUID Faulting isn't available, but masking MSRs have been detected.  A
+ * parameter of NULL is used to context switch to the default host state (by
+ * the cpu bringup-code, crash path, etc).
  */
-static void amd_ctxt_switch_levelling(const struct vcpu *next)
+static void amd_ctxt_switch_masking(const struct vcpu *next)
 {
        struct cpuidmasks *these_masks = &this_cpu(cpuidmasks);
        const struct domain *nextd = next ? next->domain : NULL;
@@ -263,6 +264,9 @@ static void __init noinline amd_init_levelling(void)
 {
        const struct cpuidmask *m = NULL;
 
+       if (probe_cpuid_faulting())
+               return;
+
        probe_masking_msrs();
 
        if (*opt_famrev != '\0') {
@@ -352,7 +356,7 @@ static void __init noinline amd_init_levelling(void)
        }
 
        if (levelling_caps)
-               ctxt_switch_levelling = amd_ctxt_switch_levelling;
+               ctxt_switch_masking = amd_ctxt_switch_masking;
 }
 
 /*
@@ -518,7 +522,7 @@ static void early_init_amd(struct cpuinfo_x86 *c)
        if (c == &boot_cpu_data)
                amd_init_levelling();
 
-       amd_ctxt_switch_levelling(NULL);
+       ctxt_switch_levelling(NULL);
 }
 
 static void init_amd(struct cpuinfo_x86 *c)
index 06e0eab13298492d985de8d4ad39417cc27cea70..1ff121887c210cf7bf249d7dbe54a5d58c375abe 100644 (file)
@@ -113,12 +113,80 @@ static const struct cpu_dev default_cpu = {
 };
 static const struct cpu_dev *this_cpu = &default_cpu;
 
-static void default_ctxt_switch_levelling(const struct vcpu *next)
+static DEFINE_PER_CPU(uint64_t, msr_misc_features);
+void (* __read_mostly ctxt_switch_masking)(const struct vcpu *next);
+
+bool __init probe_cpuid_faulting(void)
+{
+       uint64_t val;
+
+       if (rdmsr_safe(MSR_INTEL_PLATFORM_INFO, val) ||
+           !(val & MSR_PLATFORM_INFO_CPUID_FAULTING) ||
+           rdmsr_safe(MSR_INTEL_MISC_FEATURES_ENABLES,
+                      this_cpu(msr_misc_features)))
+       {
+               setup_clear_cpu_cap(X86_FEATURE_CPUID_FAULTING);
+               return false;
+       }
+
+       expected_levelling_cap |= LCAP_faulting;
+       levelling_caps |=  LCAP_faulting;
+       setup_force_cpu_cap(X86_FEATURE_CPUID_FAULTING);
+
+       return true;
+}
+
+static void set_cpuid_faulting(bool enable)
+{
+       uint64_t *this_misc_features = &this_cpu(msr_misc_features);
+       uint64_t val = *this_misc_features;
+
+       if (!!(val & MSR_MISC_FEATURES_CPUID_FAULTING) == enable)
+               return;
+
+       val ^= MSR_MISC_FEATURES_CPUID_FAULTING;
+
+       wrmsrl(MSR_INTEL_MISC_FEATURES_ENABLES, val);
+       *this_misc_features = val;
+}
+
+void ctxt_switch_levelling(const struct vcpu *next)
 {
-       /* Nop */
+       const struct domain *nextd = next ? next->domain : NULL;
+
+       if (cpu_has_cpuid_faulting) {
+               /*
+                * No need to alter the faulting setting if we are switching
+                * to idle; it won't affect any code running in idle context.
+                */
+               if (nextd && is_idle_domain(nextd))
+                       return;
+               /*
+                * We *should* be enabling faulting for the control domain.
+                *
+                * Unfortunately, the domain builder (having only ever been a
+                * PV guest) expects to be able to see host cpuid state in a
+                * native CPUID instruction, to correctly build a CPUID policy
+                * for HVM guests (notably the xstate leaves).
+                *
+                * This logic is fundimentally broken for HVM toolstack
+                * domains, and faulting causes PV guests to behave like HVM
+                * guests from their point of view.
+                *
+                * Future development plans will move responsibility for
+                * generating the maximum full cpuid policy into Xen, at which
+                * this problem will disappear.
+                */
+               set_cpuid_faulting(nextd && !is_control_domain(nextd) &&
+                                  (is_pv_domain(nextd) ||
+                                   next->arch.msr->
+                                   misc_features_enables.cpuid_faulting));
+               return;
+       }
+
+       if (ctxt_switch_masking)
+               ctxt_switch_masking(next);
 }
-void (* __read_mostly ctxt_switch_levelling)(const struct vcpu *next) =
-       default_ctxt_switch_levelling;
 
 bool_t opt_cpu_info;
 boolean_param("cpuinfo", opt_cpu_info);
index 8311952f1f131e8194fec8f7f51dafa33304eaa0..947796532111b96ff1c3b126d3d94ac64c1f0b68 100644 (file)
 
 #include "cpu.h"
 
-static bool __init probe_intel_cpuid_faulting(void)
-{
-       uint64_t x;
-
-       if (rdmsr_safe(MSR_INTEL_PLATFORM_INFO, x) ||
-           !(x & MSR_PLATFORM_INFO_CPUID_FAULTING))
-               return 0;
-
-       expected_levelling_cap |= LCAP_faulting;
-       levelling_caps |=  LCAP_faulting;
-       setup_force_cpu_cap(X86_FEATURE_CPUID_FAULTING);
-       return 1;
-}
-
-DEFINE_PER_CPU(bool, cpuid_faulting_enabled);
-
-static void set_cpuid_faulting(bool enable)
-{
-       bool *this_enabled = &this_cpu(cpuid_faulting_enabled);
-       uint32_t hi, lo;
-
-       ASSERT(cpu_has_cpuid_faulting);
-
-       if (*this_enabled == enable)
-               return;
-
-       rdmsr(MSR_INTEL_MISC_FEATURES_ENABLES, lo, hi);
-       lo &= ~MSR_MISC_FEATURES_CPUID_FAULTING;
-       if (enable)
-               lo |= MSR_MISC_FEATURES_CPUID_FAULTING;
-       wrmsr(MSR_INTEL_MISC_FEATURES_ENABLES, lo, hi);
-
-       *this_enabled = enable;
-}
-
 /*
  * Set caps in expected_levelling_cap, probe a specific masking MSR, and set
  * caps in levelling_caps if it is found, or clobber the MSR index if missing.
@@ -145,40 +110,17 @@ static void __init probe_masking_msrs(void)
 }
 
 /*
- * Context switch levelling state to the next domain.  A parameter of NULL is
- * used to context switch to the default host state (by the cpu bringup-code,
- * crash path, etc).
+ * Context switch CPUID masking state to the next domain.  Only called if
+ * CPUID Faulting isn't available, but masking MSRs have been detected.  A
+ * parameter of NULL is used to context switch to the default host state (by
+ * the cpu bringup-code, crash path, etc).
  */
-static void intel_ctxt_switch_levelling(const struct vcpu *next)
+static void intel_ctxt_switch_masking(const struct vcpu *next)
 {
        struct cpuidmasks *these_masks = &this_cpu(cpuidmasks);
        const struct domain *nextd = next ? next->domain : NULL;
-       const struct cpuidmasks *masks;
-
-       if (cpu_has_cpuid_faulting) {
-               /*
-                * We *should* be enabling faulting for the control domain.
-                *
-                * Unfortunately, the domain builder (having only ever been a
-                * PV guest) expects to be able to see host cpuid state in a
-                * native CPUID instruction, to correctly build a CPUID policy
-                * for HVM guests (notably the xstate leaves).
-                *
-                * This logic is fundimentally broken for HVM toolstack
-                * domains, and faulting causes PV guests to behave like HVM
-                * guests from their point of view.
-                *
-                * Future development plans will move responsibility for
-                * generating the maximum full cpuid policy into Xen, at which
-                * this problem will disappear.
-                */
-               set_cpuid_faulting(nextd && !is_control_domain(nextd) &&
-                                  (is_pv_domain(nextd) ||
-                                   next->arch.msr->misc_features_enables.cpuid_faulting));
-               return;
-       }
-
-       masks = (nextd && is_pv_domain(nextd) && nextd->arch.pv_domain.cpuidmasks)
+       const struct cpuidmasks *masks =
+               (nextd && is_pv_domain(nextd) && nextd->arch.pv_domain.cpuidmasks)
                ? nextd->arch.pv_domain.cpuidmasks : &cpuidmask_defaults;
 
         if (msr_basic) {
@@ -223,8 +165,10 @@ static void intel_ctxt_switch_levelling(const struct vcpu *next)
  */
 static void __init noinline intel_init_levelling(void)
 {
-       if (!probe_intel_cpuid_faulting())
-               probe_masking_msrs();
+       if (probe_cpuid_faulting())
+               return;
+
+       probe_masking_msrs();
 
        if (msr_basic) {
                uint32_t ecx, edx, tmp;
@@ -278,7 +222,7 @@ static void __init noinline intel_init_levelling(void)
        }
 
        if (levelling_caps)
-               ctxt_switch_levelling = intel_ctxt_switch_levelling;
+               ctxt_switch_masking = intel_ctxt_switch_masking;
 }
 
 static void early_init_intel(struct cpuinfo_x86 *c)
@@ -316,7 +260,7 @@ static void early_init_intel(struct cpuinfo_x86 *c)
        if (c == &boot_cpu_data)
                intel_init_levelling();
 
-       intel_ctxt_switch_levelling(NULL);
+       ctxt_switch_levelling(NULL);
 }
 
 /*
index d2dd841e158154552a932ce8d24178f6fe6e4f31..74d6f123e5bb69ee815b47d9c6eccc1449a021a4 100644 (file)
@@ -58,9 +58,6 @@ DECLARE_PER_CPU(struct cpuidmasks, cpuidmasks);
 /* Default masking MSR values, calculated at boot. */
 extern struct cpuidmasks cpuidmask_defaults;
 
-/* Whether or not cpuid faulting is available for the current domain. */
-DECLARE_PER_CPU(bool, cpuid_faulting_enabled);
-
 #define CPUID_GUEST_NR_BASIC      (0xdu + 1)
 #define CPUID_GUEST_NR_FEAT       (0u + 1)
 #define CPUID_GUEST_NR_CACHE      (5u + 1)
index 2962e83464b7bc142dccdd010ddab06265c3a479..80f841135565e024b12c837908fa0bfada94011b 100644 (file)
@@ -151,7 +151,9 @@ extern struct cpuinfo_x86 boot_cpu_data;
 extern struct cpuinfo_x86 cpu_data[];
 #define current_cpu_data cpu_data[smp_processor_id()]
 
-extern void (*ctxt_switch_levelling)(const struct vcpu *next);
+extern bool probe_cpuid_faulting(void);
+extern void ctxt_switch_levelling(const struct vcpu *next);
+extern void (*ctxt_switch_masking)(const struct vcpu *next);
 
 extern u64 host_pat;
 extern bool_t opt_cpu_info;