[xen master] x86/hvm/ioreq: add a new mappable resource type...

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[xen master] x86/hvm/ioreq: add a new mappable resource type...

patchbot
commit 6e387461ed6d8952de43a2cc76a4e5e75043f3e7
Author:     Paul Durrant <[hidden email]>
AuthorDate: Wed Aug 9 17:39:01 2017 +0100
Commit:     Wei Liu <[hidden email]>
CommitDate: Tue Apr 3 17:05:07 2018 +0100

    x86/hvm/ioreq: add a new mappable resource type...
   
    ... XENMEM_resource_ioreq_server
   
    This patch adds support for a new resource type that can be mapped using
    the XENMEM_acquire_resource memory op.
   
    If an emulator makes use of this resource type then, instead of mapping
    gfns, the IOREQ server will allocate pages which are assigned to the
    emulating domain. These pages will never be present in the P2M of the
    guest at any point (and are not even shared with the guest) and so are not
    vulnerable to any direct attack by the guest.
   
    NOTE: Use of the new resource type is not compatible with use of
          XEN_DMOP_get_ioreq_server_info unless the XEN_DMOP_no_gfns flag is
          set.
   
    Signed-off-by: Paul Durrant <[hidden email]>
    Reviewed-by: Jan Beulich <[hidden email]>
    Acked-by: Julien Grall <[hidden email]>
---
 xen/arch/x86/hvm/ioreq.c        | 167 ++++++++++++++++++++++++++++++++++++++++
 xen/arch/x86/mm.c               |  47 +++++++++++
 xen/common/memory.c             |   3 +-
 xen/include/asm-arm/mm.h        |   8 ++
 xen/include/asm-x86/hvm/ioreq.h |   2 +
 xen/include/asm-x86/mm.h        |   5 ++
 xen/include/public/hvm/dm_op.h  |   4 +
 xen/include/public/memory.h     |   9 +++
 8 files changed, 244 insertions(+), 1 deletion(-)

diff --git a/xen/arch/x86/hvm/ioreq.c b/xen/arch/x86/hvm/ioreq.c
index cad6567f88..9435291e87 100644
--- a/xen/arch/x86/hvm/ioreq.c
+++ b/xen/arch/x86/hvm/ioreq.c
@@ -270,6 +270,19 @@ static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
     struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
     int rc;
 
+    if ( iorp->page )
+    {
+        /*
+         * If a page has already been allocated (which will happen on
+         * demand if hvm_get_ioreq_server_frame() is called), then
+         * mapping a guest frame is not permitted.
+         */
+        if ( gfn_eq(iorp->gfn, INVALID_GFN) )
+            return -EPERM;
+
+        return 0;
+    }
+
     if ( d->is_dying )
         return -EINVAL;
 
@@ -292,6 +305,70 @@ static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
     return rc;
 }
 
+static int hvm_alloc_ioreq_mfn(struct hvm_ioreq_server *s, bool buf)
+{
+    struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
+
+    if ( iorp->page )
+    {
+        /*
+         * If a guest frame has already been mapped (which may happen
+         * on demand if hvm_get_ioreq_server_info() is called), then
+         * allocating a page is not permitted.
+         */
+        if ( !gfn_eq(iorp->gfn, INVALID_GFN) )
+            return -EPERM;
+
+        return 0;
+    }
+
+    /*
+     * Allocated IOREQ server pages are assigned to the emulating
+     * domain, not the target domain. This is safe because the emulating
+     * domain cannot be destroyed until the ioreq server is destroyed.
+     * Also we must use MEMF_no_refcount otherwise page allocation
+     * could fail if the emulating domain has already reached its
+     * maximum allocation.
+     */
+    iorp->page = alloc_domheap_page(s->emulator, MEMF_no_refcount);
+
+    if ( !iorp->page )
+        return -ENOMEM;
+
+    if ( !get_page_type(iorp->page, PGT_writable_page) )
+        goto fail1;
+
+    iorp->va = __map_domain_page_global(iorp->page);
+    if ( !iorp->va )
+        goto fail2;
+
+    clear_page(iorp->va);
+    return 0;
+
+ fail2:
+    put_page_type(iorp->page);
+
+ fail1:
+    put_page(iorp->page);
+    iorp->page = NULL;
+
+    return -ENOMEM;
+}
+
+static void hvm_free_ioreq_mfn(struct hvm_ioreq_server *s, bool buf)
+{
+    struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
+
+    if ( !iorp->page )
+        return;
+
+    unmap_domain_page_global(iorp->va);
+    iorp->va = NULL;
+
+    put_page_and_type(iorp->page);
+    iorp->page = NULL;
+}
+
 bool is_ioreq_server_page(struct domain *d, const struct page_info *page)
 {
     const struct hvm_ioreq_server *s;
@@ -496,6 +573,27 @@ static void hvm_ioreq_server_unmap_pages(struct hvm_ioreq_server *s)
     hvm_unmap_ioreq_gfn(s, false);
 }
 
