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

[Xen-devel] [PATCH v4 39/39] arm/xen-access: Add test of xc_altp2m_change_gfn



This commit extends xen-access by a simple test of the functionality
provided by "xc_altp2m_change_gfn". The idea is to dynamically remap a
trapping gfn to another mfn, which holds the same content as the
original mfn. On success, the guest will continue to run. Subsequent
altp2m access violations will trap into Xen and be forced by xen-access
to switch to the default view (altp2m[0]) as before. The introduced test
can be invoked by providing the argument "altp2m_remap".

Signed-off-by: Sergej Proskurin <proskurin@xxxxxxxxxxxxx>
---
Cc: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
Cc: Tamas K Lengyel <tamas@xxxxxxxxxxxxx>
Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Cc: Wei Liu <wei.liu2@xxxxxxxxxx>
---
v3: Cosmetic fixes in "xenaccess_copy_gfn" and "xenaccess_change_gfn".

    Added munmap in "copy_gfn" in the second error case.

    Added option "altp2m_remap" selecting the altp2m-remap test.

v4: Dropped the COMPAT API for mapping foreign memory. Instead, we use the
    stable library xenforeignmemory.

    Dropped the use of xc_domain_increase_reservation_exact as we do not
    need to increase the domain's physical memory. Otherwise, remapping
    a page via altp2m would become visible to the guest itself. As long
    as we have additional shadow-memory for the guest domain, we do not
    need to reserve any additional memory.
---
 tools/tests/xen-access/Makefile     |   2 +-
 tools/tests/xen-access/xen-access.c | 182 +++++++++++++++++++++++++++++++++++-
 2 files changed, 179 insertions(+), 5 deletions(-)

diff --git a/tools/tests/xen-access/Makefile b/tools/tests/xen-access/Makefile
index e11f639ccf..ab195e233f 100644
--- a/tools/tests/xen-access/Makefile
+++ b/tools/tests/xen-access/Makefile
@@ -26,6 +26,6 @@ clean:
 distclean: clean
 
 xen-access: xen-access.o Makefile
-       $(CC) -o $@ $< $(LDFLAGS) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) 
$(LDLIBS_libxenevtchn)
+       $(CC) -o $@ $< $(LDFLAGS) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) 
$(LDLIBS_libxenevtchn) $(LDLIBS_libxenforeignmemory)
 
 -include $(DEPS)
diff --git a/tools/tests/xen-access/xen-access.c 
b/tools/tests/xen-access/xen-access.c
index 481337cacd..f9b9fb6bbf 100644
--- a/tools/tests/xen-access/xen-access.c
+++ b/tools/tests/xen-access/xen-access.c
@@ -41,6 +41,7 @@
 #include <xenctrl.h>
 #include <xenevtchn.h>
 #include <xen/vm_event.h>
+#include <xenforeignmemory.h>
 
 #if defined(__arm__) || defined(__aarch64__)
 #include <xen/arch-arm.h>
@@ -49,6 +50,8 @@
 #define START_PFN 0ULL
 #endif
 
+#define INVALID_GFN ~(0UL)
+
 #define DPRINTF(a, b...) fprintf(stderr, a, ## b)
 #define ERROR(a, b...) fprintf(stderr, a "\n", ## b)
 #define PERROR(a, b...) fprintf(stderr, a ": %s\n", ## b, strerror(errno))
@@ -76,12 +79,19 @@ typedef struct vm_event {
 typedef struct xenaccess {
     xc_interface *xc_handle;
 
+    xenforeignmemory_handle *fmem;
+
     xen_pfn_t max_gpfn;
 
     vm_event_t vm_event;
+
+    unsigned int ap2m_idx;
+    xen_pfn_t gfn_old;
+    xen_pfn_t gfn_new;
 } xenaccess_t;
 
 static int interrupted;
+static int gfn_changed = 0;
 bool evtchn_bind = 0, evtchn_open = 0, mem_access_enable = 0;
 
 static void close_handler(int sig)
@@ -89,6 +99,104 @@ static void close_handler(int sig)
     interrupted = sig;
 }
 
