x86/cpuid: Recalculate a domains CPUID policy when appropriate
authorAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 11 Jan 2017 11:59:02 +0000 (11:59 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 11 Jan 2017 11:59:02 +0000 (11:59 +0000)
Introduce recalculate_cpuid_policy() which clamps a CPUID policy based on the
domains current restrictions.

Each adjustment introduced here mirrors what currently happens in
{pv,hvm}_cpuid(), although some logic is expressed differently.

 * The clearing X86_FEATURE_LM for 32bit PV guests, sanitise_featureset()
   takes out all 64bit-dependent features in one go.

 * The toolstacks choice of X86_FEATURE_ITSC in (by default) clobbered in
   domain_cpuid(), but {pv,hvm}_cpuid() needed to account for the host ITSC
   value when masking the toolstack value.

This now requires that sanitise_featureset(), lookup_deep_deps() and
associated data needs to be available at runtime, so moves out of __init.

Recalculate the cpuid policy when:

 * The domain is first created
 * Switching a PV guest to being compat
 * Setting disable_migrate or vTSC modes
 * The toolstack sets new policy data

The disable_migrate code was previously common.  To compensate, move the code
to each archs arch_do_domctl(), as the implementations now differ.

From this point on, domains have full and correct feature-leaf information in
their CPUID policies, allowing for substantial cleanup and improvements.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Stefano Stabellini <sstabellini@kernel.org>
xen/arch/arm/domctl.c
xen/arch/x86/cpuid.c
xen/arch/x86/domain.c
xen/arch/x86/domctl.c
xen/arch/x86/time.c
xen/common/domctl.c
xen/include/asm-x86/cpufeature.h
xen/include/asm-x86/cpuid.h

index 09d9959ed78897cfe8eac79579fa755392c191ee..c5d1c33bc8b8ba8008398dc31a3681fbec5b0709 100644 (file)
@@ -115,6 +115,11 @@ long arch_do_domctl(struct xen_domctl *domctl, struct domain *d,
 
         return 0;
     }
+
+    case XEN_DOMCTL_disable_migrate:
+        d->disable_migrate = domctl->u.disable_migrate.disable;
+        return 0;
+
     default:
     {
         int rc;
index fcd9acc14d86be117a37c74542f7086cc617b632..fc3c90eba457b15dba8e7ec8e9938f840d9852f8 100644 (file)
 const uint32_t known_features[] = INIT_KNOWN_FEATURES;
 const uint32_t special_features[] = INIT_SPECIAL_FEATURES;
 
-static const uint32_t __initconst pv_featuremask[] = INIT_PV_FEATURES;
-static const uint32_t __initconst hvm_shadow_featuremask[] = INIT_HVM_SHADOW_FEATURES;
-static const uint32_t __initconst hvm_hap_featuremask[] = INIT_HVM_HAP_FEATURES;
-static const uint32_t __initconst deep_features[] = INIT_DEEP_FEATURES;
+static const uint32_t pv_featuremask[] = INIT_PV_FEATURES;
+static const uint32_t hvm_shadow_featuremask[] = INIT_HVM_SHADOW_FEATURES;
+static const uint32_t hvm_hap_featuremask[] = INIT_HVM_HAP_FEATURES;
+static const uint32_t deep_features[] = INIT_DEEP_FEATURES;
 
 #define EMPTY_LEAF ((struct cpuid_leaf){})
 
@@ -33,7 +33,7 @@ static void cpuid_count_leaf(uint32_t leaf, uint32_t subleaf,
     cpuid_count(leaf, subleaf, &data->a, &data->b, &data->c, &data->d);
 }
 
-static void __init sanitise_featureset(uint32_t *fs)
+static void sanitise_featureset(uint32_t *fs)
 {
     /* for_each_set_bit() uses unsigned longs.  Extend with zeroes. */
     uint32_t disabled_features[
@@ -232,12 +232,12 @@ void __init init_guest_cpuid(void)
     calculate_hvm_max_policy();
 }
 
-const uint32_t * __init lookup_deep_deps(uint32_t feature)
+const uint32_t *lookup_deep_deps(uint32_t feature)
 {
     static const struct {
         uint32_t feature;
         uint32_t fs[FSCAPINTS];
-    } deep_deps[] __initconst = INIT_DEEP_DEPS;
+    } deep_deps[] = INIT_DEEP_DEPS;
     unsigned int start = 0, end = ARRAY_SIZE(deep_deps);
 
     BUILD_BUG_ON(ARRAY_SIZE(deep_deps) != NR_DEEP_DEPS);
@@ -262,6 +262,60 @@ const uint32_t * __init lookup_deep_deps(uint32_t feature)
     return NULL;
 }
 
+void recalculate_cpuid_policy(struct domain *d)
+{
+    struct cpuid_policy *p = d->arch.cpuid;
+    const struct cpuid_policy *max =
+        is_pv_domain(d) ? &pv_max_policy : &hvm_max_policy;
+    uint32_t fs[FSCAPINTS], max_fs[FSCAPINTS];
+    unsigned int i;
+
+    cpuid_policy_to_featureset(p, fs);
+    memcpy(max_fs, max->fs, sizeof(max_fs));
+
+    /*
+     * HVM domains using Shadow paging have further restrictions on their
+     * available paging features.
+     */
+    if ( is_hvm_domain(d) && !hap_enabled(d) )
+    {
+        for ( i = 0; i < ARRAY_SIZE(max_fs); i++ )
+            max_fs[i] &= hvm_shadow_featuremask[i];
+    }
+
+    /*
+     * 32bit PV domains can't use any Long Mode features, and cannot use
+     * SYSCALL on non-AMD hardware.
+     */
+    if ( is_pv_32bit_domain(d) )
+    {
+        __clear_bit(X86_FEATURE_LM, max_fs);
+        if ( boot_cpu_data.x86_vendor != X86_VENDOR_AMD )
+            __clear_bit(X86_FEATURE_SYSCALL, max_fs);
+    }
+
+    /*
+     * ITSC is masked by default (so domains are safe to migrate), but a
+     * toolstack which has configured disable_migrate or vTSC for a domain may
+     * safely select it, and needs a way of doing so.
+     */
+    if ( cpu_has_itsc && (d->disable_migrate || d->arch.vtsc) )
+        __set_bit(X86_FEATURE_ITSC, max_fs);
+
+    /* Clamp the toolstacks choices to reality. */
+    for ( i = 0; i < ARRAY_SIZE(fs); i++ )
+        fs[i] &= max_fs[i];
+
+    sanitise_featureset(fs);
+
+    /* Fold host's FDP_EXCP_ONLY and NO_FPU_SEL into guest's view. */
+    fs[FEATURESET_7b0] &= ~special_features[FEATURESET_7b0];
+    fs[FEATURESET_7b0] |= (host_featureset[FEATURESET_7b0] &
+                           special_features[FEATURESET_7b0]);
+
+    cpuid_featureset_to_policy(fs, p);
+}
+
 int init_domain_cpuid_policy(struct domain *d)
 {
     d->arch.cpuid = xmalloc(struct cpuid_policy);
@@ -271,6 +325,8 @@ int init_domain_cpuid_policy(struct domain *d)
 
     *d->arch.cpuid = is_pv_domain(d) ? pv_max_policy : hvm_max_policy;
 
+    recalculate_cpuid_policy(d);
+
     return 0;
 }
 
index c1f95ccaffc49922fc2c86053bb76479650ee8ca..7d33c41165871125e3d924d7d8f2b9133570e198 100644 (file)
@@ -352,6 +352,7 @@ int switch_compat(struct domain *d)
     }
 
     domain_set_alloc_bitsize(d);
+    recalculate_cpuid_policy(d);
 
     d->arch.x87_fip_width = 4;
 
index ab141b106673e114f3c004f09619bbcae1a9a7b3..a37a6a135988f690a8bbb43fdf56a7ecbc1f9fef 100644 (file)
@@ -51,6 +51,30 @@ static int gdbsx_guest_mem_io(domid_t domid, struct xen_domctl_gdbsx_memio *iop)
 static void update_domain_cpuid_info(struct domain *d,
                                      const xen_domctl_cpuid_t *ctl)
 {
+    struct cpuid_policy *p = d->arch.cpuid;
+    const struct cpuid_leaf leaf = { ctl->eax, ctl->ebx, ctl->ecx, ctl->edx };
+
+    /* Insert ctl data into cpuid_policy. */
+    if ( ctl->input[0] < ARRAY_SIZE(p->basic.raw) )
+    {
+        if ( ctl->input[0] == 7 )
+        {
+            if ( ctl->input[1] < ARRAY_SIZE(p->feat.raw) )
+                p->feat.raw[ctl->input[1]] = leaf;
+        }
+        else if ( ctl->input[0] == XSTATE_CPUID )
+        {
+            if ( ctl->input[1] < ARRAY_SIZE(p->xstate.raw) )
+                p->xstate.raw[ctl->input[1]] = leaf;
+        }
+        else
+            p->basic.raw[ctl->input[0]] = leaf;
+    }
+    else if ( (ctl->input[0] - 0x80000000) < ARRAY_SIZE(p->extd.raw) )
+        p->extd.raw[ctl->input[0] - 0x80000000] = leaf;
+
+    recalculate_cpuid_policy(d);
+
     switch ( ctl->input[0] )
     {
     case 0: {
@@ -1409,6 +1433,11 @@ long arch_do_domctl(
         }
         break;
 
+    case XEN_DOMCTL_disable_migrate:
+        d->disable_migrate = domctl->u.disable_migrate.disable;
+        recalculate_cpuid_policy(d);
+        break;
+
     default:
         ret = iommu_do_domctl(domctl, d, u_domctl);
         break;
index b89fa1306870023c3f77ce1600aac3e4ccc926f8..e17da1b6672e2cfddc8989225efe7a9371a598c2 100644 (file)
@@ -2106,6 +2106,8 @@ void tsc_set_info(struct domain *d,
                                      d->arch.hvm_domain.sync_tsc);
         }
     }
+
+    recalculate_cpuid_policy(d);
 }
 
 /* vtsc may incur measurable performance degradation, diagnose with this */
index b0ee9619f970a3d7cb291dc294343b0993ae2fc8..12cf4a93918a395108ad9554e1b9a9de0d5dde3c 100644 (file)
@@ -1103,10 +1103,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
         copyback = 1;
         break;
 
-    case XEN_DOMCTL_disable_migrate:
-        d->disable_migrate = op->u.disable_migrate.disable;
-        break;
-
 #ifdef CONFIG_HAS_MEM_ACCESS
     case XEN_DOMCTL_set_access_required:
         if ( unlikely(current->domain == d) ) /* no domain_pause() */
index 932661647117658df39b48440640d11db748e3ab..f34d01c9763ee41d534c72f08d94d59e87b10ca6 100644 (file)
@@ -71,6 +71,7 @@
 #define cpu_has_eist           boot_cpu_has(X86_FEATURE_EIST)
 #define cpu_has_hypervisor     boot_cpu_has(X86_FEATURE_HYPERVISOR)
 #define cpu_has_cmp_legacy     boot_cpu_has(X86_FEATURE_CMP_LEGACY)
+#define cpu_has_itsc           boot_cpu_has(X86_FEATURE_ITSC)
 
 enum _cache_type {
     CACHE_TYPE_NULL = 0,
index 77a467a6e877bc69df06a24bb14c03b3be20665d..0592b385a1ee4638f57b220bad8f01f070f60c76 100644 (file)
@@ -86,13 +86,8 @@ struct cpuid_policy
      *
      * Per-domain objects:
      *
-     * - Host accurate:
-     *   - max_{,sub}leaf
-     *   - {xcr0,xss}_{high,low}
-     *   - All FEATURESET_* words
-     *
      * - Guest accurate:
-     *   - Nothing
+     *   - All FEATURESET_* words
      *
      * Everything else should be considered inaccurate, and not necesserily 0.
      */
@@ -202,6 +197,9 @@ extern struct cpuid_policy raw_policy, host_policy, pv_max_policy,
 /* Allocate and initialise a CPUID policy suitable for the domain. */
 int init_domain_cpuid_policy(struct domain *d);
 
+/* Clamp the CPUID policy to reality. */
+void recalculate_cpuid_policy(struct domain *d);
+
 void guest_cpuid(const struct vcpu *v, uint32_t leaf,
                  uint32_t subleaf, struct cpuid_leaf *res);