diff mbox

[v4,07/26] xen/x86: Calculate maximum host and guest featuresets

Message ID 1458750989-28967-8-git-send-email-andrew.cooper3@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrew Cooper March 23, 2016, 4:36 p.m. UTC
All of this information will be used by the toolstack to make informed
levelling decisions for VMs, and by Xen to sanity check toolstack-provided
information.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
CC: Jan Beulich <JBeulich@suse.com>

v3:
 * Move as much as possible into .init.
 * Fix the handing of the shared bits for the cross-vendor case.
 * Fix extended check.
v4:
 * Fix copy&paste error in calculate_hvm_featureset()
---
 xen/arch/x86/cpuid.c        | 162 ++++++++++++++++++++++++++++++++++++++++++++
 xen/arch/x86/setup.c        |   3 +
 xen/include/asm-x86/cpuid.h |  17 +++++
 3 files changed, 182 insertions(+)

Comments

Jan Beulich March 29, 2016, 8:57 a.m. UTC | #1
>>> On 23.03.16 at 17:36, <andrew.cooper3@citrix.com> wrote:
> All of this information will be used by the toolstack to make informed
> levelling decisions for VMs, and by Xen to sanity check toolstack-provided
> information.

Not only am I still missing a sentence or two here on the two HVM
feature sets (namely why only one gets exposed), I'm also now
realizing that your intention of not exposing both in the public
interface is in contradiction with patch 6, which does expose both
of them, even if only in textual (comment) form. I.e. I think the
public header should then also get a note added that these
annotations are not part of the public interface.

Jan
diff mbox

Patch

diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c
index 77e008a..41439f8 100644
--- a/xen/arch/x86/cpuid.c
+++ b/xen/arch/x86/cpuid.c
@@ -1,14 +1,176 @@ 
 #include <xen/init.h>
 #include <xen/lib.h>
 #include <asm/cpuid.h>
+#include <asm/hvm/hvm.h>
+#include <asm/hvm/vmx/vmcs.h>
+#include <asm/processor.h>
 
 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;
+
+uint32_t __read_mostly raw_featureset[FSCAPINTS];
+uint32_t __read_mostly pv_featureset[FSCAPINTS];
+uint32_t __read_mostly hvm_featureset[FSCAPINTS];
+
+static void __init sanitise_featureset(uint32_t *fs)
+{
+    unsigned int i;
+
+    for ( i = 0; i < FSCAPINTS; ++i )
+    {
+        /* Clamp to known mask. */
+        fs[i] &= known_features[i];
+    }
+
+    /*
+     * Sort out shared bits.  We are constructing a featureset which needs to
+     * be applicable to a cross-vendor case.  Intel strictly clears the common
+     * bits in e1d, while AMD strictly duplicates them.
+     *
+     * We duplicate them here to be compatible with AMD while on Intel, and
+     * rely on logic closer to the guest to make the featureset stricter if
+     * emulating Intel.
+     */
+    fs[FEATURESET_e1d] = ((fs[FEATURESET_1d]  &  CPUID_COMMON_1D_FEATURES) |
+                          (fs[FEATURESET_e1d] & ~CPUID_COMMON_1D_FEATURES));
+}
+
+static void __init calculate_raw_featureset(void)
+{
+    unsigned int max, tmp;
+
+    max = cpuid_eax(0);
+
+    if ( max >= 1 )
+        cpuid(0x1, &tmp, &tmp,
+              &raw_featureset[FEATURESET_1c],
+              &raw_featureset[FEATURESET_1d]);
+    if ( max >= 7 )
+        cpuid_count(0x7, 0, &tmp,
+                    &raw_featureset[FEATURESET_7b0],
+                    &raw_featureset[FEATURESET_7c0],
+                    &tmp);
+    if ( max >= 0xd )
+        cpuid_count(0xd, 1,
+                    &raw_featureset[FEATURESET_Da1],
+                    &tmp, &tmp, &tmp);
+
+    max = cpuid_eax(0x80000000);
+    if ( (max >> 16) != 0x8000 )
+        return;
+
+    if ( max >= 0x80000001 )
+        cpuid(0x80000001, &tmp, &tmp,
+              &raw_featureset[FEATURESET_e1c],
+              &raw_featureset[FEATURESET_e1d]);
+    if ( max >= 0x80000007 )
+        cpuid(0x80000007, &tmp, &tmp, &tmp,
+              &raw_featureset[FEATURESET_e7d]);
+    if ( max >= 0x80000008 )
+        cpuid(0x80000008, &tmp,
+              &raw_featureset[FEATURESET_e8b],
+              &tmp, &tmp);
+}
+
+static void __init calculate_pv_featureset(void)
+{
+    unsigned int i;
+
+    for ( i = 0; i < FSCAPINTS; ++i )
+        pv_featureset[i] = host_featureset[i] & pv_featuremask[i];
+
+    /* Unconditionally claim to be able to set the hypervisor bit. */
+    __set_bit(X86_FEATURE_HYPERVISOR, pv_featureset);
+
+    /*
+     * Allow the toolstack to set HTT, X2APIC and CMP_LEGACY.  These bits
+     * affect how to interpret topology information in other cpuid leaves.
+     */
+    __set_bit(X86_FEATURE_HTT, pv_featureset);
+    __set_bit(X86_FEATURE_X2APIC, pv_featureset);
+    __set_bit(X86_FEATURE_CMP_LEGACY, pv_featureset);
+
+    sanitise_featureset(pv_featureset);
+}
+
+static void __init calculate_hvm_featureset(void)
+{
+    unsigned int i;
+    const uint32_t *hvm_featuremask;
+
+    if ( !hvm_enabled )
+        return;
+
+    hvm_featuremask = hvm_funcs.hap_supported ?
+        hvm_hap_featuremask : hvm_shadow_featuremask;
+
+    for ( i = 0; i < FSCAPINTS; ++i )
+        hvm_featureset[i] = host_featureset[i] & hvm_featuremask[i];
+
+    /* Unconditionally claim to be able to set the hypervisor bit. */
+    __set_bit(X86_FEATURE_HYPERVISOR, hvm_featureset);
+
+    /*
+     * Allow the toolstack to set HTT, X2APIC and CMP_LEGACY.  These bits
+     * affect how to interpret topology information in other cpuid leaves.
+     */
+    __set_bit(X86_FEATURE_HTT, hvm_featureset);
+    __set_bit(X86_FEATURE_X2APIC, hvm_featureset);
+    __set_bit(X86_FEATURE_CMP_LEGACY, hvm_featureset);
+
+    /*
+     * Xen can provide an APIC emulation to HVM guests even if the host's APIC
+     * isn't enabled.
+     */
+    __set_bit(X86_FEATURE_APIC, hvm_featureset);
+
+    /*
+     * On AMD, PV guests are entirely unable to use SYSENTER as Xen runs in
+     * long mode (and init_amd() has cleared it out of host capabilities), but
+     * HVM guests are able if running in protected mode.
+     */
+    if ( (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) &&
+         test_bit(X86_FEATURE_SEP, raw_featureset) )
+        __set_bit(X86_FEATURE_SEP, hvm_featureset);
+
+    /*
+     * With VT-x, some features are only supported by Xen if dedicated
+     * hardware support is also available.
+     */
+    if ( cpu_has_vmx )
+    {
+        if ( !(vmx_vmexit_control & VM_EXIT_CLEAR_BNDCFGS) ||
+             !(vmx_vmentry_control & VM_ENTRY_LOAD_BNDCFGS) )
+            __clear_bit(X86_FEATURE_MPX, hvm_featureset);
+
+        if ( !cpu_has_vmx_xsaves )
+            __clear_bit(X86_FEATURE_XSAVES, hvm_featureset);
+
+        if ( !cpu_has_vmx_pcommit )
+            __clear_bit(X86_FEATURE_PCOMMIT, hvm_featureset);
+    }
+
+    sanitise_featureset(hvm_featureset);
+}
+
+void __init calculate_featuresets(void)
+{
+    calculate_raw_featureset();
+    calculate_pv_featureset();
+    calculate_hvm_featureset();
+}
+
 static void __init __maybe_unused build_assertions(void)
 {
     BUILD_BUG_ON(ARRAY_SIZE(known_features) != FSCAPINTS);
     BUILD_BUG_ON(ARRAY_SIZE(special_features) != FSCAPINTS);
+    BUILD_BUG_ON(ARRAY_SIZE(pv_featuremask) != FSCAPINTS);
+    BUILD_BUG_ON(ARRAY_SIZE(hvm_shadow_featuremask) != FSCAPINTS);
+    BUILD_BUG_ON(ARRAY_SIZE(hvm_hap_featuremask) != FSCAPINTS);
 }
 
 /*
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index ee65f55..3696c31 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -50,6 +50,7 @@ 
 #include <asm/nmi.h>
 #include <asm/alternative.h>
 #include <asm/mc146818rtc.h>
+#include <asm/cpuid.h>
 
 /* opt_nosmp: If true, secondary processors are ignored. */
 static bool_t __initdata opt_nosmp;
