[v2,10/10] xen-access: Add support for vm_event_ng interface
diff mbox series

Message ID fa78b1f1a9b046f52a017afcb73c6ef57d018d82.1563293545.git.ppircalabu@bitdefender.com
State New
Headers show
Series
  • Per vcpu vm_event channels
Related show

Commit Message

Petre Ovidiu PIRCALABU July 16, 2019, 5:06 p.m. UTC
Split xen-access in order to accommodate both vm_event interfaces
(legacy and NG). By default, the legacy vm_event is selected but
this can be changed by adding the '-n' flag in the command line.

Signed-off-by: Petre Pircalabu <ppircalabu@bitdefender.com>
---
 tools/tests/xen-access/Makefile      |   7 +-
 tools/tests/xen-access/vm-event-ng.c | 183 ++++++++++++++++++
 tools/tests/xen-access/vm-event.c    | 194 +++++++++++++++++++
 tools/tests/xen-access/xen-access.c  | 348 +++++++++++------------------------
 tools/tests/xen-access/xen-access.h  |  91 +++++++++
 5 files changed, 582 insertions(+), 241 deletions(-)
 create mode 100644 tools/tests/xen-access/vm-event-ng.c
 create mode 100644 tools/tests/xen-access/vm-event.c
 create mode 100644 tools/tests/xen-access/xen-access.h

Patch
diff mbox series

diff --git a/tools/tests/xen-access/Makefile b/tools/tests/xen-access/Makefile
index 131c9f3..17760d8 100644
--- a/tools/tests/xen-access/Makefile
+++ b/tools/tests/xen-access/Makefile
@@ -7,6 +7,7 @@  CFLAGS += -DXC_WANT_COMPAT_DEVICEMODEL_API
 CFLAGS += $(CFLAGS_libxenctrl)
 CFLAGS += $(CFLAGS_libxenguest)
 CFLAGS += $(CFLAGS_libxenevtchn)
+CFLAGS += $(CFLAGS_libxenforeignmemory)
 CFLAGS += $(CFLAGS_xeninclude)
 
 TARGETS-y := xen-access
@@ -25,8 +26,10 @@  clean:
 .PHONY: distclean
 distclean: clean
 
-xen-access: xen-access.o Makefile
-	$(CC) -o $@ $< $(LDFLAGS) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenevtchn)
+OBJS = xen-access.o vm-event.o vm-event-ng.o
+
+xen-access: $(OBJS) Makefile
+	$(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenevtchn) $(LDLIBS_libxenforeignmemory)
 
 install uninstall:
 
