From patchwork Thu May 30 14:18:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Petre Ovidiu PIRCALABU X-Patchwork-Id: 10968939 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B95E1912 for ; Thu, 30 May 2019 14:20:32 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A852822B1F for ; Thu, 30 May 2019 14:20:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9CB3A28AD5; Thu, 30 May 2019 14:20:32 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A831122B1F for ; Thu, 30 May 2019 14:20:14 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1hWLt5-0003FG-Rd; Thu, 30 May 2019 14:18:35 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1hWLt4-0003CU-4X for xen-devel@lists.xenproject.org; Thu, 30 May 2019 14:18:34 +0000 X-Inumbo-ID: cb69e44c-82e5-11e9-8fc3-d7a4e6e4b3c5 Received: from mx01.bbu.dsd.mx.bitdefender.com (unknown [91.199.104.161]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id cb69e44c-82e5-11e9-8fc3-d7a4e6e4b3c5; Thu, 30 May 2019 14:18:29 +0000 (UTC) Received: from smtp.bitdefender.com (smtp02.buh.bitdefender.net [10.17.80.76]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E54BD305FFAB; Thu, 30 May 2019 17:18:25 +0300 (EEST) Received: from bitdefender.com (unknown [195.189.155.70]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C20813051E7B; Thu, 30 May 2019 17:18:25 +0300 (EEST) From: Petre Pircalabu To: xen-devel@lists.xenproject.org Date: Thu, 30 May 2019 17:18:23 +0300 Message-Id: <2da0f80cc9af391f623466f8152a1728274a967b.1559224640.git.ppircalabu@bitdefender.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: References: In-Reply-To: References: Subject: [Xen-devel] [PATCH 9/9] xen-access: Add support for vm_event_ng interface X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: Petre Pircalabu , Tamas K Lengyel , Ian Jackson , Wei Liu , Razvan Cojocaru MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 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..9177cfc --- /dev/null +++ b/tools/tests/xen-access/vm-event-ng.c @@ -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 +#include +#include +#include +#include +#include +#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: +- */ diff --git a/tools/tests/xen-access/vm-event.c b/tools/tests/xen-access/vm-event.c new file mode 100644 index 0000000..ffd5476 --- /dev/null +++ b/tools/tests/xen-access/vm-event.c @@ -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 +#include +#include +#include +#include +#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: + */ diff --git a/tools/tests/xen-access/xen-access.c b/tools/tests/xen-access/xen-access.c index 6aaee16..267d163 100644 --- a/tools/tests/xen-access/xen-access.c +++ b/tools/tests/xen-access/xen-access.c @@ -35,12 +35,8 @@ #include #include #include -#include #include - -#include -#include -#include +#include #include @@ -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] write|exec", progname); + fprintf(stderr, "Usage: %s [-m] [-n] 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_idxch, 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 +#include +#include + +#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: + */