@@ -1525,6 +1526,8 @@  void __init noreturn __start_xen(unsigned long mbi_p)
                "Multiple initrd candidates, picking module #%u\n",
                initrdidx);
 
+    calculate_featuresets();
+
     /*
      * Temporarily clear SMAP in CR4 to allow user-accesses in construct_dom0().
      * This saves a large number of corner cases interactions with
diff --git a/xen/include/asm-x86/cpuid.h b/xen/include/asm-x86/cpuid.h
index 0ecf357..5041bcd 100644
--- a/xen/include/asm-x86/cpuid.h
+++ b/xen/include/asm-x86/cpuid.h
@@ -6,12 +6,29 @@ 
 
 #define FSCAPINTS FEATURESET_NR_ENTRIES
 
+#define FEATURESET_1d     0 /* 0x00000001.edx      */
+#define FEATURESET_1c     1 /* 0x00000001.ecx      */
+#define FEATURESET_e1d    2 /* 0x80000001.edx      */
+#define FEATURESET_e1c    3 /* 0x80000001.ecx      */
+#define FEATURESET_Da1    4 /* 0x0000000d:1.eax    */
+#define FEATURESET_7b0    5 /* 0x00000007:0.ebx    */
+#define FEATURESET_7c0    6 /* 0x00000007:0.ecx    */
+#define FEATURESET_e7d    7 /* 0x80000007.edx      */
+#define FEATURESET_e8b    8 /* 0x80000008.ebx      */
+
 #ifndef __ASSEMBLY__
 #include <xen/types.h>
 
 extern const uint32_t known_features[FSCAPINTS];
 extern const uint32_t special_features[FSCAPINTS];
 
+extern uint32_t raw_featureset[FSCAPINTS];
+#define host_featureset boot_cpu_data.x86_capability
+extern uint32_t pv_featureset[FSCAPINTS];
+extern uint32_t hvm_featureset[FSCAPINTS];
+
+void calculate_featuresets(void);
+
 #endif /* __ASSEMBLY__ */
 #endif /* !__X86_CPUID_H__ */