diff --git a/tools/tests/xen-access/vm-event-ng.c b/tools/tests/xen-access/vm-event-ng.c
new file mode 100644
index 0000000..2c79f61
--- /dev/null
+++ b/tools/tests/xen-access/vm-event-ng.c
@@ -0,0 +1,183 @@ 
+/*
+ * vm-event-ng.c
+ *
+ * Copyright (c) 2019 Bitdefender S.R.L.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <xenforeignmemory.h>
+#include "xen-access.h"
+
+#ifndef PFN_UP
+#define PFN_UP(x)     (((x) + XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT)
+#endif /* PFN_UP */
+
+typedef struct vm_event_channels
+{
+    vm_event_t vme;
+    int num_channels;
+    xenforeignmemory_resource_handle *fres;
+    struct vm_event_slot *slots;
+    int *ports;
+} vm_event_channels_t;
+
+#define to_channels(_vme) container_of((_vme), vm_event_channels_t, vme)
+
+static int vm_event_channels_init(xc_interface *xch, xenevtchn_handle *xce,
+                                  domid_t domain_id, vm_event_ops_t *ops,
+                                  vm_event_t **vm_event)
+{
+    vm_event_channels_t *impl = NULL;
+    int rc, i;
+
+    impl = (vm_event_channels_t *)calloc(1, sizeof(vm_event_channels_t));
+    if ( impl == NULL )
+        return -ENOMEM;
+
+    rc = xc_monitor_ng_enable(xch, domain_id, &impl->fres, &impl->num_channels, (void**)&impl->slots);
+    if ( rc )
+    {
+        ERROR("Failed to enable monitor");
+        return rc;
+    }
+
+    impl->ports = calloc(impl->num_channels, sizeof(int));
+    if ( impl->ports == NULL )
+    {
+        rc = -ENOMEM;
+        goto err;
+    }
+
+    for ( i = 0; i < impl->num_channels; i++)
+    {
+        rc = xenevtchn_bind_interdomain(xce, domain_id, impl->slots[i].port);
+        if (  rc < 0 )
+        {
+            ERROR("Failed to bind vm_event_slot port for vcpu %d", i);
+            rc = -errno;
+            goto err;
+        }
+
+        impl->ports[i] = rc;
+    }
+
+    *vm_event = (vm_event_t*) impl;
+    return 0;
+
+err:
+    while ( --i >= 0 )
+        xenevtchn_unbind(xce, impl->ports[i]);
+    free(impl->ports);
+    xc_monitor_ng_disable(xch, domain_id, &impl->fres);
+    free(impl);
+    return rc;
+}
+
+static int vcpu_id_by_port(vm_event_channels_t *impl, int port, int *vcpu_id)
+{
+    int i;
+
+    for ( i = 0; i < impl->num_channels; i++ )
+    {
+        if ( port == impl->ports[i] )
+        {
+            *vcpu_id = i;
+            return 0;
+        }
+    }
+
+    return -EINVAL;
+}
+
+static int vm_event_channels_teardown(vm_event_t *vm_event)
+{
+    vm_event_channels_t *impl = to_channels(vm_event);
+    int rc, i;
+
+    if ( impl == NULL || impl->ports == NULL )
+        return -EINVAL;
+
+    for ( i = 0; i < impl->num_channels; i++ )
+    {
+        rc = xenevtchn_unbind(vm_event->xce, impl->ports[i]);
+        if ( rc != 0 )
+        {
+            ERROR("Error unbinding event port");
+            return rc;
+        }
+    }
+
+    return xc_monitor_ng_disable(impl->vme.xch, impl->vme.domain_id, &impl->fres);
+}
+
+static bool vm_event_channels_get_request(vm_event_t *vm_event, vm_event_request_t *req, int *port)
+{
+    int vcpu_id;
+    vm_event_channels_t *impl = to_channels(vm_event);
+
+    if ( vcpu_id_by_port(impl, *port, &vcpu_id) != 0 )
+        return false;
+
+    if ( impl->slots[vcpu_id].state != STATE_VM_EVENT_SLOT_SUBMIT )
+        return false;
+
+    memcpy(req, &impl->slots[vcpu_id].u.req, sizeof(*req));
+
+    return true;
+}
+
+static void vm_event_channels_put_response(vm_event_t *vm_event, vm_event_response_t *rsp, int port)
+{
+    int vcpu_id;
+    vm_event_channels_t *impl = to_channels(vm_event);
+
+    if ( vcpu_id_by_port(impl, port, &vcpu_id) != 0 )
+        return;
+
+    memcpy(&impl->slots[vcpu_id].u.rsp, rsp, sizeof(*rsp));
+    impl->slots[vcpu_id].state = STATE_VM_EVENT_SLOT_FINISH;
+}
+
+static int vm_event_channels_notify_port(vm_event_t *vm_event, int port)
+{
+    return xenevtchn_notify(vm_event->xce, port);
+}
+
+vm_event_ops_t channel_ops = {
+    .get_request = vm_event_channels_get_request,
+    .put_response = vm_event_channels_put_response,
+    .notify_port = vm_event_channels_notify_port,
+    .init = vm_event_channels_init,
+    .teardown = vm_event_channels_teardown,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/tests/xen-access/vm-event.c b/tools/tests/xen-access/vm-event.c
new file mode 100644
index 0000000..e6b20ce
--- /dev/null
+++ b/tools/tests/xen-access/vm-event.c
@@ -0,0 +1,194 @@ 
+/*
+ * vm-event.c
+ *
+ * Copyright (c) 2019 Bitdefender S.R.L.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include "xen-access.h"
+
+typedef struct vm_event_ring {
+    vm_event_t vme;
+    int port;
+    vm_event_back_ring_t back_ring;
+    uint32_t evtchn_port;
+    void *ring_page;
+} vm_event_ring_t;
+
+#define to_ring(_vme) container_of((_vme), vm_event_ring_t, vme)
+
+static int vm_event_ring_init(xc_interface *xch, xenevtchn_handle *xce,
+                              domid_t domain_id, vm_event_ops_t *ops,
+                              vm_event_t **vm_event)
+{
+    vm_event_ring_t *impl;
+    int rc;
+
+    impl = (vm_event_ring_t*) calloc (1, sizeof(vm_event_ring_t));
+    if ( impl == NULL )
+        return -ENOMEM;
+
+    /* Enable mem_access */
+    impl->ring_page = xc_monitor_enable(xch, domain_id, &impl->evtchn_port);
+    if ( impl->ring_page == NULL )
+    {
+        switch ( errno )
+        {
+        case EBUSY:
+            ERROR("xenaccess is (or was) active on this domain");
+            break;
+        case ENODEV:
+            ERROR("EPT not supported for this guest");
+            break;
+        default:
+            perror("Error enabling mem_access");
+            break;
+        }
+        rc = -errno;
+        goto err;
+    }
+
+    /* Bind event notification */
+    rc = xenevtchn_bind_interdomain(xce, domain_id, impl->evtchn_port);
+    if ( rc < 0 )
+    {
+        ERROR("Failed to bind event channel");
+        munmap(impl->ring_page, XC_PAGE_SIZE);
+        xc_monitor_disable(xch, domain_id);
+        goto err;
+    }
+
+    impl->port = rc;
+
+    /* Initialise ring */
+    SHARED_RING_INIT((vm_event_sring_t *)impl->ring_page);
+    BACK_RING_INIT(&impl->back_ring, (vm_event_sring_t *)impl->ring_page,
+                   XC_PAGE_SIZE);
+
+    *vm_event = (vm_event_t*) impl;
+    return 0;
+
+err:
+    free(impl);
+    return rc;
+}
+
+static int vm_event_ring_teardown(vm_event_t *vm_event)
+{
+    vm_event_ring_t *impl = to_ring(vm_event);
+    int rc;
+
+    if ( impl->ring_page != NULL )
+        munmap(impl->ring_page, XC_PAGE_SIZE);
+
+    /* Tear down domain xenaccess in Xen */
+    rc = xc_monitor_disable(vm_event->xch, vm_event->domain_id);
+    if ( rc != 0 )
+    {
+        ERROR("Error tearing down domain xenaccess in xen");
+        return rc;
+    }
+
+    /* Unbind VIRQ */
+    rc = xenevtchn_unbind(vm_event->xce, impl->port);
+    if ( rc != 0 )
+    {
+        ERROR("Error unbinding event port");
+        return rc;
+    }
+
+    return 0;
+}
+
+/*
+ * Note that this function is not thread safe.
+ */
+static bool vm_event_ring_get_request(vm_event_t *vm_event, vm_event_request_t *req, int *port)
+{
+    vm_event_back_ring_t *back_ring;
+    RING_IDX req_cons;
+    vm_event_ring_t *impl = to_ring(vm_event);
+
+    if ( !RING_HAS_UNCONSUMED_REQUESTS(&impl->back_ring) )
+        return false;
+
+    back_ring = &impl->back_ring;
+    req_cons = back_ring->req_cons;
+
+    /* Copy request */
+    memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
+    req_cons++;
+
+    /* Update ring */
+    back_ring->req_cons = req_cons;
+    back_ring->sring->req_event = req_cons + 1;
+
+    *port = impl->port;
+
+    return true;
+}
+
+/*
+ * Note that this function is not thread safe.
+ */
+static void vm_event_ring_put_response(vm_event_t *vm_event, vm_event_response_t *rsp, int port)
+{
+    vm_event_back_ring_t *back_ring;
+    RING_IDX rsp_prod;
+    vm_event_ring_t *impl = to_ring(vm_event);
+
+    back_ring = &impl->back_ring;
+    rsp_prod = back_ring->rsp_prod_pvt;
+
+    /* Copy response */
+    memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
+    rsp_prod++;
+
+    /* Update ring */
+    back_ring->rsp_prod_pvt = rsp_prod;
+    RING_PUSH_RESPONSES(back_ring);
+}
+
+static int vm_event_ring_notify_port(vm_event_t *vm_event, int port)
+{
+    return xenevtchn_notify(vm_event->xce, port);
+}
+
+vm_event_ops_t ring_ops = {
+    .get_request = vm_event_ring_get_request,
+    .put_response = vm_event_ring_put_response,
+    .notify_port = vm_event_ring_notify_port,
+    .init = vm_event_ring_init,
+    .teardown = vm_event_ring_teardown,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/tests/xen-access/xen-access.c b/tools/tests/xen-access/xen-access.c
index abf17a2..9157f23 100644
--- a/tools/tests/xen-access/xen-access.c
+++ b/tools/tests/xen-access/xen-access.c
@@ -35,14 +35,9 @@ 
 #include <time.h>
 #include <signal.h>
 #include <unistd.h>
-#include <sys/mman.h>
 #include <poll.h>
 #include <getopt.h>
 
-#include <xenctrl.h>
-#include <xenevtchn.h>
-#include <xen/vm_event.h>
-
 #include <xen-tools/libs.h>
 
 #if defined(__arm__) || defined(__aarch64__)
@@ -52,9 +47,7 @@ 
 #define START_PFN 0ULL
 #endif
 
-#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))
+#include "xen-access.h"
 
 /* From xen/include/asm-x86/processor.h */
 #define X86_TRAP_DEBUG  1
@@ -63,32 +56,14 @@ 
 /* From xen/include/asm-x86/x86-defns.h */
 #define X86_CR4_PGE        0x00000080 /* enable global pages */
 
-typedef struct vm_event {
-    domid_t domain_id;
-    xenevtchn_handle *xce_handle;
-    int port;
-    vm_event_back_ring_t back_ring;
-    uint32_t evtchn_port;
-    void *ring_page;
-} vm_event_t;
-
-typedef struct xenaccess {
-    xc_interface *xc_handle;
-
-    xen_pfn_t max_gpfn;
-
-    vm_event_t vm_event;
-} xenaccess_t;
-
 static int interrupted;
-bool evtchn_bind = 0, evtchn_open = 0, mem_access_enable = 0;
 
 static void close_handler(int sig)
 {
     interrupted = sig;
 }
 
-int xc_wait_for_event_or_timeout(xc_interface *xch, xenevtchn_handle *xce, unsigned long ms)
+static 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 };
     int port;
