@@ -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:
new file mode 100644
@@ -0,0 +1,210 @@
+/*
+ * 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_vcpus;
+ xenforeignmemory_handle *fmem;
+ xenforeignmemory_resource_handle *fres;
+ struct vm_event_slot *slots;
+ int ports[0];
+} 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, num_vcpus;
+ xc_dominfo_t info;
+ unsigned long nr_frames;
+
+ /* Get the numbers of vcpus */
+ rc = xc_domain_getinfo(xch, domain_id, 1, &info);
+ if ( rc != 1 )
+ {
+ ERROR("xc_domain_getinfo failed. rc = %d\n", rc);
+ return rc;
+ }
+
+ num_vcpus = info.max_vcpu_id + 1;
+
+ impl = (vm_event_channels_t *)calloc(1, sizeof(vm_event_channels_t) +
+ num_vcpus * sizeof(int));
+ if ( !impl )
+ return -ENOMEM;
+
+ impl->num_vcpus = num_vcpus;
+
+ impl->fmem = xenforeignmemory_open(0,0);
+ if ( !impl->fmem )
+ {
+ rc = -errno;
+ goto err;
+ }
+
+ rc = xc_monitor_ng_create(xch, domain_id);
+ if ( rc )
+ {
+ ERROR("Failed to enable monitor");
+ goto err;
+ }
+
+ nr_frames = PFN_UP(num_vcpus * sizeof(struct vm_event_slot));
+
+ impl->fres = xenforeignmemory_map_resource(impl->fmem, domain_id,
+ XENMEM_resource_vm_event,
+ XEN_VM_EVENT_TYPE_MONITOR, 0,
+ nr_frames, (void*)&impl->slots,
+ PROT_READ | PROT_WRITE, 0);
+ if ( !impl->fres )
+ {
+ ERROR("Failed to map vm_event resource");
+ rc = -errno;
+ goto err;
+ }
+
+ for ( i = 0; i < impl->num_vcpus; 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;
+ }
+
+ rc = xc_monitor_ng_set_state(xch, domain_id, true);
+ if ( rc < 0 )
+ {
+ ERROR("Failed to start monitor rc = %d", rc);
+ goto err;
+ }
+
+
+ *vm_event = (vm_event_t*) impl;
+ return 0;
+
+err:
+ xc_monitor_ng_destroy(xch, domain_id);
+ xenforeignmemory_unmap_resource(impl->fmem, impl->fres);
+ xenforeignmemory_close(impl->fmem);
+ 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_vcpus; 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);
+
+ xc_monitor_ng_destroy(impl->vme.xch, impl->vme.domain_id);
+ xenforeignmemory_unmap_resource(impl->fmem, impl->fres);
+ xenforeignmemory_close(impl->fmem);
+
+ return 0;
+}
+
+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:
+- */
new file mode 100644
@@ -0,0 +1,193 @@
+/*
+ * 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 )
+ 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 )
+ 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:
+ */
@@ -35,12 +35,8 @@
#include <time.h>
#include <signal.h>
#include <unistd.h>
-#include <sys/mman.h>
#include <poll.h>
-
-#include <xenctrl.h>
-#include <xenevtchn.h>
-#include <xen/vm_event.h>
+#include <getopt.h>
#include <xen-tools/libs.h>
@@ -51,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
@@ -62,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;
@@ -128,160 +104,86 @@ 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 )
- 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 )
+ return NULL;
+
xch = xc_interface_open(NULL, NULL, 0);
if ( !xch )
- 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 )
{
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);
+
+ rc = ops->init(xch, xce, domain_id, ops, &vm_event);
+ if ( rc < 0 )
+ goto err;
- DPRINTF("max_gpfn = %"PRI_xen_pfn"\n", xenaccess->max_gpfn);
+ vm_event->xch = xch;
+ vm_event->xce = xce;
+ vm_event->domain_id = domain_id;
+ vm_event->ops = ops;
+ vm_event->max_gpfn = max_gpfn;
- return xenaccess;
+ return vm_event;
err:
- rc = xenaccess_teardown(xch, xenaccess);
- if ( rc )
- {
- ERROR("Failed to teardown xenaccess structure!\n");
- }
+ xenevtchn_close(xce);
+ xc_interface_close(xch);
- err_iface:
return NULL;
}
@@ -299,26 +201,6 @@ int control_singlestep(
}
/*
- * 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)
@@ -336,29 +218,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__)
@@ -368,19 +230,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;
@@ -395,106 +260,122 @@ 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];
- argv++;
- argc--;
+ char* command;
+ int c;
+ int option_index;
+ struct option long_options[] =
+ {
+ { "mem-access-listener", no_argument, 0, 'm' },
+ { "new-interface", no_argument, 0, 'n' }
+ };
- if ( argc == 3 && argv[0][0] == '-' )
+ while(1)
{
- if ( !strcmp(argv[0], "-m") )
- required = 1;
- else
+ c = getopt_long(argc, argv, "mn", long_options, &option_index);
+ if ( c == -1 )
+ break;
+
+ switch (c)
{
- usage(progname);
- return -1;
+ case 'm':
+ required = 1;
+ break;
+
+ case 'n':
+ new_interface = 1;
+ break;
+
+ default:
+ usage(progname);
+ return -1;
}
- argv++;
- argc--;
}
- if ( argc != 2 )
+ if ( argc - optind != 2 )
{
usage(progname);
return -1;
}
- domain_id = atoi(argv[0]);
- argv++;
- argc--;
+ domain_id = atoi(argv[optind++]);
+ command = argv[optind];
- if ( !strcmp(argv[0], "write") )
+ if ( !strcmp(command, "write") )
{
default_access = XENMEM_access_rx;
after_first_access = XENMEM_access_rwx;
memaccess = 1;
}
- else if ( !strcmp(argv[0], "exec") )
+ else if ( !strcmp(command, "exec") )
{
default_access = XENMEM_access_rw;
after_first_access = XENMEM_access_rwx;
memaccess = 1;
}
#if defined(__i386__) || defined(__x86_64__)
- else if ( !strcmp(argv[0], "breakpoint") )
+ else if ( !strcmp(command, "breakpoint") )
{
breakpoint = 1;
}
- else if ( !strcmp(argv[0], "altp2m_write") )
+ else if ( !strcmp(command, "altp2m_write") )
{
default_access = XENMEM_access_rx;
altp2m = 1;
memaccess = 1;
}
- else if ( !strcmp(argv[0], "altp2m_exec") )
+ else if ( !strcmp(command, "altp2m_exec") )
{
default_access = XENMEM_access_rw;
altp2m = 1;
memaccess = 1;
}
- else if ( !strcmp(argv[0], "altp2m_write_no_gpt") )
+ else if ( !strcmp(command, "altp2m_write_no_gpt") )
{
default_access = XENMEM_access_rw;
altp2m_write_no_gpt = 1;
memaccess = 1;
altp2m = 1;
}
- else if ( !strcmp(argv[0], "debug") )
+ else if ( !strcmp(command, "debug") )
{
debug = 1;
}
- else if ( !strcmp(argv[0], "cpuid") )
+ else if ( !strcmp(command, "cpuid") )
{
cpuid = 1;
}
- else if ( !strcmp(argv[0], "desc_access") )
+ else if ( !strcmp(command, "desc_access") )
{
desc_access = 1;
}
- else if ( !strcmp(argv[0], "write_ctrlreg_cr4") )
+ else if ( !strcmp(command, "write_ctrlreg_cr4") )
{
write_ctrlreg_cr4 = 1;
}
#elif defined(__arm__) || defined(__aarch64__)
- else if ( !strcmp(argv[0], "privcall") )
+ else if ( !strcmp(command, "privcall") )
{
privcall = 1;
}
#endif
else
{
- usage(argv[0]);
+ usage(command);
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;
}
- DPRINTF("starting %s %u\n", argv[0], domain_id);
+ DPRINTF("starting %s %u\n", command, domain_id);
/* ensure that if we get a signal, we'll do cleanup, then exit */
act.sa_handler = close_handler;
@@ -506,7 +387,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);
@@ -521,13 +402,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);
@@ -535,14 +416,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);
@@ -552,24 +434,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);
@@ -580,15 +462,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 )
{
@@ -600,7 +482,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);
@@ -610,7 +492,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);
@@ -620,7 +502,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);
@@ -630,7 +512,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);
@@ -640,7 +522,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);
@@ -651,7 +533,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 )
{
@@ -663,41 +545,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");
@@ -709,10 +593,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");
@@ -735,7 +619,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);
@@ -768,7 +652,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)
{
@@ -788,7 +672,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);
@@ -833,7 +717,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,
@@ -896,17 +780,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 )
@@ -919,13 +801,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;
new file mode 100644
@@ -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:
+ */
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 | 210 ++++++++++++++++++ tools/tests/xen-access/vm-event.c | 193 +++++++++++++++++ tools/tests/xen-access/xen-access.c | 408 +++++++++++++---------------------- tools/tests/xen-access/xen-access.h | 91 ++++++++ 5 files changed, 644 insertions(+), 265 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