[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 07/15] xen: x86: add functions to populate and destroy EPC for domain



Add per-domain structure to store SGX per-domain info. Currently only domain's
EPC base and size are stored. Also add new functions for further use:
    - hvm_populate_epc  # populate EPC when EPC base & size are notified.
    - hvm_reset_epc     # Reset domain's EPC to be invalid. Used when domain
                          goes to S3-S5, or being destroyed.
    - hvm_destroy_epc   # destroy and free domain's EPC.

Signed-off-by: Kai Huang <kai.huang@xxxxxxxxxxxxxxx>
---
 xen/arch/x86/hvm/vmx/sgx.c         | 315 +++++++++++++++++++++++++++++++++++++
 xen/arch/x86/hvm/vmx/vmx.c         |   3 +
 xen/include/asm-x86/hvm/vmx/sgx.h  |  14 ++
 xen/include/asm-x86/hvm/vmx/vmcs.h |   2 +
 4 files changed, 334 insertions(+)

diff --git a/xen/arch/x86/hvm/vmx/sgx.c b/xen/arch/x86/hvm/vmx/sgx.c
index f4c9b2f933..14379151e8 100644
--- a/xen/arch/x86/hvm/vmx/sgx.c
+++ b/xen/arch/x86/hvm/vmx/sgx.c
@@ -9,6 +9,8 @@
 #include <asm/msr.h>
 #include <xen/errno.h>
 #include <xen/mm.h>
+#include <xen/sched.h>
+#include <asm/p2m.h>
 #include <asm/hvm/vmx/sgx.h>
 #include <asm/hvm/vmx/vmcs.h>
 
@@ -90,6 +92,319 @@ void unmap_epc_page(void *addr)
     /* Nothing */
 }
 