@@ -129,161 +104,85 @@  int xc_wait_for_event_or_timeout(xc_interface *xch, xenevtchn_handle *xce, unsig
     return -errno;
 }
 
-int xenaccess_teardown(xc_interface *xch, xenaccess_t *xenaccess)
+static int vm_event_teardown(vm_event_t *vm_event)
 {
     int rc;
 
-    if ( xenaccess == NULL )
+    if ( vm_event == NULL )
         return 0;
 
-    /* Tear down domain xenaccess in Xen */
-    if ( xenaccess->vm_event.ring_page != NULL )
-        munmap(xenaccess->vm_event.ring_page, XC_PAGE_SIZE);
-
-    if ( mem_access_enable )
-    {
-        rc = xc_monitor_disable(xenaccess->xc_handle,
-                                xenaccess->vm_event.domain_id);
-        if ( rc != 0 )
-        {
-            ERROR("Error tearing down domain xenaccess in xen");
-            return rc;
-        }
-    }
-
-    /* Unbind VIRQ */
-    if ( evtchn_bind )
-    {
-        rc = xenevtchn_unbind(xenaccess->vm_event.xce_handle,
-                              xenaccess->vm_event.port);
-        if ( rc != 0 )
-        {
-            ERROR("Error unbinding event port");
-            return rc;
-        }
-    }
+    rc = vm_event->ops->teardown(vm_event);
+    if ( rc != 0 )
+        return rc;
 
     /* Close event channel */
-    if ( evtchn_open )
+    rc = xenevtchn_close(vm_event->xce);
+    if ( rc != 0 )
     {
-        rc = xenevtchn_close(xenaccess->vm_event.xce_handle);
-        if ( rc != 0 )
-        {
-            ERROR("Error closing event channel");
-            return rc;
-        }
+        ERROR("Error closing event channel");
+        return rc;
     }
 
     /* Close connection to Xen */
-    rc = xc_interface_close(xenaccess->xc_handle);
+    rc = xc_interface_close(vm_event->xch);
     if ( rc != 0 )
     {
         ERROR("Error closing connection to xen");
         return rc;
     }
-    xenaccess->xc_handle = NULL;
-
-    free(xenaccess);
 
     return 0;
 }
 
-xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id)
+static vm_event_t *vm_event_init(domid_t domain_id, vm_event_ops_t *ops)
 {
-    xenaccess_t *xenaccess = 0;
+    vm_event_t *vm_event;
     xc_interface *xch;
+    xenevtchn_handle *xce;
+    xen_pfn_t max_gpfn;
     int rc;
 
+    if ( ops == NULL )
+        return NULL;
+
     xch = xc_interface_open(NULL, NULL, 0);
     if ( xch == NULL )
-        goto err_iface;
+        goto err;
 
     DPRINTF("xenaccess init\n");
-    *xch_r = xch;
-
-    /* Allocate memory */
-    xenaccess = malloc(sizeof(xenaccess_t));
-    memset(xenaccess, 0, sizeof(xenaccess_t));
-
-    /* Open connection to xen */
-    xenaccess->xc_handle = xch;
-
-    /* Set domain id */
-    xenaccess->vm_event.domain_id = domain_id;
-
-    /* Enable mem_access */
-    xenaccess->vm_event.ring_page =
-            xc_monitor_enable(xenaccess->xc_handle,
-                              xenaccess->vm_event.domain_id,
-                              &xenaccess->vm_event.evtchn_port);
-    if ( xenaccess->vm_event.ring_page == NULL )
-    {
-        switch ( errno )
-        {
-        case EBUSY:
-            ERROR("xenaccess is (or was) active on this domain");
-            break;
-        case ENODEV:
-            ERROR("EPT not supported for this guest");
-            break;
-        default:
-            perror("Error enabling mem_access");
-            break;
-        }
-        goto err;
-    }
-    mem_access_enable = 1;
 
     /* Open event channel */
-    xenaccess->vm_event.xce_handle = xenevtchn_open(NULL, 0);
-    if ( xenaccess->vm_event.xce_handle == NULL )
+    xce = xenevtchn_open(NULL, 0);
+    if ( xce == NULL )
     {
         ERROR("Failed to open event channel");
         goto err;
     }
-    evtchn_open = 1;
-
-    /* Bind event notification */
-    rc = xenevtchn_bind_interdomain(xenaccess->vm_event.xce_handle,
-                                    xenaccess->vm_event.domain_id,
-                                    xenaccess->vm_event.evtchn_port);
-    if ( rc < 0 )
-    {
-        ERROR("Failed to bind event channel");
-        goto err;
-    }
-    evtchn_bind = 1;
-    xenaccess->vm_event.port = rc;
-
-    /* Initialise ring */
-    SHARED_RING_INIT((vm_event_sring_t *)xenaccess->vm_event.ring_page);
-    BACK_RING_INIT(&xenaccess->vm_event.back_ring,
-                   (vm_event_sring_t *)xenaccess->vm_event.ring_page,
-                   XC_PAGE_SIZE);
 
     /* Get max_gpfn */
-    rc = xc_domain_maximum_gpfn(xenaccess->xc_handle,
-                                xenaccess->vm_event.domain_id,
-                                &xenaccess->max_gpfn);
-
+    rc = xc_domain_maximum_gpfn(xch, domain_id, &max_gpfn);
     if ( rc )
     {
         ERROR("Failed to get max gpfn");
         goto err;
     }
+    DPRINTF("max_gpfn = %"PRI_xen_pfn"\n", max_gpfn);
 
-    DPRINTF("max_gpfn = %"PRI_xen_pfn"\n", xenaccess->max_gpfn);
+    rc = ops->init(xch, xce, domain_id, ops, &vm_event);
+    if ( rc < 0 )
+        goto err;
 
-    return xenaccess;
+    vm_event->xch = xch;
+    vm_event->xce = xce;
+    vm_event->domain_id = domain_id;
+    vm_event->ops = ops;
+    vm_event->max_gpfn = max_gpfn;
 
- err:
-    rc = xenaccess_teardown(xch, xenaccess);
-    if ( rc )
-    {
-        ERROR("Failed to teardown xenaccess structure!\n");
-    }
+    return vm_event;
 
- err_iface:
+ err:
+    xenevtchn_close(xce);
+    xc_interface_close(xch);
 
     return NULL;
 }
