All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kai Huang <kaih.linux@gmail.com>
To: xen-devel@lists.xen.org
Cc: andrew.cooper3@citrix.com, kevin.tian@intel.com, jbeulich@suse.com
Subject: [PATCH 07/15] xen: x86: add functions to populate and destroy EPC for domain
Date: Sun,  9 Jul 2017 20:09:06 +1200	[thread overview]
Message-ID: <df8bb867cc935ff747632e83f0077a4a9ff7d718.1499586046.git.kai.huang@linux.intel.com> (raw)
In-Reply-To: <4b8baf9779038897e6ba2ed4ac0a3e9663db2756.1499586046.git.kai.huang@linux.intel.com>
In-Reply-To: <cover.1499586046.git.kai.huang@linux.intel.com>

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@linux.intel.com>
---
 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@lists.xen.org
https://lists.xen.org/xen-devel

  parent reply	other threads:[~2017-07-09  8:09 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-09  8:03 [RFC PATCH 00/15] RFC: SGX virtualization design and draft patches Kai Huang
2017-07-09  8:04 ` [PATCH 01/15] xen: x86: expose SGX to HVM domain in CPU featureset Kai Huang
2017-07-12 11:09   ` Andrew Cooper
2017-07-17  6:20     ` Huang, Kai
2017-07-18 10:12   ` Andrew Cooper
2017-07-18 22:41     ` Huang, Kai
2017-07-09  8:09 ` [PATCH 02/15] xen: vmx: detect ENCLS VMEXIT Kai Huang
2017-07-12 11:11   ` Andrew Cooper
2017-07-12 18:54     ` Jan Beulich
2017-07-13  4:57       ` Huang, Kai
2017-07-09  8:09 ` [PATCH 03/15] xen: x86: add early stage SGX feature detection Kai Huang
2017-07-19 14:23   ` Andrew Cooper
2017-07-21  9:17     ` Huang, Kai
2017-07-22  1:06       ` Huang, Kai
2017-07-09  8:09 ` [PATCH 06/15] xen: x86: add SGX basic EPC management Kai Huang
2017-07-09  8:09 ` Kai Huang [this message]
2017-07-09  8:09 ` [PATCH 09/15] xen: vmx: handle SGX related MSRs Kai Huang
2017-07-19 17:27   ` Andrew Cooper
2017-07-21  9:42     ` Huang, Kai
2017-07-22  1:37       ` Huang, Kai
2017-07-09  8:09 ` [PATCH 10/15] xen: vmx: handle ENCLS VMEXIT Kai Huang
2017-07-09  8:09 ` [PATCH 11/15] xen: vmx: handle VMEXIT from SGX enclave Kai Huang
2017-07-09  8:09 ` [PATCH 12/15] xen: x86: reset EPC when guest got suspended Kai Huang
2017-07-09  8:10 ` [PATCH 04/15] xen: mm: add ioremap_cache Kai Huang
2017-07-11 20:14   ` Julien Grall
2017-07-12  1:52     ` Huang, Kai
2017-07-12  7:13       ` Julien Grall
2017-07-13  5:01         ` Huang, Kai
2017-07-12  6:17     ` Jan Beulich
2017-07-13  4:59       ` Huang, Kai
2017-07-09  8:10 ` [PATCH 08/15] xen: x86: add SGX cpuid handling support Kai Huang
2017-07-12 10:56   ` Andrew Cooper
2017-07-13  5:42     ` Huang, Kai
2017-07-14  7:37       ` Andrew Cooper
2017-07-14 11:08         ` Jan Beulich
2017-07-17  6:16         ` Huang, Kai
2017-07-09  8:12 ` [PATCH 05/15] xen: p2m: new 'p2m_epc' type for EPC mapping Kai Huang
2017-07-12 11:01   ` Andrew Cooper
2017-07-12 12:21     ` George Dunlap
2017-07-13  5:56       ` Huang, Kai
2017-07-09  8:14 ` [PATCH 13/15] xen: tools: add new 'epc' parameter support Kai Huang
2017-07-09  8:15 ` [PATCH 14/15] xen: tools: add SGX to applying CPUID policy Kai Huang
2017-07-09  8:16 ` [PATCH 15/15] xen: tools: expose EPC in ACPI table Kai Huang
2017-07-12 11:05   ` Andrew Cooper
2017-07-13  8:23     ` Huang, Kai
2017-07-14 11:31   ` Jan Beulich
2017-07-17  6:11     ` Huang, Kai
2017-07-17 10:54   ` Roger Pau Monné
2017-07-18  8:36     ` Huang, Kai
2017-07-18 10:21       ` Roger Pau Monné
2017-07-18 22:44         ` Huang, Kai
2017-07-11 14:13 ` [RFC PATCH 00/15] RFC: SGX virtualization design and draft patches Andrew Cooper
2017-07-17  6:08   ` Huang, Kai
2017-07-21  9:04     ` Huang, Kai
2017-07-17  9:16 ` Wei Liu
2017-07-18  8:22   ` Huang, Kai
2017-07-28 13:40     ` Wei Liu
2017-07-31  8:37       ` Huang, Kai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=df8bb867cc935ff747632e83f0077a4a9ff7d718.1499586046.git.kai.huang@linux.intel.com \
    --to=kaih.linux@gmail.com \
    --cc=andrew.cooper3@citrix.com \
    --cc=jbeulich@suse.com \
    --cc=kevin.tian@intel.com \
    --cc=xen-devel@lists.xen.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.