diff --git a/xen/arch/x86/hvm/vmx/Makefile b/xen/arch/x86/hvm/vmx/Makefile
index 04a29ce59d..f6bcf0d143 100644
--- a/xen/arch/x86/hvm/vmx/Makefile
+++ b/xen/arch/x86/hvm/vmx/Makefile
@@ -4,3 +4,4 @@ obj-y += realmode.o
 obj-y += vmcs.o
 obj-y += vmx.o
 obj-y += vvmx.o
+obj-y += sgx.o
diff --git a/xen/arch/x86/hvm/vmx/sgx.c b/xen/arch/x86/hvm/vmx/sgx.c
new file mode 100644
index 0000000000..6b41469371
--- /dev/null
+++ b/xen/arch/x86/hvm/vmx/sgx.c
@@ -0,0 +1,208 @@
+/*
+ * Intel Software Guard Extensions support
+ *
+ * Author: Kai Huang <kai.huang@linux.intel.com>
+ */
+
+#include <asm/cpufeature.h>
+#include <asm/msr-index.h>
+#include <asm/msr.h>
+#include <asm/hvm/vmx/sgx.h>
+#include <asm/hvm/vmx/vmcs.h>
+
+static struct sgx_cpuinfo __read_mostly sgx_cpudata[NR_CPUS];
+static struct sgx_cpuinfo __read_mostly boot_sgx_cpudata;
+
+static bool_t sgx_enabled_in_bios(void)
+{
+    uint64_t val, sgx_enabled = IA32_FEATURE_CONTROL_SGX_ENABLE |
+                                IA32_FEATURE_CONTROL_LOCK;
+
+    rdmsrl(MSR_IA32_FEATURE_CONTROL, val);
+
+    return (val & sgx_enabled) == sgx_enabled;
+}
+
+static void __detect_sgx(int cpu)
+{
+    struct sgx_cpuinfo *sgxinfo = &sgx_cpudata[cpu];
+    u32 eax, ebx, ecx, edx;
+
+    memset(sgxinfo, 0, sizeof(*sgxinfo));
+
+    /*
+     * In reality if SGX is not enabled in BIOS, SGX CPUID should report
+     * invalid SGX info, but we do the check anyway to make sure.
+     */
+    if ( !sgx_enabled_in_bios() )
+    {
+        printk("CPU%d: SGX disabled in BIOS.\n", cpu);
+        goto not_supported;
+    }
+
+    /*
+     * CPUID.0x12.0x0:
+     *
+     *  EAX [0]:    whether SGX1 is supported.
+     *      [1]:    whether SGX2 is supported.
+     *  EBX [31:0]: miscselect
+     *  ECX [31:0]: reserved
+     *  EDX [7:0]:  MaxEnclaveSize_Not64
+     *      [15:8]: MaxEnclaveSize_64
+     */
+    cpuid_count(SGX_CPUID, 0x0, &eax, &ebx, &ecx, &edx);
+    sgxinfo->cap = eax & (SGX_CAP_SGX1 | SGX_CAP_SGX2);
+    sgxinfo->miscselect = ebx;
+    sgxinfo->max_enclave_size32 = edx & 0xff;
+    sgxinfo->max_enclave_size64 = (edx & 0xff00) >> 8;
+
+    if ( !(eax & SGX_CAP_SGX1) )
+    {
+        /* We may reach here if BIOS doesn't enable SGX */
+        printk("CPU%d: CPUID.0x12.0x0 reports not SGX support.\n", cpu);
+        goto not_supported;
+    }
+
+    /*
+     * CPUID.0x12.0x1:
+     *
+     *  EAX [31:0]: bitmask of 1-setting of SECS.ATTRIBUTES[31:0]
+     *  EBX [31:0]: bitmask of 1-setting of SECS.ATTRIBUTES[63:32]
+     *  ECX [31:0]: bitmask of 1-setting of SECS.ATTRIBUTES[95:64]
+     *  EDX [31:0]: bitmask of 1-setting of SECS.ATTRIBUTES[127:96]
+     */
+    cpuid_count(SGX_CPUID, 0x1, &eax, &ebx, &ecx, &edx);
+    sgxinfo->secs_attr_bitmask[0] = eax;
+    sgxinfo->secs_attr_bitmask[1] = ebx;
+    sgxinfo->secs_attr_bitmask[2] = ecx;
+    sgxinfo->secs_attr_bitmask[3] = edx;
+
+    /*
+     * CPUID.0x12.0x2:
+     *
+     *  EAX [3:0]:      0000: this sub-leaf is invalid
+     *                  0001: this sub-leaf enumerates EPC resource
+     *      [11:4]:     reserved
+     *      [31:12]:    bits 31:12 of physical address of EPC base (when
+     *                  EAX[3:0] is 0001, which applies to following)
+     *  EBX [19:0]:     bits 51:32 of physical address of EPC base
+     *      [31:20]:    reserved
+     *  ECX [3:0]:      0000: EDX:ECX are 0
+     *                  0001: this is EPC section.
+     *      [11:4]:     reserved
+     *      [31:12]:    bits 31:12 of EPC size
+     *  EDX [19:0]:     bits 51:32 of EPC size
+     *      [31:20]:    reserved
+     *
+     *  TODO: So far assume there's only one EPC resource.
+     */
+    cpuid_count(SGX_CPUID, 0x2, &eax, &ebx, &ecx, &edx);
+    if ( !(eax & 0x1) || !(ecx & 0x1) )
+    {
+        /* We may reach here if BIOS doesn't enable SGX */
+        printk("CPU%d: CPUID.0x12.0x2 reports invalid EPC resource.\n", cpu);
+        goto not_supported;
+    }
+    sgxinfo->epc_base = (((u64)(ebx & 0xfffff)) << 32) | (eax & 0xfffff000);
+    sgxinfo->epc_size = (((u64)(edx & 0xfffff)) << 32) | (ecx & 0xfffff000);
+
+    return;
+
+not_supported:
+    memset(sgxinfo, 0, sizeof(*sgxinfo));
+}
+
+void detect_sgx(int cpu)
+{
+    /* Caller (vmx_cpu_up) has checked cpu_has_vmx_encls */
+    if ( !cpu_has_sgx || boot_cpu_data.cpuid_level < SGX_CPUID )
+    {
+        setup_clear_cpu_cap(X86_FEATURE_SGX);
+        return;
+    }
+
+    __detect_sgx(cpu);
+}
+
+static void __init disable_sgx(void)
+{
+    memset(&boot_sgx_cpudata, 0, sizeof (struct sgx_cpuinfo));
+    /*
+     * X86_FEATURE_SGX is cleared in boot_cpu_data so that cpu_has_sgx
+     * can be used anywhere to check whether SGX is supported by Xen.
+     *
+     * FIXME: also adjust boot_cpu_data.cpuid_level ?
+     */
+    setup_clear_cpu_cap(X86_FEATURE_SGX);
+}
+
+static void __init print_sgx_cpuinfo(struct sgx_cpuinfo *sgxinfo)
+{
+    printk("SGX: \n"
+           "\tCAP: %s,%s\n"
+           "\tEPC: [0x%"PRIx64", 0x%"PRIx64")\n",
+           boot_sgx_cpudata.cap & SGX_CAP_SGX1 ? "SGX1" : "",
+           boot_sgx_cpudata.cap & SGX_CAP_SGX2 ? "SGX2" : "",
+           boot_sgx_cpudata.epc_base,
+           boot_sgx_cpudata.epc_base + boot_sgx_cpudata.epc_size);
+}
+
+/*
+ * Check SGX CPUID info all for all CPUs, and only support SGX when all CPUs
+ * report the same SGX info. SDM (37.7.2 Intel SGX Resource Enumeration Leaves)
+ * says "software should not assume that if Intel SGX instructions are
+ * supported on one hardware thread, they are also supported elsewhere.".
+ * For simplicity, we only support SGX when all CPUs reports consistent SGX
+ * info.
+ *
+ * boot_sgx_cpudata is set to store the *common* SGX CPUID info.
+ */
+static bool_t __init check_sgx_consistency(void)
+{
+    int i;
+
+    for_each_online_cpu ( i )
+    {
+        struct sgx_cpuinfo *s = &sgx_cpudata[i];
+
+        if ( memcmp(&boot_sgx_cpudata, s, sizeof (*s)) )
+        {
+            printk("SGX inconsistency between CPU 0 and CPU %d. "
+                    "Disable SGX.\n", i);
+            memset(&boot_sgx_cpudata, 0,  sizeof (*s));
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static int __init sgx_init(void)
+{
+    /* Assume CPU 0 is always online */
+    boot_sgx_cpudata = sgx_cpudata[0];
+
+    if ( !(boot_sgx_cpudata.cap & SGX_CAP_SGX1) )
+        goto not_supported;
+
+    if ( !check_sgx_consistency() )
+        goto not_supported;
+
+    print_sgx_cpuinfo(&boot_sgx_cpudata);
+
+    return 0;
+not_supported:
+    disable_sgx();
+    return -EINVAL;
+}
+__initcall(sgx_init);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c
index ae7e6f9321..518133bbfd 100644
--- a/xen/arch/x86/hvm/vmx/vmcs.c
+++ b/xen/arch/x86/hvm/vmx/vmcs.c
@@ -40,6 +40,7 @@
 #include <asm/shadow.h>
 #include <asm/tboot.h>
 #include <asm/apic.h>
+#include <asm/hvm/vmx/sgx.h>
 
 static bool_t __read_mostly opt_vpid_enabled = 1;
 boolean_param("vpid", opt_vpid_enabled);
@@ -696,6 +697,9 @@ int vmx_cpu_up(void)
 
     vmx_pi_per_cpu_init(cpu);
 
+    if ( cpu_has_vmx_encls )
+        detect_sgx(cpu);
+
     return 0;
 }
 
diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h
index 84cc51d2bd..9793f8c1c5 100644
--- a/xen/include/asm-x86/cpufeature.h
+++ b/xen/include/asm-x86/cpufeature.h
@@ -85,6 +85,7 @@
 
 /* CPUID level 0x00000007:0.ebx */
 #define cpu_has_fsgsbase        boot_cpu_has(X86_FEATURE_FSGSBASE)
+#define cpu_has_sgx             boot_cpu_has(X86_FEATURE_SGX)
 #define cpu_has_bmi1            boot_cpu_has(X86_FEATURE_BMI1)
 #define cpu_has_hle             boot_cpu_has(X86_FEATURE_HLE)
 #define cpu_has_avx2            boot_cpu_has(X86_FEATURE_AVX2)
diff --git a/xen/include/asm-x86/hvm/vmx/sgx.h b/xen/include/asm-x86/hvm/vmx/sgx.h
new file mode 100644
index 0000000000..5414d8237e
--- /dev/null
+++ b/xen/include/asm-x86/hvm/vmx/sgx.h
@@ -0,0 +1,45 @@
+/*
+ * Intel Software Guard Extensions support
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * Author: Kai Huang <kai.huang@linux.intel.com>
+ */
+#ifndef __ASM_X86_HVM_VMX_SGX_H__
+#define __ASM_X86_HVM_VMX_SGX_H__
+
+#include <xen/config.h>
+#include <xen/types.h>
+#include <xen/init.h>
+#include <asm/processor.h>
+
+#define SGX_CPUID 0x12
+
+/*
+ * SGX info reported by SGX CPUID.
+ *
+ * TODO:
+ *
+ * SDM (37.7.2 Intel SGX Resource Enumeration Leaves) actually says it's
+ * possible there are multiple EPC resources on the machine (CPUID.0x12,
+ * ECX starting with 0x2 enumerates available EPC resources until invalid
+ * EPC resource is returned). But this is only for multiple socket server,
+ * which we current don't support now (there are additional things need to
+ * be done as well). So far for simplicity we assume there is only one EPC.
+ */
+struct sgx_cpuinfo {
+#define SGX_CAP_SGX1    (1UL << 0)
+#define SGX_CAP_SGX2    (1UL << 1)
+    uint32_t cap;
+    uint32_t miscselect;
+    uint8_t max_enclave_size64;
+    uint8_t max_enclave_size32;
+    uint32_t secs_attr_bitmask[4];
+    uint64_t epc_base;
+    uint64_t epc_size;
+};
+
+/* Detect SGX info for particular CPU via SGX CPUID */
+void detect_sgx(int cpu);
+
+#endif  /* __ASM_X86_HVM_VMX_SGX_H__ */