@@ -298,26 +197,6 @@  static inline int control_singlestep(xc_interface *xch, domid_t domain_id,
 }
 
 /*
- * Note that this function is not thread safe.
- */
-static void get_request(vm_event_t *vm_event, vm_event_request_t *req)
-{
-    vm_event_back_ring_t *back_ring;
-    RING_IDX req_cons;
-
-    back_ring = &vm_event->back_ring;
-    req_cons = back_ring->req_cons;
-
-    /* Copy request */
-    memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
-    req_cons++;
-
-    /* Update ring */
-    back_ring->req_cons = req_cons;
-    back_ring->sring->req_event = req_cons + 1;
-}
-
-/*
  * X86 control register names
  */
 static const char* get_x86_ctrl_reg_name(uint32_t index)
@@ -335,29 +214,9 @@  static const char* get_x86_ctrl_reg_name(uint32_t index)
     return names[index];
 }
 
-/*
- * Note that this function is not thread safe.
- */
-static void put_response(vm_event_t *vm_event, vm_event_response_t *rsp)
-{
-    vm_event_back_ring_t *back_ring;
-    RING_IDX rsp_prod;
-
-    back_ring = &vm_event->back_ring;
-    rsp_prod = back_ring->rsp_prod_pvt;
-
-    /* Copy response */
-    memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
-    rsp_prod++;
-
-    /* Update ring */
-    back_ring->rsp_prod_pvt = rsp_prod;
-    RING_PUSH_RESPONSES(back_ring);
-}
-
 void usage(char* progname)
 {
-    fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
+    fprintf(stderr, "Usage: %s [-m] [-n] <domain_id> write|exec", progname);
 #if defined(__i386__) || defined(__x86_64__)
     fprintf(stderr, "|breakpoint|altp2m_write|altp2m_exec|debug|cpuid|desc_access|write_ctrlreg_cr4|altp2m_write_no_gpt");
 #elif defined(__arm__) || defined(__aarch64__)
@@ -367,19 +226,22 @@  void usage(char* progname)
             "\n"
             "Logs first page writes, execs, or breakpoint traps that occur on the domain.\n"
             "\n"
-            "-m requires this program to run, or else the domain may pause\n");
+            "-m requires this program to run, or else the domain may pause\n"
+            "-n uses the per-vcpu channels vm_event interface\n");
 }
 
