x86/cpuid: Introduce recalculate_xstate()
authorAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 4 Jan 2017 15:00:23 +0000 (15:00 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 18 Jan 2017 14:32:30 +0000 (14:32 +0000)
All data in the xstate union, other than the Da1 feature word, is derived from
other state; either feature bits from other words, or layout information which
has already been collected by Xen's xstate driver.

Recalculate the xstate information for each policy object when the feature
bits may have changed.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/cpuid.c
xen/include/asm-x86/cpuid.h

index bcdac03dca82979cee5888d6fba37f2cb784088e..619ead8795a2d09d2293bd29e1c550a837353273 100644 (file)
@@ -80,6 +80,88 @@ static void sanitise_featureset(uint32_t *fs)
                           (fs[FEATURESET_e1d] & ~CPUID_COMMON_1D_FEATURES));
 }
 
+static void recalculate_xstate(struct cpuid_policy *p)
+{
+    uint64_t xstates = XSTATE_FP_SSE;
+    uint32_t xstate_size = XSTATE_AREA_MIN_SIZE;
+    unsigned int i, Da1 = p->xstate.Da1;
+
+    /*
+     * The Da1 leaf is the only piece of information preserved in the common
+     * case.  Everything else is derived from other feature state.
+     */
+    memset(&p->xstate, 0, sizeof(p->xstate));
+
+    if ( !p->basic.xsave )
+        return;
+
+    if ( p->basic.avx )
+    {
+        xstates |= XSTATE_YMM;
+        xstate_size = max(xstate_size,
+                          xstate_offsets[_XSTATE_YMM] +
+                          xstate_sizes[_XSTATE_YMM]);
+    }
+
+    if ( p->feat.mpx )
+    {
+        xstates |= XSTATE_BNDREGS | XSTATE_BNDCSR;
+        xstate_size = max(xstate_size,
+                          xstate_offsets[_XSTATE_BNDCSR] +
+                          xstate_sizes[_XSTATE_BNDCSR]);
+    }
+
+    if ( p->feat.avx512f )
+    {
+        xstates |= XSTATE_OPMASK | XSTATE_ZMM | XSTATE_HI_ZMM;
+        xstate_size = max(xstate_size,
+                          xstate_offsets[_XSTATE_HI_ZMM] +
+                          xstate_sizes[_XSTATE_HI_ZMM]);
+    }
+
+    if ( p->feat.pku )
+    {
+        xstates |= XSTATE_PKRU;
+        xstate_size = max(xstate_size,
+                          xstate_offsets[_XSTATE_PKRU] +
+                          xstate_sizes[_XSTATE_PKRU]);
+    }
+
+    if ( p->extd.lwp )
+    {
+        xstates |= XSTATE_LWP;
+        xstate_size = max(xstate_size,
+                          xstate_offsets[_XSTATE_LWP] +
+                          xstate_sizes[_XSTATE_LWP]);
+    }
+
+    p->xstate.max_size  =  xstate_size;
+    p->xstate.xcr0_low  =  xstates & ~XSTATE_XSAVES_ONLY;
+    p->xstate.xcr0_high = (xstates & ~XSTATE_XSAVES_ONLY) >> 32;
+
+    p->xstate.Da1 = Da1;
+    if ( p->xstate.xsaves )
+    {
+        p->xstate.xss_low   =  xstates & XSTATE_XSAVES_ONLY;
+        p->xstate.xss_high  = (xstates & XSTATE_XSAVES_ONLY) >> 32;
+    }
+    else
+        xstates &= ~XSTATE_XSAVES_ONLY;
+
+    for ( i = 2; i < min(63ul, ARRAY_SIZE(p->xstate.comp)); ++i )
+    {
+        uint64_t curr_xstate = 1ul << i;
+
+        if ( !(xstates & curr_xstate) )
+            continue;
+
+        p->xstate.comp[i].size   = xstate_sizes[i];
+        p->xstate.comp[i].offset = xstate_offsets[i];
+        p->xstate.comp[i].xss    = curr_xstate & XSTATE_XSAVES_ONLY;
+        p->xstate.comp[i].align  = curr_xstate & xstate_align;
+    }
+}
+
 static void __init calculate_raw_policy(void)
 {
     struct cpuid_policy *p = &raw_policy;
@@ -149,6 +231,7 @@ static void __init calculate_host_policy(void)
               0x80000000u + ARRAY_SIZE(p->extd.raw) - 1);
 
     cpuid_featureset_to_policy(boot_cpu_data.x86_capability, p);
+    recalculate_xstate(p);
 }
 
 static void __init calculate_pv_max_policy(void)
@@ -168,6 +251,7 @@ static void __init calculate_pv_max_policy(void)
 
     sanitise_featureset(pv_featureset);
     cpuid_featureset_to_policy(pv_featureset, p);
+    recalculate_xstate(p);
 }
 
 static void __init calculate_hvm_max_policy(void)
@@ -221,6 +305,7 @@ static void __init calculate_hvm_max_policy(void)
 
     sanitise_featureset(hvm_featureset);
     cpuid_featureset_to_policy(hvm_featureset, p);
+    recalculate_xstate(p);
 }
 
 void __init init_guest_cpuid(void)
@@ -328,6 +413,7 @@ void recalculate_cpuid_policy(struct domain *d)
                            special_features[FEATURESET_7b0]);
 
     cpuid_featureset_to_policy(fs, p);
+    recalculate_xstate(p);
 }
 
 int init_domain_cpuid_policy(struct domain *d)
index 24ad3e0b86a10f5830cd1cbf2fc6c13092b80f9c..e5140cac718aebdd6f0969c4252fed51abb44ebf 100644 (file)
@@ -77,18 +77,15 @@ struct cpuid_policy
      *
      * Global *_policy objects:
      *
-     * - Host accurate:
-     *   - {xcr0,xss}_{high,low}
-     *
      * - Guest accurate:
-     *   - All of the feat union
+     *   - All of the feat and xstate unions
      *   - max_{,sub}leaf
      *   - All FEATURESET_* words
      *
      * Per-domain objects:
      *
      * - Guest accurate:
-     *   - All of the feat union
+     *   - All of the feat and xstate unions
      *   - max_{,sub}leaf
      *   - All FEATURESET_* words
      *
@@ -143,9 +140,10 @@ struct cpuid_policy
     /* Xstate feature leaf: 0x0000000D[xx] */
     union {
         struct cpuid_leaf raw[CPUID_GUEST_NR_XSTATE];
+
         struct {
             /* Subleaf 0. */
-            uint32_t xcr0_low, /* b */:32, /* c */:32, xcr0_high;
+            uint32_t xcr0_low, /* b */:32, max_size, xcr0_high;
 
             /* Subleaf 1. */
             union {
@@ -154,6 +152,13 @@ struct cpuid_policy
             };
             uint32_t /* b */:32, xss_low, xss_high;
         };
+
+        /* Per-component common state.  Valid for i >= 2. */
+        struct {
+            uint32_t size, offset;
+            bool xss:1, align:1;
+            uint32_t _res_d;
+        } comp[CPUID_GUEST_NR_XSTATE];
     } xstate;
 
     /* Extended leaves: 0x800000xx */