+static int xenaccess_copy_gfn(xenaccess_t *xenaccess,
+                              domid_t domain_id,
+                              xen_pfn_t dst_gfn,
+                              xen_pfn_t src_gfn)
+{
+    void *src_vaddr = NULL;
+    void *dst_vaddr = NULL;
+
+    src_vaddr = xenforeignmemory_map(xenaccess->fmem, domain_id, PROT_READ,
+                                     1, &src_gfn, NULL);
+    if ( src_vaddr == NULL )
+        return -1;
+
+    dst_vaddr = xenforeignmemory_map(xenaccess->fmem, domain_id, PROT_WRITE,
+                                     1, &dst_gfn, NULL);
+    if ( dst_vaddr == NULL )
+    {
+        munmap(src_vaddr, XC_PAGE_SIZE);
+        return -1;
+    }
+
+    memcpy(dst_vaddr, src_vaddr, XC_PAGE_SIZE);
+
+    xenforeignmemory_unmap(xenaccess->fmem, src_vaddr, 1);
+    xenforeignmemory_unmap(xenaccess->fmem, dst_vaddr, 1);
+
+    return 0;
+}
+
+/*
+ * This function allocates and populates a page in the guest's physmap that is
+ * subsequently filled with contents of the trapping address. Finally, through
+ * the invocation of xc_altp2m_change_gfn, the altp2m subsystem changes the gfn
+ * to mfn mapping of the target altp2m view.
+ */
+static int xenaccess_change_gfn(xenaccess_t *xenaccess,
+                                domid_t domain_id,
+                                unsigned int ap2m_idx,
+                                xen_pfn_t gfn_old,
+                                xen_pfn_t *gfn_new)
+{
+    int rc;
+
+    /*
+     * We perform this function only once as it is intended to be used for
+     * testing and demonstration purposes. Thus, we signalize that further
+     * altp2m-related traps will not change trapping gfn's.
+     */
+    gfn_changed = 1;
+
+    *gfn_new = ++(xenaccess->max_gpfn);
+
+    rc = xc_domain_populate_physmap_exact(xenaccess->xc_handle, domain_id, 1, 
0, 0, gfn_new);
+    if ( rc < 0 )
+        goto err;
+
+    /* Copy content of the old gfn into the newly allocated gfn */
+    rc = xenaccess_copy_gfn(xenaccess, domain_id, *gfn_new, gfn_old);
+    if ( rc < 0 )
+        goto err;
+
+    rc = xc_altp2m_change_gfn(xenaccess->xc_handle, domain_id, ap2m_idx, 
gfn_old, *gfn_new);
+    if ( rc < 0 )
+        goto err;
+
+    return 0;
+
+err:
+    xc_domain_decrease_reservation_exact(xenaccess->xc_handle, domain_id, 1, 
0, gfn_new);
+
+    (xenaccess->max_gpfn)--;
+
+    return -1;
+}
+
+static int xenaccess_reset_gfn(xc_interface *xch,
+                               domid_t domain_id,
+                               unsigned int ap2m_idx,
+                               xen_pfn_t gfn_old,
+                               xen_pfn_t gfn_new)
+{
+    int rc;
+
+    /* Reset previous state */
+    xc_altp2m_change_gfn(xch, domain_id, ap2m_idx, gfn_old, INVALID_GFN);
+
+    /* Invalidate the new gfn */
+    xc_altp2m_change_gfn(xch, domain_id, ap2m_idx, gfn_new, INVALID_GFN);
+
+    rc = xc_domain_decrease_reservation_exact(xch, domain_id, 1, 0, &gfn_new);
+    if ( rc < 0 )
+        return -1;
+
+    (xenaccess->max_gpfn)--;
+
+    return 0;
+}
+
 int xc_wait_for_event_or_timeout(xc_interface *xch, xenevtchn_handle *xce, 
unsigned long ms)
 {
     struct pollfd fd = { .fd = xenevtchn_fd(xce), .events = POLLIN | POLLERR };
@@ -175,6 +283,13 @@ int xenaccess_teardown(xc_interface *xch, xenaccess_t 
*xenaccess)
         }
     }
 
+    /* Close Xen foreign memory interface */
+    rc = xenforeignmemory_close(xenaccess->fmem);
+    if ( rc != 0 )
+    {
+        ERROR("Error closing Xen foreign memory interface");
+    }
+
     /* Close connection to Xen */
     rc = xc_interface_close(xenaccess->xc_handle);
     if ( rc != 0 )
@@ -234,6 +349,10 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t 
domain_id)
     }
     mem_access_enable = 1;
 