+static int hvm_ioreq_server_alloc_pages(struct hvm_ioreq_server *s)
+{
+    int rc;
+
+    rc = hvm_alloc_ioreq_mfn(s, false);
+
+    if ( !rc && (s->bufioreq_handling != HVM_IOREQSRV_BUFIOREQ_OFF) )
+        rc = hvm_alloc_ioreq_mfn(s, true);
+
+    if ( rc )
+        hvm_free_ioreq_mfn(s, false);
+
+    return rc;
+}
+
+static void hvm_ioreq_server_free_pages(struct hvm_ioreq_server *s)
+{
+    hvm_free_ioreq_mfn(s, true);
+    hvm_free_ioreq_mfn(s, false);
+}
+
 static void hvm_ioreq_server_free_rangesets(struct hvm_ioreq_server *s)
 {
     unsigned int i;
@@ -647,7 +745,19 @@ static void hvm_ioreq_server_deinit(struct hvm_ioreq_server *s)
 {
     ASSERT(!s->enabled);
     hvm_ioreq_server_remove_all_vcpus(s);
+
+    /*
+     * NOTE: It is safe to call both hvm_ioreq_server_unmap_pages() and
+     *       hvm_ioreq_server_free_pages() in that order.
+     *       This is because the former will do nothing if the pages
+     *       are not mapped, leaving the page to be freed by the latter.
+     *       However if the pages are mapped then the former will set
+     *       the page_info pointer to NULL, meaning the latter will do
+     *       nothing.
+     */
     hvm_ioreq_server_unmap_pages(s);
+    hvm_ioreq_server_free_pages(s);
+
     hvm_ioreq_server_free_rangesets(s);
 
     put_domain(s->emulator);
@@ -825,6 +935,63 @@ int hvm_get_ioreq_server_info(struct domain *d, ioservid_t id,
     return rc;
 }
 
+int hvm_get_ioreq_server_frame(struct domain *d, ioservid_t id,
+                               unsigned long idx, mfn_t *mfn)
+{
+    struct hvm_ioreq_server *s;
+    int rc;
+
+    if ( id == DEFAULT_IOSERVID )
+        return -EOPNOTSUPP;
+
+    if ( !is_hvm_domain(d) )
+        return -EINVAL;
+
+    spin_lock_recursive(&d->arch.hvm_domain.ioreq_server.lock);
+
+    s = get_ioreq_server(d, id);
+
+    rc = -ENOENT;
+    if ( !s )
+        goto out;
+
+    ASSERT(!IS_DEFAULT(s));
+
+    rc = -EPERM;
+    if ( s->emulator != current->domain )
+        goto out;
+
+    rc = hvm_ioreq_server_alloc_pages(s);
+    if ( rc )
+        goto out;
+
+    switch ( idx )
+    {
+    case XENMEM_resource_ioreq_server_frame_bufioreq:
+        rc = -ENOENT;
+        if ( !HANDLE_BUFIOREQ(s) )
+            goto out;
+
+        *mfn = _mfn(page_to_mfn(s->bufioreq.page));
+        rc = 0;
+        break;
+
+    case XENMEM_resource_ioreq_server_frame_ioreq(0):
+        *mfn = _mfn(page_to_mfn(s->ioreq.page));
+        rc = 0;
+        break;
+
+    default:
+        rc = -EINVAL;
+        break;
+    }
+
+ out:
+    spin_unlock_recursive(&d->arch.hvm_domain.ioreq_server.lock);
+
+    return rc;
+}
+
 int hvm_map_io_range_to_ioreq_server(struct domain *d, ioservid_t id,
                                      uint32_t type, uint64_t start,
                                      uint64_t end)
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 17558e0c8c..4964910d09 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -123,6 +123,7 @@
 #include <asm/io_apic.h>
 #include <asm/pci.h>
 #include <asm/guest.h>
+#include <asm/hvm/ioreq.h>
 
 #include <asm/hvm/grant_table.h>
 #include <asm/pv/grant_table.h>
@@ -4228,6 +4229,52 @@ int xenmem_add_to_physmap_one(
     return rc;
 }
 
+int arch_acquire_resource(struct domain *d, unsigned int type,
+                          unsigned int id, unsigned long frame,
+                          unsigned int nr_frames, xen_pfn_t mfn_list[],
+                          unsigned int *flags)
+{
+    int rc;
+
+    switch ( type )
+    {
+    case XENMEM_resource_ioreq_server:
+    {
+        ioservid_t ioservid = id;
+        unsigned int i;
+
+        rc = -EINVAL;
+        if ( id != (unsigned int)ioservid )
+            break;
+
+        rc = 0;
+        for ( i = 0; i < nr_frames; i++ )
+        {
+            mfn_t mfn;
+
+            rc = hvm_get_ioreq_server_frame(d, id, frame + i, &mfn);
+            if ( rc )
+                break;
+
+            mfn_list[i] = mfn_x(mfn);
+        }
+
+        /*
+         * The frames will have been assigned to the domain that created
+         * the ioreq server.
+         */
+        *flags |= XENMEM_rsrc_acq_caller_owned;
+        break;
+    }
+
+    default:
+        rc = -EOPNOTSUPP;
+        break;
+    }
+
+    return rc;
+}
+
 long arch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
 {
     int rc;
diff --git a/xen/common/memory.c b/xen/common/memory.c
index 9e9ac445f7..2091bb8c2f 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -1013,7 +1013,8 @@ static int acquire_resource(
     switch ( xmar.type )
     {
     default:
-        rc = -EOPNOTSUPP;
+        rc = arch_acquire_resource(d, xmar.type, xmar.id, xmar.frame,
+                                   xmar.nr_frames, mfn_list, &xmar.flags);
         break;
     }
 
diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h
index a0e922f360..f4a8e7953c 100644
--- a/xen/include/asm-arm/mm.h
+++ b/xen/include/asm-arm/mm.h
@@ -374,6 +374,14 @@ static inline void put_page_and_type(struct page_info *page)
 
 void clear_and_clean_page(struct page_info *page);
 
+static inline
+int arch_acquire_resource(struct domain *d, unsigned int type, unsigned int id,
+                          unsigned long frame, unsigned int nr_frames,
+                          xen_pfn_t mfn_list[], unsigned int *flags)
+{
+    return -EOPNOTSUPP;
+}
+
 #endif /*  __ARCH_ARM_MM__ */
 /*
  * Local variables:
diff --git a/xen/include/asm-x86/hvm/ioreq.h b/xen/include/asm-x86/hvm/ioreq.h
index 1bd1a02f23..bab473cf44 100644
--- a/xen/include/asm-x86/hvm/ioreq.h
+++ b/xen/include/asm-x86/hvm/ioreq.h
@@ -30,6 +30,8 @@ int hvm_get_ioreq_server_info(struct domain *d, ioservid_t id,
                               unsigned long *ioreq_gfn,
                               unsigned long *bufioreq_gfn,
                               evtchn_port_t *bufioreq_port);
+int hvm_get_ioreq_server_frame(struct domain *d, ioservid_t id,
+                               unsigned long idx, mfn_t *mfn);
 int hvm_map_io_range_to_ioreq_server(struct domain *d, ioservid_t id,
                                      uint32_t type, uint64_t start,
                                      uint64_t end);
diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h
index c115661837..d3cd5b9b22 100644
--- a/xen/include/asm-x86/mm.h
+++ b/xen/include/asm-x86/mm.h
@@ -623,4 +623,9 @@ static inline bool arch_mfn_in_directmap(unsigned long mfn)
     return mfn <= (virt_to_mfn(eva - 1) + 1);
 }
 
+int arch_acquire_resource(struct domain *d, unsigned int type,
+                          unsigned int id, unsigned long frame,
+                          unsigned int nr_frames, xen_pfn_t mfn_list[],
+                          unsigned int *flags);
+
 #endif /* __ASM_X86_MM_H__ */
diff --git a/xen/include/public/hvm/dm_op.h b/xen/include/public/hvm/dm_op.h
index 9823726d79..d3b554d019 100644
--- a/xen/include/public/hvm/dm_op.h
+++ b/xen/include/public/hvm/dm_op.h
@@ -90,6 +90,10 @@ struct xen_dm_op_create_ioreq_server {
  * the frame numbers passed back in gfns <ioreq_gfn> and <bufioreq_gfn>
  * respectively. (If the IOREQ Server is not handling buffered emulation
  * only <ioreq_gfn> will be valid).
+ *
+ * NOTE: To access the synchronous ioreq structures and buffered ioreq
+ *       ring, it is preferable to use the XENMEM_acquire_resource memory
+ *       op specifying resource type XENMEM_resource_ioreq_server.
  */
 #define XEN_DMOP_get_ioreq_server_info 2
 
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index fddc973f19..bf2f81faae 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -609,9 +609,14 @@ struct xen_mem_acquire_resource {
     domid_t domid;
     /* IN - the type of resource */
     uint16_t type;
+
+#define XENMEM_resource_ioreq_server 0
+
     /*
      * IN - a type-specific resource identifier, which must be zero
      *      unless stated otherwise.
+     *
+     * type == XENMEM_resource_ioreq_server -> id == ioreq server id
      */
     uint32_t id;
     /*
@@ -636,6 +641,10 @@ struct xen_mem_acquire_resource {
      *      is ignored if nr_frames is 0.
      */
     uint64_aligned_t frame;
+
+#define XENMEM_resource_ioreq_server_frame_bufioreq 0
+#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n))
+
     /*
      * IN/OUT - If the tools domain is PV then, upon return, frame_list
      *          will be populated with the MFNs of the resource.
--
generated by git-patchbot for /home/xen/git/xen.git#master

_______________________________________________
Xen-changelog mailing list
[hidden email]
https://lists.xenproject.org/xen-changelog