+/* ENCLS opcode */
+#define ENCLS   .byte 0x0f, 0x01, 0xcf
+
+/*
+ * ENCLS leaf functions
+ *
+ * However currently we only needs EREMOVE..
+ */
+enum {
+    ECREATE = 0x0,
+    EADD    = 0x1,
+    EINIT   = 0x2,
+    EREMOVE = 0x3,
+    EDGBRD  = 0x4,
+    EDGBWR  = 0x5,
+    EEXTEND = 0x6,
+    ELDU    = 0x8,
+    EBLOCK  = 0x9,
+    EPA     = 0xA,
+    EWB     = 0xB,
+    ETRACK  = 0xC,
+    EAUG    = 0xD,
+    EMODPR  = 0xE,
+    EMODT   = 0xF,
+};
+
+/*
+ * ENCLS error code
+ *
+ * Currently we only need SGX_CHILD_PRESENT
+ */
+#define SGX_CHILD_PRESENT   13
+
+static inline int __encls(unsigned long rax, unsigned long rbx,
+                          unsigned long rcx, unsigned long rdx)
+{
+    int ret;
+
+    asm volatile ( "ENCLS;\n\t"
+            : "=a" (ret)
+            : "a" (rax), "b" (rbx), "c" (rcx), "d" (rdx)
+            : "memory", "cc");
+
+    return ret;
+}
+
+static inline int __eremove(void *epc)
+{
+    unsigned long rbx = 0, rdx = 0;
+
+    return __encls(EREMOVE, rbx, (unsigned long)epc, rdx);
+}
+
+static int sgx_eremove(struct epc_page *epg)
+{
+    void *addr = map_epc_page_to_xen(epg);
+    int ret;
+
+    BUG_ON(!addr);
+
+    ret =  __eremove(addr);
+
+    unmap_epc_page(addr);
+
+    return ret;
+}
+
+/*
+ * Reset domain's EPC with EREMOVE. free_epc indicates whether to free EPC
+ * pages during reset. This will be called when domain goes into S3-S5 state
+ * (with free_epc being false), and when domain is destroyed (with free_epc
+ * being true).
+ *
+ * It is possible that EREMOVE will be called for SECS when it still has
+ * children present, in which case SGX_CHILD_PRESENT will be returned. In this
+ * case, SECS page is kept to a tmp list and after all EPC pages have been
+ * called with EREMOVE, we call EREMOVE for all the SECS pages again, and this
+ * time SGX_CHILD_PRESENT should never occur as all children should have been
+ * removed.
+ *
+ * If unexpected error returned by EREMOVE, it means the EPC page becomes
+ * abnormal, so it will not be freed even free_epc is true, as further use of
+ * this EPC can cause unexpected error, potentially damaging other domains.
+ */
+static int __hvm_reset_epc(struct domain *d, unsigned long epc_base_pfn,
+        unsigned long epc_npages, bool_t free_epc)
+{
+    struct list_head secs_list;
+    struct list_head *p, *tmp;
+    unsigned long i;
+    int ret = 0;
+
+    INIT_LIST_HEAD(&secs_list);
+
+    for ( i = 0; i < epc_npages; i++ )
+    {
+        struct epc_page *epg;
+        unsigned long gfn;
+        mfn_t mfn;
+        p2m_type_t t;
+        int r;
+
+        gfn = i + epc_base_pfn;
+        mfn = get_gfn_query(d, gfn, &t);
+        if ( unlikely(mfn_eq(mfn, INVALID_MFN)) )
+        {
+            printk("Domain %d: Reset EPC error: invalid MFN for gfn 0x%lx\n",
+                    d->domain_id, gfn);
+            put_gfn(d, gfn);
+            ret = -EFAULT;
+            continue;
+        }
+
+        if ( unlikely(!p2m_is_epc(t)) )
+        {
+            printk("Domain %d: Reset EPC error: (gfn 0x%lx, mfn 0x%lx): " 
+                    "is not p2m_epc.\n", d->domain_id, gfn, mfn_x(mfn));
+            put_gfn(d, gfn);
+            ret = -EFAULT;
+            continue;
+        }
+
+        put_gfn(d, gfn);
+
+        epg = epc_mfn_to_page(mfn_x(mfn));
+
+        /* EREMOVE the EPC page to make it invalid */
+        r = sgx_eremove(epg);
+        if ( r == SGX_CHILD_PRESENT )
+        {
+            list_add_tail(&epg->list, &secs_list);
+            continue;
+        }
+
+        if ( r )
+        {
+            printk("Domain %d: Reset EPC error: (gfn 0x%lx, mfn 0x%lx): "
+                    "EREMOVE returns %d\n", d->domain_id, gfn, mfn_x(mfn), r);
+            ret = r;
+            if ( free_epc )
+                printk("WARNING: EPC (mfn 0x%lx) becomes abnormal. "
+                        "Remove it from useable EPC.", mfn_x(mfn));
+            continue;
+        }
+
+        if ( free_epc )
+        {
+            /* If EPC page is going to be freed, then also remove the mapping 
*/
+            if ( clear_epc_p2m_entry(d, gfn, mfn) )
+            {
+                printk("Domain %d: Reset EPC error: (gfn 0x%lx, mfn 0x%lx): "
+                        "clear p2m entry failed.\n", d->domain_id, gfn,
+                        mfn_x(mfn));
+                ret = -EFAULT;
+            }
+            free_epc_page(epg);
+        }
+    }
+
+    list_for_each_safe(p, tmp, &secs_list)
+    {
+        struct epc_page *epg = list_entry(p, struct epc_page, list);
+        int r;
+
+        r = sgx_eremove(epg);
+        if ( r )
+        {
+            printk("Domain %d: Reset EPC error: mfn 0x%lx: "
+                    "EREMOVE returns %d for SECS page\n",
+                    d->domain_id, epc_page_to_mfn(epg), r);
+            ret = r;
+            list_del(p);
+
+            if ( free_epc )
+                printk("WARNING: EPC (mfn 0x%lx) becomes abnormal. "
+                        "Remove it from useable EPC.",
+                        epc_page_to_mfn(epg));
+            continue;
+        }
+
+        if ( free_epc )
+            free_epc_page(epg);
+    }
+
+    return ret;
+}
+
+static void __hvm_unpopulate_epc(struct domain *d, unsigned long epc_base_pfn,
+        unsigned long populated_npages)
+{
+    unsigned long i;
+
+    for ( i = 0; i < populated_npages; i++ )
+    {
+        struct epc_page *epg;
+        unsigned long gfn;
+        mfn_t mfn;
+        p2m_type_t t;
+
+        gfn = i + epc_base_pfn;
+        mfn = get_gfn_query(d, gfn, &t);
+        if ( unlikely(mfn_eq(mfn, INVALID_MFN)) )
+        {
+            /*
+             * __hvm_unpopulate_epc only called when creating the domain on
+             * failure, therefore we can just ignore this error.
+             */
+            printk("%s: Domain %u gfn 0x%lx returns invalid mfn\n", __func__,
+                    d->domain_id, gfn);
+            put_gfn(d, gfn);
+            continue;
+        }
+
+        if ( unlikely(!p2m_is_epc(t)) )
+        {
+            printk("%s: Domain %u gfn 0x%lx returns non-EPC p2m type: %d\n",
+                    __func__, d->domain_id, gfn, (int)t);
+            put_gfn(d, gfn);
+            continue;
+        }
+
+        put_gfn(d, gfn);
+
+        if ( clear_epc_p2m_entry(d, gfn, mfn) )
+        {
+            printk("clear_epc_p2m_entry failed: gfn 0x%lx, mfn 0x%lx\n",
+                    gfn, mfn_x(mfn));
+            continue;
+        }
+
+        epg = epc_mfn_to_page(mfn_x(mfn));
+        free_epc_page(epg);
+    }
+}
+
+static int __hvm_populate_epc(struct domain *d, unsigned long epc_base_pfn,
+        unsigned long epc_npages)
+{
+    unsigned long i;
+    int ret;
+
+    for ( i = 0; i < epc_npages; i++ )
+    {
+        struct epc_page *epg = alloc_epc_page();
+        unsigned long mfn;
+
+        if ( !epg )
+        {
+            printk("%s: Out of EPC\n", __func__);
+            ret = -ENOMEM;
+            goto err;
+        }
+
+        mfn = epc_page_to_mfn(epg);
+        ret = set_epc_p2m_entry(d, i + epc_base_pfn, _mfn(mfn));
+        if ( ret )
+        {
+            printk("%s: set_epc_p2m_entry failed with %d: gfn 0x%lx, "
+                    "mfn 0x%lx\n", __func__, ret, i + epc_base_pfn, mfn);
+            free_epc_page(epg);
+            goto err;
+        }
+    }
+
+    return 0;
+
+err:
+    __hvm_unpopulate_epc(d, epc_base_pfn, i);
+    return ret;
+}
+
+int hvm_populate_epc(struct domain *d, unsigned long epc_base_pfn,
+        unsigned long epc_npages)
+{
+    struct sgx_domain *sgx = to_sgx(d);
+    int ret;
+
+    if ( hvm_epc_populated(d) )
+        return -EBUSY;
+
+    if ( !epc_base_pfn || !epc_npages )
+        return -EINVAL;
+
+    if ( (ret = __hvm_populate_epc(d, epc_base_pfn, epc_npages)) )
+        return ret;
+
+    sgx->epc_base_pfn = epc_base_pfn;
+    sgx->epc_npages = epc_npages;
+
+    return 0;
+}
+
+/*
+ *
+*
+ * This function returns error immediately if there's any unexpected error
+ * during this process.
+ */
+int hvm_reset_epc(struct domain *d, bool_t free_epc)
+{
+    struct sgx_domain *sgx = to_sgx(d);
+
+    if ( !hvm_epc_populated(d) )
+        return 0;
+
+    return __hvm_reset_epc(d, sgx->epc_base_pfn, sgx->epc_npages, free_epc);
+}
+
+void hvm_destroy_epc(struct domain *d)
+{
+    hvm_reset_epc(d, true);
+}
+
 static bool_t sgx_enabled_in_bios(void)
 {
     uint64_t val, sgx_enabled = IA32_FEATURE_CONTROL_SGX_ENABLE |
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index c53b24955a..243643111d 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -416,6 +416,9 @@ static int vmx_domain_initialise(struct domain *d)
 
 static void vmx_domain_destroy(struct domain *d)
 {
+    if ( hvm_epc_populated(d) )
+        hvm_destroy_epc(d);
+
     if ( !has_vlapic(d) )
         return;
 
diff --git a/xen/include/asm-x86/hvm/vmx/sgx.h 
b/xen/include/asm-x86/hvm/vmx/sgx.h
index ff420e006e..40f860662a 100644
--- a/xen/include/asm-x86/hvm/vmx/sgx.h
+++ b/xen/include/asm-x86/hvm/vmx/sgx.h
@@ -13,6 +13,7 @@
 #include <xen/init.h>
 #include <asm/processor.h>
 #include <xen/list.h>
+#include <public/hvm/params.h>   /* HVM_PARAM_SGX */
 
 #define SGX_CPUID 0x12
 
@@ -61,4 +62,17 @@ struct epc_page *epc_mfn_to_page(unsigned long mfn);
 void *map_epc_page_to_xen(struct epc_page *epg);
 void unmap_epc_page(void *addr);
 
+struct sgx_domain {
+    unsigned long epc_base_pfn;
+    unsigned long epc_npages;
+};
+
+#define to_sgx(d)   (&((d)->arch.hvm_domain.vmx.sgx))
+#define hvm_epc_populated(d)  (!!((d)->arch.hvm_domain.vmx.sgx.epc_base_pfn))
+
+int hvm_populate_epc(struct domain *d, unsigned long epc_base_pfn,
+        unsigned long epc_npages);
+int hvm_reset_epc(struct domain *d, bool_t free_epc);
+void hvm_destroy_epc(struct domain *d);
+
 #endif  /* __ASM_X86_HVM_VMX_SGX_H__ */
diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h 
b/xen/include/asm-x86/hvm/vmx/vmcs.h
index 889091da42..6cfa5c3310 100644
--- a/xen/include/asm-x86/hvm/vmx/vmcs.h
+++ b/xen/include/asm-x86/hvm/vmx/vmcs.h
@@ -20,6 +20,7 @@
 
 #include <asm/hvm/io.h>
 #include <irq_vectors.h>
+#include <asm/hvm/vmx/sgx.h>
 
 extern void vmcs_dump_vcpu(struct vcpu *v);
 extern void setup_vmcs_dump(void);
@@ -62,6 +63,7 @@ struct vmx_domain {
     unsigned long apic_access_mfn;
     /* VMX_DOMAIN_* */
     unsigned int status;
+    struct sgx_domain sgx;
 };
 
 struct pi_desc {
-- 
2.11.0


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.