+    xenaccess->ap2m_idx = ~(0);
+    xenaccess->gfn_old = INVALID_GFN;
+    xenaccess->gfn_new = INVALID_GFN;
+
     /* Open event channel */
     xenaccess->vm_event.xce_handle = xenevtchn_open(NULL, 0);
     if ( xenaccess->vm_event.xce_handle == NULL )
@@ -274,6 +393,13 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t 
domain_id)
 
     DPRINTF("max_gpfn = %"PRI_xen_pfn"\n", xenaccess->max_gpfn);
 
+    xenaccess->fmem = xenforeignmemory_open(0, 0);
+    if ( xenaccess->fmem == NULL )
+    {
+        ERROR("Failed to open xen foreign memory interface");
+        goto err;
+    }
+
     return xenaccess;
 
  err:
@@ -366,7 +492,7 @@ void usage(char* progname)
 #elif defined(__arm__) || defined(__aarch64__)
             fprintf(stderr, "|privcall");
 #endif
-            fprintf(stderr, "|altp2m_write|altp2m_exec");
+            fprintf(stderr, "|altp2m_write|altp2m_exec|altp2m_remap");
             fprintf(stderr,
             "\n"
             "Logs first page writes, execs, or breakpoint traps that occur on 
the domain.\n"
@@ -392,6 +518,7 @@ int main(int argc, char *argv[])
     int shutting_down = 0;
     int privcall = 0;
     int altp2m = 0;
+    int altp2m_remap = 0;
     int debug = 0;
     int cpuid = 0;
     int desc_access = 0;
@@ -476,6 +603,13 @@ int main(int argc, char *argv[])
         altp2m = 1;
         memaccess = 1;
     }
+    else if ( !strcmp(argv[0], "altp2m_remap") )
+    {
+        default_access = XENMEM_access_rw;
+        altp2m = 1;
+        altp2m_remap = 1;
+        memaccess = 1;
+    }
     else
     {
         usage(argv[0]);
@@ -669,6 +803,14 @@ int main(int argc, char *argv[])
 #if defined(__i386__) || defined(__x86_64__)
                 rc = xc_monitor_singlestep(xch, domain_id, 0);
 #endif
+
+                /* Reset remapped gfn. */
+                if ( altp2m_remap && xenaccess->gfn_new != INVALID_GFN )
+                    rc = xenaccess_reset_gfn(xenaccess->xc_handle,
+                                             xenaccess->vm_event.domain_id,
+                                             xenaccess->ap2m_idx,
+                                             xenaccess->gfn_old,
+                                             xenaccess->gfn_new);
             } else {
                 rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, 
~0ull, 0);
                 rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, 
START_PFN,
@@ -742,10 +884,42 @@ int main(int argc, char *argv[])
 
                 if ( altp2m && req.flags & VM_EVENT_FLAG_ALTERNATE_P2M)
                 {
-                    DPRINTF("\tSwitching back to default view!\n");
-
                     rsp.flags |= (VM_EVENT_FLAG_ALTERNATE_P2M | 
VM_EVENT_FLAG_TOGGLE_SINGLESTEP);
-                    rsp.altp2m_idx = 0;
+
+                    if ( altp2m_remap )
+                    {
+                        if ( !gfn_changed )
+                        {
+                            /* Store trapping gfn and ap2m index for cleanup. 
*/
+                            xenaccess->gfn_old = req.u.mem_access.gfn;
+                            xenaccess->ap2m_idx = req.altp2m_idx;
+
+                            /* Note that this function is called only once. */
+                            rc = xenaccess_change_gfn(xenaccess, domain_id, 
req.altp2m_idx,
+                                                      xenaccess->gfn_old, 
&xenaccess->gfn_new);
+                            if (rc < 0)
+                            {
+                                ERROR("Error remapping gfn=%"PRIx64"\n", 
xenaccess->gfn_old);
+                                interrupted = -1;
+                                continue;
+                            }
+
+                            /* Do not change the currently active altp2m view, 
yet. */
+                            rsp.altp2m_idx = req.altp2m_idx;
+                        }
+                        else
+                        {
+                            DPRINTF("\tSwitching back to default view!\n");
+
+                            rsp.altp2m_idx = 0;
+                        }
+                    }
+                    else
+                    {
+                        DPRINTF("\tSwitching back to default view!\n");
+
+                        rsp.altp2m_idx = 0;
+                    }
                 }
                 else if ( default_access != after_first_access )
                 {
-- 
2.13.3


_______________________________________________
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®.