+extern vm_event_ops_t ring_ops;
+extern vm_event_ops_t channel_ops;
+
 int main(int argc, char *argv[])
 {
     struct sigaction act;
     domid_t domain_id;
-    xenaccess_t *xenaccess;
+    vm_event_t *vm_event;
     vm_event_request_t req;
     vm_event_response_t rsp;
     int rc = -1;
     int rc1;
-    xc_interface *xch;
     xenmem_access_t default_access = XENMEM_access_rwx;
     xenmem_access_t after_first_access = XENMEM_access_rwx;
     int memaccess = 0;
@@ -394,6 +256,7 @@  int main(int argc, char *argv[])
     int write_ctrlreg_cr4 = 0;
     int altp2m_write_no_gpt = 0;
     uint16_t altp2m_view_id = 0;
+    int new_interface = 0;
 
     char* progname = argv[0];
     char* command;
@@ -402,11 +265,12 @@  int main(int argc, char *argv[])
     struct option long_options[] =
     {
         { "mem-access-listener", no_argument, 0, 'm' },
+        { "new-interface", no_argument, 0, 'n' },
     };
 
     while ( 1 )
     {
-        c = getopt_long(argc, argv, "m", long_options, &option_index);
+        c = getopt_long(argc, argv, "mn", long_options, &option_index);
         if ( c == -1 )
             break;
 
@@ -416,6 +280,10 @@  int main(int argc, char *argv[])
             required = 1;
             break;
 
+        case 'n':
+            new_interface = 1;
+            break;
+
         default:
             usage(progname);
             return -1;
@@ -495,10 +363,11 @@  int main(int argc, char *argv[])
         return -1;
     }
 
-    xenaccess = xenaccess_init(&xch, domain_id);
-    if ( xenaccess == NULL )
+    vm_event = vm_event_init(domain_id,
+                             (new_interface) ? &channel_ops : &ring_ops);
+    if ( vm_event == NULL )
     {
-        ERROR("Error initialising xenaccess");
+        ERROR("Error initialising vm_event");
         return 1;
     }
 
@@ -514,7 +383,7 @@  int main(int argc, char *argv[])
     sigaction(SIGALRM, &act, NULL);
 
     /* Set whether the access listener is required */
-    rc = xc_domain_set_access_required(xch, domain_id, required);
+    rc = xc_domain_set_access_required(vm_event->xch, domain_id, required);
     if ( rc < 0 )
     {
         ERROR("Error %d setting mem_access listener required\n", rc);
@@ -529,13 +398,13 @@  int main(int argc, char *argv[])
 
         if( altp2m_write_no_gpt )
         {
-            rc = xc_monitor_inguest_pagefault(xch, domain_id, 1);
+            rc = xc_monitor_inguest_pagefault(vm_event->xch, domain_id, 1);
             if ( rc < 0 )
             {
                 ERROR("Error %d setting inguest pagefault\n", rc);
                 goto exit;
             }
-            rc = xc_monitor_emul_unimplemented(xch, domain_id, 1);
+            rc = xc_monitor_emul_unimplemented(vm_event->xch, domain_id, 1);
             if ( rc < 0 )
             {
                 ERROR("Error %d failed to enable emul unimplemented\n", rc);
@@ -543,14 +412,15 @@  int main(int argc, char *argv[])
             }
         }
 
-        rc = xc_altp2m_set_domain_state( xch, domain_id, 1 );
+        rc = xc_altp2m_set_domain_state( vm_event->xch, domain_id, 1 );
         if ( rc < 0 )
         {
             ERROR("Error %d enabling altp2m on domain!\n", rc);
             goto exit;
         }
 
-        rc = xc_altp2m_create_view( xch, domain_id, default_access, &altp2m_view_id );
+        rc = xc_altp2m_create_view( vm_event->xch, domain_id, default_access,
+                                    &altp2m_view_id );
         if ( rc < 0 )
         {
             ERROR("Error %d creating altp2m view!\n", rc);
@@ -560,24 +430,24 @@  int main(int argc, char *argv[])
         DPRINTF("altp2m view created with id %u\n", altp2m_view_id);
         DPRINTF("Setting altp2m mem_access permissions.. ");
 
-        for( ; gfn < xenaccess->max_gpfn; ++gfn )
+        for( ; gfn < vm_event->max_gpfn; ++gfn )
         {
-            rc = xc_altp2m_set_mem_access( xch, domain_id, altp2m_view_id, gfn,
-                                           default_access);
+            rc = xc_altp2m_set_mem_access( vm_event->xch, domain_id,
+                                           altp2m_view_id, gfn, default_access);
             if ( !rc )
                 perm_set++;
         }
 
         DPRINTF("done! Permissions set on %lu pages.\n", perm_set);
 
-        rc = xc_altp2m_switch_to_view( xch, domain_id, altp2m_view_id );
+        rc = xc_altp2m_switch_to_view( vm_event->xch, domain_id, altp2m_view_id );
         if ( rc < 0 )
         {
             ERROR("Error %d switching to altp2m view!\n", rc);
             goto exit;
         }
 
-        rc = xc_monitor_singlestep( xch, domain_id, 1 );
+        rc = xc_monitor_singlestep( vm_event->xch, domain_id, 1 );
         if ( rc < 0 )
         {
             ERROR("Error %d failed to enable singlestep monitoring!\n", rc);
@@ -588,15 +458,15 @@  int main(int argc, char *argv[])
     if ( memaccess && !altp2m )
     {
         /* Set the default access type and convert all pages to it */
-        rc = xc_set_mem_access(xch, domain_id, default_access, ~0ull, 0);
+        rc = xc_set_mem_access(vm_event->xch, domain_id, default_access, ~0ull, 0);
         if ( rc < 0 )
         {
             ERROR("Error %d setting default mem access type\n", rc);
             goto exit;
         }
 
-        rc = xc_set_mem_access(xch, domain_id, default_access, START_PFN,
-                               (xenaccess->max_gpfn - START_PFN) );
+        rc = xc_set_mem_access(vm_event->xch, domain_id, default_access, START_PFN,
+                               (vm_event->max_gpfn - START_PFN) );
 
         if ( rc < 0 )
         {
@@ -608,7 +478,7 @@  int main(int argc, char *argv[])
 
     if ( breakpoint )
     {
-        rc = xc_monitor_software_breakpoint(xch, domain_id, 1);
+        rc = xc_monitor_software_breakpoint(vm_event->xch, domain_id, 1);
         if ( rc < 0 )
         {
             ERROR("Error %d setting breakpoint trapping with vm_event\n", rc);
@@ -618,7 +488,7 @@  int main(int argc, char *argv[])
 
     if ( debug )
     {
-        rc = xc_monitor_debug_exceptions(xch, domain_id, 1, 1);
+        rc = xc_monitor_debug_exceptions(vm_event->xch, domain_id, 1, 1);
         if ( rc < 0 )
         {
             ERROR("Error %d setting debug exception listener with vm_event\n", rc);
@@ -628,7 +498,7 @@  int main(int argc, char *argv[])
 
     if ( cpuid )
     {
-        rc = xc_monitor_cpuid(xch, domain_id, 1);
+        rc = xc_monitor_cpuid(vm_event->xch, domain_id, 1);
         if ( rc < 0 )
         {
             ERROR("Error %d setting cpuid listener with vm_event\n", rc);
@@ -638,7 +508,7 @@  int main(int argc, char *argv[])
 
     if ( desc_access )
     {
-        rc = xc_monitor_descriptor_access(xch, domain_id, 1);
+        rc = xc_monitor_descriptor_access(vm_event->xch, domain_id, 1);
         if ( rc < 0 )
         {
             ERROR("Error %d setting descriptor access listener with vm_event\n", rc);
@@ -648,7 +518,7 @@  int main(int argc, char *argv[])
 
     if ( privcall )
     {
-        rc = xc_monitor_privileged_call(xch, domain_id, 1);
+        rc = xc_monitor_privileged_call(vm_event->xch, domain_id, 1);
         if ( rc < 0 )
         {
             ERROR("Error %d setting privileged call trapping with vm_event\n", rc);
@@ -659,7 +529,7 @@  int main(int argc, char *argv[])
     if ( write_ctrlreg_cr4 )
     {
         /* Mask the CR4.PGE bit so no events will be generated for global TLB flushes. */
-        rc = xc_monitor_write_ctrlreg(xch, domain_id, VM_EVENT_X86_CR4, 1, 1,
+        rc = xc_monitor_write_ctrlreg(vm_event->xch, domain_id, VM_EVENT_X86_CR4, 1, 1,
                                       X86_CR4_PGE, 1);
         if ( rc < 0 )
         {
@@ -671,41 +541,43 @@  int main(int argc, char *argv[])
     /* Wait for access */
     for ( ; ; )
     {
+        int port = 0;
+
         if ( interrupted )
         {
             /* Unregister for every event */
             DPRINTF("xenaccess shutting down on signal %d\n", interrupted);
 
             if ( breakpoint )
-                rc = xc_monitor_software_breakpoint(xch, domain_id, 0);
+                rc = xc_monitor_software_breakpoint(vm_event->xch, domain_id, 0);
             if ( debug )
-                rc = xc_monitor_debug_exceptions(xch, domain_id, 0, 0);
+                rc = xc_monitor_debug_exceptions(vm_event->xch, domain_id, 0, 0);
             if ( cpuid )
-                rc = xc_monitor_cpuid(xch, domain_id, 0);
+                rc = xc_monitor_cpuid(vm_event->xch, domain_id, 0);
             if ( desc_access )
-                rc = xc_monitor_descriptor_access(xch, domain_id, 0);
+                rc = xc_monitor_descriptor_access(vm_event->xch, domain_id, 0);
             if ( write_ctrlreg_cr4 )
-                rc = xc_monitor_write_ctrlreg(xch, domain_id, VM_EVENT_X86_CR4, 0, 0, 0, 0);
+                rc = xc_monitor_write_ctrlreg(vm_event->xch, domain_id, VM_EVENT_X86_CR4, 0, 0, 0, 0);
 
             if ( privcall )
-                rc = xc_monitor_privileged_call(xch, domain_id, 0);
+                rc = xc_monitor_privileged_call(vm_event->xch, domain_id, 0);
 
             if ( altp2m )
             {
-                rc = xc_altp2m_switch_to_view( xch, domain_id, 0 );
-                rc = xc_altp2m_destroy_view(xch, domain_id, altp2m_view_id);
-                rc = xc_altp2m_set_domain_state(xch, domain_id, 0);
-                rc = xc_monitor_singlestep(xch, domain_id, 0);
+                rc = xc_altp2m_switch_to_view( vm_event->xch, domain_id, 0 );
+                rc = xc_altp2m_destroy_view(vm_event->xch, domain_id, altp2m_view_id);
+                rc = xc_altp2m_set_domain_state(vm_event->xch, domain_id, 0);
+                rc = xc_monitor_singlestep(vm_event->xch, domain_id, 0);
             } 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,
-                                       (xenaccess->max_gpfn - START_PFN) );
+                rc = xc_set_mem_access(vm_event->xch, domain_id, XENMEM_access_rwx, ~0ull, 0);
+                rc = xc_set_mem_access(vm_event->xch, domain_id, XENMEM_access_rwx, START_PFN,
+                                       (vm_event->max_gpfn - START_PFN) );
             }
 
             shutting_down = 1;
         }
 
-        rc = xc_wait_for_event_or_timeout(xch, xenaccess->vm_event.xce_handle, 100);
+        rc = xc_wait_for_event_or_timeout(vm_event->xch, vm_event->xce, 100);
         if ( rc < -1 )
         {
             ERROR("Error getting event");
@@ -717,10 +589,10 @@  int main(int argc, char *argv[])
             DPRINTF("Got event from Xen\n");
         }
 
-        while ( RING_HAS_UNCONSUMED_REQUESTS(&xenaccess->vm_event.back_ring) )
-        {
-            get_request(&xenaccess->vm_event, &req);
+        port = rc;
 
+        while ( vm_event->ops->get_request(vm_event, &req, &port) )
+        {
             if ( req.version != VM_EVENT_INTERFACE_VERSION )
             {
                 ERROR("Error: vm_event interface version mismatch!\n");
@@ -744,7 +616,7 @@  int main(int argc, char *argv[])
                      * At shutdown we have already reset all the permissions so really no use getting it again.
                      */
                     xenmem_access_t access;
-                    rc = xc_get_mem_access(xch, domain_id, req.u.mem_access.gfn, &access);
+                    rc = xc_get_mem_access(vm_event->xch, domain_id, req.u.mem_access.gfn, &access);
                     if (rc < 0)
                     {
                         ERROR("Error %d getting mem_access event\n", rc);
@@ -777,7 +649,7 @@  int main(int argc, char *argv[])
                 }
                 else if ( default_access != after_first_access )
                 {
-                    rc = xc_set_mem_access(xch, domain_id, after_first_access,
+                    rc = xc_set_mem_access(vm_event->xch, domain_id, after_first_access,
                                            req.u.mem_access.gfn, 1);
                     if (rc < 0)
                     {
@@ -798,7 +670,7 @@  int main(int argc, char *argv[])
                        req.vcpu_id);
 
                 /* Reinject */
-                rc = xc_hvm_inject_trap(xch, domain_id, req.vcpu_id,
+                rc = xc_hvm_inject_trap(vm_event->xch, domain_id, req.vcpu_id,
                                         X86_TRAP_INT3,
                                         req.u.software_breakpoint.type, -1,
                                         req.u.software_breakpoint.insn_length, 0);
@@ -846,7 +718,7 @@  int main(int argc, char *argv[])
                        req.u.debug_exception.insn_length);
 
                 /* Reinject */
-                rc = xc_hvm_inject_trap(xch, domain_id, req.vcpu_id,
+                rc = xc_hvm_inject_trap(vm_event->xch, domain_id, req.vcpu_id,
                                         X86_TRAP_DEBUG,
                                         req.u.debug_exception.type, -1,
                                         req.u.debug_exception.insn_length,
@@ -914,17 +786,15 @@  int main(int argc, char *argv[])
             }
 
             /* Put the response on the ring */
-            put_response(&xenaccess->vm_event, &rsp);
-        }
-
-        /* Tell Xen page is ready */
-        rc = xenevtchn_notify(xenaccess->vm_event.xce_handle,
-                              xenaccess->vm_event.port);
+            put_response(vm_event, &rsp, port);
 
-        if ( rc != 0 )
-        {
-            ERROR("Error resuming page");
-            interrupted = -1;
+            /* Tell Xen page is ready */
+            rc = notify_port(vm_event, port);
+            if ( rc != 0 )
+            {
+                ERROR("Error resuming page");
+                interrupted = -1;
+            }
         }
 
         if ( shutting_down )
@@ -937,13 +807,13 @@  exit:
     {
         uint32_t vcpu_id;
         for ( vcpu_id = 0; vcpu_id<XEN_LEGACY_MAX_VCPUS; vcpu_id++)
-            rc = control_singlestep(xch, domain_id, vcpu_id, 0);
+            rc = control_singlestep(vm_event->xch, domain_id, vcpu_id, 0);
     }
 
-    /* Tear down domain xenaccess */
-    rc1 = xenaccess_teardown(xch, xenaccess);
+    /* Tear down domain */
+    rc1 = vm_event_teardown(vm_event);
     if ( rc1 != 0 )
-        ERROR("Error tearing down xenaccess");
+        ERROR("Error tearing down vm_event");
 
     if ( rc == 0 )
         rc = rc1;
diff --git a/tools/tests/xen-access/xen-access.h b/tools/tests/xen-access/xen-access.h
new file mode 100644
index 0000000..9fc640c
--- /dev/null
+++ b/tools/tests/xen-access/xen-access.h
@@ -0,0 +1,91 @@ 
+/*
+ * xen-access.h
+ *
+ * Copyright (c) 2019 Bitdefender S.R.L.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef XEN_ACCESS_H
+#define XEN_ACCESS_H
+
+#include <xenctrl.h>
+#include <xenevtchn.h>
+#include <xen/vm_event.h>
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+                const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+                (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif /* container_of */
+
+#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))
+
+struct vm_event_ops;
+
+typedef struct vm_event {
+    xc_interface *xch;
+    domid_t domain_id;
+    xenevtchn_handle *xce;
+    xen_pfn_t max_gpfn;
+    struct vm_event_ops *ops;
+} vm_event_t;
+
+typedef struct vm_event_ops {
+    int (*init)(xc_interface *, xenevtchn_handle *, domid_t,
+                struct vm_event_ops *, vm_event_t **);
+    int (*teardown)(vm_event_t *);
+    bool (*get_request)(vm_event_t *, vm_event_request_t *, int *);
+    void (*put_response)(vm_event_t *, vm_event_response_t *, int);
+    int (*notify_port)(vm_event_t *, int port);
+} vm_event_ops_t;
+
+static inline bool get_request(vm_event_t *vm_event, vm_event_request_t *req,
+                               int *port)
+{
+    return ( vm_event ) ? vm_event->ops->get_request(vm_event, req, port) :
+                          false;
+}
+
+static inline void put_response(vm_event_t *vm_event, vm_event_response_t *rsp, int port)
+{
+    if (  vm_event )
+        vm_event->ops->put_response(vm_event, rsp, port);
+}
+
+static inline int notify_port(vm_event_t *vm_event, int port)
+{
+    if ( !vm_event )
+        return -EINVAL;
+
+    return vm_event->ops->notify_port(vm_event, port);
+}
+
+#endif /* XEN_ACCESS_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */