[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [RFC PATCH V3 05/12] xen: Introduce vm_event
To make it easier to review the renaming process of mem_event -> vm_event, the process is broken into three pieces, of which this patch is the first. In this patch the vm_event subsystem is introduced and hooked into the build process, but it is not yet used anywhere. Signed-off-by: Tamas K Lengyel <tamas.lengyel@xxxxxxxxxxxx> --- MAINTAINERS | 1 + docs/misc/xsm-flask.txt | 1 + tools/libxc/Makefile | 1 + tools/libxc/xc_private.h | 12 + tools/libxc/xc_vm_event.c | 162 ++++++++ xen/common/Makefile | 1 + xen/common/vm_event.c | 739 ++++++++++++++++++++++++++++++++++++ xen/include/public/domctl.h | 79 ++++ xen/include/public/vm_event.h | 193 ++++++++++ xen/include/xen/sched.h | 41 ++ xen/include/xen/vm_event.h | 143 +++++++ xen/include/xsm/dummy.h | 12 + xen/include/xsm/xsm.h | 12 + xen/xsm/dummy.c | 2 + xen/xsm/flask/hooks.c | 12 + xen/xsm/flask/policy/access_vectors | 1 + 16 files changed, 1412 insertions(+) create mode 100644 tools/libxc/xc_vm_event.c create mode 100644 xen/common/vm_event.c create mode 100644 xen/include/public/vm_event.h create mode 100644 xen/include/xen/vm_event.h diff --git a/MAINTAINERS b/MAINTAINERS index 3bbac9e..cff3e5f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -364,6 +364,7 @@ F: tools/memshr MEMORY EVENT AND ACCESS M: Tim Deegan <tim@xxxxxxx> S: Supported +F: xen/common/vm_event.c F: xen/common/mem_event.c F: xen/common/mem_access.c diff --git a/docs/misc/xsm-flask.txt b/docs/misc/xsm-flask.txt index 9559028..9eead61 100644 --- a/docs/misc/xsm-flask.txt +++ b/docs/misc/xsm-flask.txt @@ -87,6 +87,7 @@ __HYPERVISOR_domctl (xen/include/public/domctl.h) * XEN_DOMCTL_set_machine_address_size * XEN_DOMCTL_debug_op * XEN_DOMCTL_gethvmcontext_partial + * XEN_DOMCTL_vm_event_op * XEN_DOMCTL_mem_event_op * XEN_DOMCTL_mem_sharing_op * XEN_DOMCTL_setvcpuextstate diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile index bd2ca6c..b97e535 100644 --- a/tools/libxc/Makefile +++ b/tools/libxc/Makefile @@ -26,6 +26,7 @@ CTRL_SRCS-y += xc_pm.c CTRL_SRCS-y += xc_cpu_hotplug.c CTRL_SRCS-y += xc_resume.c CTRL_SRCS-y += xc_tmem.c +CTRL_SRCS-y += xc_vm_event.c CTRL_SRCS-y += xc_mem_event.c CTRL_SRCS-y += xc_mem_paging.c CTRL_SRCS-y += xc_mem_access.c diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h index f1f601c..58db86d 100644 --- a/tools/libxc/xc_private.h +++ b/tools/libxc/xc_private.h @@ -432,4 +432,16 @@ int xc_mem_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, void *xc_mem_event_enable(xc_interface *xch, domid_t domain_id, int param, uint32_t *port, int enable_introspection); +/** + * vm_event operations. Internal use only. + */ +int xc_vm_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, + unsigned int mode, uint32_t *port); +/* + * Enables vm_event and returns the mapped ring page indicated by param. + * param can be HVM_PARAM_PAGING/ACCESS/SHARING_RING_PFN + */ +void *xc_vm_event_enable(xc_interface *xch, domid_t domain_id, int param, + uint32_t *port, int enable_introspection); + #endif /* __XC_PRIVATE_H__ */ diff --git a/tools/libxc/xc_vm_event.c b/tools/libxc/xc_vm_event.c new file mode 100644 index 0000000..dda766e --- /dev/null +++ b/tools/libxc/xc_vm_event.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * + * xc_vm_event.c + * + * Interface to low-level VM event functionality. + * + * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xc_private.h" + +int xc_vm_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, + unsigned int mode, uint32_t *port) +{ + DECLARE_DOMCTL; + int rc; + + domctl.cmd = XEN_DOMCTL_mem_event_op; + domctl.domain = domain_id; + domctl.u.vm_event_op.op = op; + domctl.u.vm_event_op.mode = mode; + + rc = do_domctl(xch, &domctl); + if ( !rc && port ) + *port = domctl.u.vm_event_op.port; + return rc; +} + +void *xc_vm_event_enable(xc_interface *xch, domid_t domain_id, int param, + uint32_t *port, int enable_introspection) +{ + void *ring_page = NULL; + uint64_t pfn; + xen_pfn_t ring_pfn, mmap_pfn; + unsigned int op, mode; + int rc1, rc2, saved_errno; + + if ( !port ) + { + errno = EINVAL; + return NULL; + } + + /* Pause the domain for ring page setup */ + rc1 = xc_domain_pause(xch, domain_id); + if ( rc1 != 0 ) + { + PERROR("Unable to pause domain\n"); + return NULL; + } + + /* Get the pfn of the ring page */ + rc1 = xc_hvm_param_get(xch, domain_id, param, &pfn); + if ( rc1 != 0 ) + { + PERROR("Failed to get pfn of ring page\n"); + goto out; + } + + ring_pfn = pfn; + mmap_pfn = pfn; + ring_page = xc_map_foreign_batch(xch, domain_id, PROT_READ | PROT_WRITE, + &mmap_pfn, 1); + if ( mmap_pfn & XEN_DOMCTL_PFINFO_XTAB ) + { + /* Map failed, populate ring page */ + rc1 = xc_domain_populate_physmap_exact(xch, domain_id, 1, 0, 0, + &ring_pfn); + if ( rc1 != 0 ) + { + PERROR("Failed to populate ring pfn\n"); + goto out; + } + + mmap_pfn = ring_pfn; + ring_page = xc_map_foreign_batch(xch, domain_id, PROT_READ | PROT_WRITE, + &mmap_pfn, 1); + if ( mmap_pfn & XEN_DOMCTL_PFINFO_XTAB ) + { + PERROR("Could not map the ring page\n"); + goto out; + } + } + + switch ( param ) + { + case HVM_PARAM_PAGING_RING_PFN: + op = XEN_DOMCTL_VM_EVENT_OP_PAGING_ENABLE; + mode = XEN_DOMCTL_VM_EVENT_OP_PAGING; + break; + + case HVM_PARAM_MONITOR_RING_PFN: + if ( enable_introspection ) + op = XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE_INTROSPECTION; + else + op = XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE; + mode = XEN_DOMCTL_VM_EVENT_OP_MONITOR; + break; + + case HVM_PARAM_SHARING_RING_PFN: + op = XEN_DOMCTL_VM_EVENT_OP_SHARING_ENABLE; + mode = XEN_DOMCTL_VM_EVENT_OP_SHARING; + break; + + /* + * This is for the outside chance that the HVM_PARAM is valid but is invalid + * as far as vm_event goes. + */ + default: + errno = EINVAL; + rc1 = -1; + goto out; + } + + rc1 = xc_vm_event_control(xch, domain_id, op, mode, port); + if ( rc1 != 0 ) + { + PERROR("Failed to enable vm_event\n"); + goto out; + } + + /* Remove the ring_pfn from the guest's physmap */ + rc1 = xc_domain_decrease_reservation_exact(xch, domain_id, 1, 0, &ring_pfn); + if ( rc1 != 0 ) + PERROR("Failed to remove ring page from guest physmap"); + + out: + saved_errno = errno; + + rc2 = xc_domain_unpause(xch, domain_id); + if ( rc1 != 0 || rc2 != 0 ) + { + if ( rc2 != 0 ) + { + if ( rc1 == 0 ) + saved_errno = errno; + PERROR("Unable to unpause domain"); + } + + if ( ring_page ) + munmap(ring_page, XC_PAGE_SIZE); + ring_page = NULL; + + errno = saved_errno; + } + + return ring_page; +} diff --git a/xen/common/Makefile b/xen/common/Makefile index 1956091..0db6967 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -55,6 +55,7 @@ obj-y += lzo.o obj-$(HAS_PDX) += pdx.o obj-$(HAS_MEM_ACCESS) += mem_access.o obj-$(HAS_MEM_ACCESS) += mem_event.o +obj-$(HAS_MEM_ACCESS) += vm_event.o obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma unlzo unlz4 earlycpio,$(n).init.o) diff --git a/xen/common/vm_event.c b/xen/common/vm_event.c new file mode 100644 index 0000000..0db899e --- /dev/null +++ b/xen/common/vm_event.c @@ -0,0 +1,739 @@ +/****************************************************************************** + * vm_event.c + * + * VM event support. + * + * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <xen/sched.h> +#include <xen/event.h> +#include <xen/wait.h> +#include <xen/vm_event.h> +#include <xen/mem_access.h> +#include <asm/p2m.h> + +#ifdef HAS_MEM_PAGING +#include <asm/mem_paging.h> +#endif + +#ifdef HAS_MEM_SHARING +#include <asm/mem_sharing.h> +#endif + +#include <xsm/xsm.h> + +/* for public/io/ring.h macros */ +#define xen_mb() mb() +#define xen_rmb() rmb() +#define xen_wmb() wmb() + +#define vm_event_ring_lock_init(_ved) spin_lock_init(&(_ved)->ring_lock) +#define vm_event_ring_lock(_ved) spin_lock(&(_ved)->ring_lock) +#define vm_event_ring_unlock(_ved) spin_unlock(&(_ved)->ring_lock) + +static int vm_event_enable( + struct domain *d, + xen_domctl_vm_event_op_t *vec, + struct vm_event_domain *ved, + int pause_flag, + int param, + xen_event_channel_notification_t notification_fn) +{ + int rc; + unsigned long ring_gfn = d->arch.hvm_domain.params[param]; + + /* Only one helper at a time. If the helper crashed, + * the ring is in an undefined state and so is the guest. + */ + if ( ved->ring_page ) + return -EBUSY; + + /* The parameter defaults to zero, and it should be + * set to something */ + if ( ring_gfn == 0 ) + return -ENOSYS; + + vm_event_ring_lock_init(ved); + vm_event_ring_lock(ved); + + rc = prepare_ring_for_helper(d, ring_gfn, &ved->ring_pg_struct, + &ved->ring_page); + if ( rc < 0 ) + goto err; + + /* Set the number of currently blocked vCPUs to 0. */ + ved->blocked = 0; + + /* Allocate event channel */ + rc = alloc_unbound_xen_event_channel(d->vcpu[0], + current->domain->domain_id, + notification_fn); + if ( rc < 0 ) + goto err; + + ved->xen_port = vec->port = rc; + + /* Prepare ring buffer */ + FRONT_RING_INIT(&ved->front_ring, + (vm_event_sring_t *)ved->ring_page, + PAGE_SIZE); + + /* Save the pause flag for this particular ring. */ + ved->pause_flag = pause_flag; + + /* Initialize the last-chance wait queue. */ + init_waitqueue_head(&ved->wq); + + vm_event_ring_unlock(ved); + return 0; + + err: + destroy_ring_for_helper(&ved->ring_page, + ved->ring_pg_struct); + vm_event_ring_unlock(ved); + + return rc; +} + +static unsigned int vm_event_ring_available(struct vm_event_domain *ved) +{ + int avail_req = RING_FREE_REQUESTS(&ved->front_ring); + avail_req -= ved->target_producers; + avail_req -= ved->foreign_producers; + + BUG_ON(avail_req < 0); + + return avail_req; +} + +/* + * vm_event_wake_blocked() will wakeup vcpus waiting for room in the + * ring. These vCPUs were paused on their way out after placing an event, + * but need to be resumed where the ring is capable of processing at least + * one event from them. + */ +static void vm_event_wake_blocked(struct domain *d, struct vm_event_domain *ved) +{ + struct vcpu *v; + int online = d->max_vcpus; + unsigned int avail_req = vm_event_ring_available(ved); + + if ( avail_req == 0 || ved->blocked == 0 ) + return; + + /* + * We ensure that we only have vCPUs online if there are enough free slots + * for their memory events to be processed. This will ensure that no + * memory events are lost (due to the fact that certain types of events + * cannot be replayed, we need to ensure that there is space in the ring + * for when they are hit). + * See comment below in vm_event_put_request(). + */ + for_each_vcpu ( d, v ) + if ( test_bit(ved->pause_flag, &v->pause_flags) ) + online--; + + ASSERT(online == (d->max_vcpus - ved->blocked)); + + /* We remember which vcpu last woke up to avoid scanning always linearly + * from zero and starving higher-numbered vcpus under high load */ + if ( d->vcpu ) + { + int i, j, k; + + for (i = ved->last_vcpu_wake_up + 1, j = 0; j < d->max_vcpus; i++, j++) + { + k = i % d->max_vcpus; + v = d->vcpu[k]; + if ( !v ) + continue; + + if ( !(ved->blocked) || online >= avail_req ) + break; + + if ( test_and_clear_bit(ved->pause_flag, &v->pause_flags) ) + { + vcpu_unpause(v); + online++; + ved->blocked--; + ved->last_vcpu_wake_up = k; + } + } + } +} + +/* + * In the event that a vCPU attempted to place an event in the ring and + * was unable to do so, it is queued on a wait queue. These are woken as + * needed, and take precedence over the blocked vCPUs. + */ +static void vm_event_wake_queued(struct domain *d, struct vm_event_domain *ved) +{ + unsigned int avail_req = vm_event_ring_available(ved); + + if ( avail_req > 0 ) + wake_up_nr(&ved->wq, avail_req); +} + +/* + * vm_event_wake() will wakeup all vcpus waiting for the ring to + * become available. If we have queued vCPUs, they get top priority. We + * are guaranteed that they will go through code paths that will eventually + * call vm_event_wake() again, ensuring that any blocked vCPUs will get + * unpaused once all the queued vCPUs have made it through. + */ +void vm_event_wake(struct domain *d, struct vm_event_domain *ved) +{ + if (!list_empty(&ved->wq.list)) + vm_event_wake_queued(d, ved); + else + vm_event_wake_blocked(d, ved); +} + +static int vm_event_disable(struct domain *d, struct vm_event_domain *ved) +{ + if ( ved->ring_page ) + { + struct vcpu *v; + + vm_event_ring_lock(ved); + + if ( !list_empty(&ved->wq.list) ) + { + vm_event_ring_unlock(ved); + return -EBUSY; + } + + /* Free domU's event channel and leave the other one unbound */ + free_xen_event_channel(d->vcpu[0], ved->xen_port); + + /* Unblock all vCPUs */ + for_each_vcpu ( d, v ) + { + if ( test_and_clear_bit(ved->pause_flag, &v->pause_flags) ) + { + vcpu_unpause(v); + ved->blocked--; + } + } + + destroy_ring_for_helper(&ved->ring_page, + ved->ring_pg_struct); + vm_event_ring_unlock(ved); + } + + return 0; +} + +static inline void vm_event_release_slot(struct domain *d, + struct vm_event_domain *ved) +{ + /* Update the accounting */ + if ( current->domain == d ) + ved->target_producers--; + else + ved->foreign_producers--; + + /* Kick any waiters */ + vm_event_wake(d, ved); +} + +/* + * vm_event_mark_and_pause() tags vcpu and put it to sleep. + * The vcpu will resume execution in vm_event_wake_waiters(). + */ +void vm_event_mark_and_pause(struct vcpu *v, struct vm_event_domain *ved) +{ + if ( !test_and_set_bit(ved->pause_flag, &v->pause_flags) ) + { + vcpu_pause_nosync(v); + ved->blocked++; + } +} + +/* + * This must be preceded by a call to claim_slot(), and is guaranteed to + * succeed. As a side-effect however, the vCPU may be paused if the ring is + * overly full and its continued execution would cause stalling and excessive + * waiting. The vCPU will be automatically unpaused when the ring clears. + */ +void vm_event_put_request(struct domain *d, + struct vm_event_domain *ved, + vm_event_request_t *req) +{ + vm_event_front_ring_t *front_ring; + int free_req; + unsigned int avail_req; + RING_IDX req_prod; + + if ( current->domain != d ) + { + req->flags |= VM_EVENT_FLAG_FOREIGN; +#ifndef NDEBUG + if ( !(req->flags & VM_EVENT_FLAG_VCPU_PAUSED) ) + gdprintk(XENLOG_G_WARNING, "d%dv%d was not paused.\n", + d->domain_id, req->vcpu_id); +#endif + } + + vm_event_ring_lock(ved); + + /* Due to the reservations, this step must succeed. */ + front_ring = &ved->front_ring; + free_req = RING_FREE_REQUESTS(front_ring); + ASSERT(free_req > 0); + + /* Copy request */ + req_prod = front_ring->req_prod_pvt; + memcpy(RING_GET_REQUEST(front_ring, req_prod), req, sizeof(*req)); + req_prod++; + + /* Update ring */ + front_ring->req_prod_pvt = req_prod; + RING_PUSH_REQUESTS(front_ring); + + /* We've actually *used* our reservation, so release the slot. */ + vm_event_release_slot(d, ved); + + /* Give this vCPU a black eye if necessary, on the way out. + * See the comments above wake_blocked() for more information + * on how this vechanism works to avoid waiting. */ + avail_req = vm_event_ring_available(ved); + if( current->domain == d && avail_req < d->max_vcpus ) + vm_event_mark_and_pause(current, ved); + + vm_event_ring_unlock(ved); + + notify_via_xen_event_channel(d, ved->xen_port); +} + +int vm_event_get_response(struct domain *d, struct vm_event_domain *ved, vm_event_response_t *rsp) +{ + vm_event_front_ring_t *front_ring; + RING_IDX rsp_cons; + + vm_event_ring_lock(ved); + + front_ring = &ved->front_ring; + rsp_cons = front_ring->rsp_cons; + + if ( !RING_HAS_UNCONSUMED_RESPONSES(front_ring) ) + { + vm_event_ring_unlock(ved); + return 0; + } + + /* Copy response */ + memcpy(rsp, RING_GET_RESPONSE(front_ring, rsp_cons), sizeof(*rsp)); + rsp_cons++; + + /* Update ring */ + front_ring->rsp_cons = rsp_cons; + front_ring->sring->rsp_event = rsp_cons + 1; + + /* Kick any waiters -- since we've just consumed an event, + * there may be additional space available in the ring. */ + vm_event_wake(d, ved); + + vm_event_ring_unlock(ved); + + return 1; +} + +void vm_event_cancel_slot(struct domain *d, struct vm_event_domain *ved) +{ + vm_event_ring_lock(ved); + vm_event_release_slot(d, ved); + vm_event_ring_unlock(ved); +} + +static int vm_event_grab_slot(struct vm_event_domain *ved, int foreign) +{ + unsigned int avail_req; + + if ( !ved->ring_page ) + return -ENOSYS; + + vm_event_ring_lock(ved); + + avail_req = vm_event_ring_available(ved); + if ( avail_req == 0 ) + { + vm_event_ring_unlock(ved); + return -EBUSY; + } + + if ( !foreign ) + ved->target_producers++; + else + ved->foreign_producers++; + + vm_event_ring_unlock(ved); + + return 0; +} + +/* Simple try_grab wrapper for use in the wait_event() macro. */ +static int vm_event_wait_try_grab(struct vm_event_domain *ved, int *rc) +{ + *rc = vm_event_grab_slot(ved, 0); + return *rc; +} + +/* Call vm_event_grab_slot() until the ring doesn't exist, or is available. */ +static int vm_event_wait_slot(struct vm_event_domain *ved) +{ + int rc = -EBUSY; + wait_event(ved->wq, vm_event_wait_try_grab(ved, &rc) != -EBUSY); + return rc; +} + +bool_t vm_event_check_ring(struct vm_event_domain *ved) +{ + return (ved->ring_page != NULL); +} + +/* + * Determines whether or not the current vCPU belongs to the target domain, + * and calls the appropriate wait function. If it is a guest vCPU, then we + * use vm_event_wait_slot() to reserve a slot. As long as there is a ring, + * this function will always return 0 for a guest. For a non-guest, we check + * for space and return -EBUSY if the ring is not available. + * + * Return codes: -ENOSYS: the ring is not yet configured + * -EBUSY: the ring is busy + * 0: a spot has been reserved + * + */ +int __vm_event_claim_slot(struct domain *d, struct vm_event_domain *ved, + bool_t allow_sleep) +{ + if ( (current->domain == d) && allow_sleep ) + return vm_event_wait_slot(ved); + else + return vm_event_grab_slot(ved, (current->domain != d)); +} + +#ifdef HAS_MEM_PAGING +/* Registered with Xen-bound event channel for incoming notifications. */ +static void mem_paging_notification(struct vcpu *v, unsigned int port) +{ + if ( likely(v->domain->vm_event->paging.ring_page != NULL) ) + p2m_mem_paging_resume(v->domain); +} +#endif + +#ifdef HAS_MEM_ACCESS +/* Registered with Xen-bound event channel for incoming notifications. */ +static void mem_access_notification(struct vcpu *v, unsigned int port) +{ + if ( likely(v->domain->vm_event->monitor.ring_page != NULL) ) + mem_access_resume(v->domain); +} +#endif + +#ifdef HAS_MEM_SHARING +/* Registered with Xen-bound event channel for incoming notifications. */ +static void mem_sharing_notification(struct vcpu *v, unsigned int port) +{ + if ( likely(v->domain->vm_event->share.ring_page != NULL) ) + mem_sharing_sharing_resume(v->domain); +} +#endif + +int do_vm_event_op(int op, uint32_t domain, void *arg) +{ + int ret; + struct domain *d; + + ret = rcu_lock_live_remote_domain_by_id(domain, &d); + if ( ret ) + return ret; + + ret = xsm_vm_event_op(XSM_DM_PRIV, d, op); + if ( ret ) + goto out; + + switch (op) + { +#ifdef HAS_MEM_PAGING + case XENMEM_paging_op: + ret = mem_paging_memop(d, arg); + break; +#endif +#ifdef HAS_MEM_SHARING + case XENMEM_sharing_op: + ret = mem_sharing_memop(d, arg); + break; +#endif + default: + ret = -ENOSYS; + } + + out: + rcu_unlock_domain(d); + return ret; +} + +/* Clean up on domain destruction */ +void vm_event_cleanup(struct domain *d) +{ +#ifdef HAS_MEM_PAGING + if ( d->vm_event->paging.ring_page ) { + /* Destroying the wait queue head means waking up all + * queued vcpus. This will drain the list, allowing + * the disable routine to complete. It will also drop + * all domain refs the wait-queued vcpus are holding. + * Finally, because this code path involves previously + * pausing the domain (domain_kill), unpausing the + * vcpus causes no harm. */ + destroy_waitqueue_head(&d->vm_event->paging.wq); + (void)vm_event_disable(d, &d->vm_event->paging); + } +#endif +#ifdef HAS_MEM_ACCESS + if ( d->vm_event->monitor.ring_page ) { + destroy_waitqueue_head(&d->vm_event->monitor.wq); + (void)vm_event_disable(d, &d->vm_event->monitor); + } +#endif +#ifdef HAS_MEM_SHARING + if ( d->vm_event->share.ring_page ) { + destroy_waitqueue_head(&d->vm_event->share.wq); + (void)vm_event_disable(d, &d->vm_event->share); + } +#endif +} + +int vm_event_domctl(struct domain *d, xen_domctl_vm_event_op_t *vec, + XEN_GUEST_HANDLE_PARAM(void) u_domctl) +{ + int rc; + + rc = xsm_vm_event_control(XSM_PRIV, d, vec->mode, vec->op); + if ( rc ) + return rc; + + if ( unlikely(d == current->domain) ) + { + gdprintk(XENLOG_INFO, "Tried to do a memory event op on itself.\n"); + return -EINVAL; + } + + if ( unlikely(d->is_dying) ) + { + gdprintk(XENLOG_INFO, "Ignoring memory event op on dying domain %u\n", + d->domain_id); + return 0; + } + + if ( unlikely(d->vcpu == NULL) || unlikely(d->vcpu[0] == NULL) ) + { + gdprintk(XENLOG_INFO, + "Memory event op on a domain (%u) with no vcpus\n", + d->domain_id); + return -EINVAL; + } + + rc = -ENOSYS; + + switch ( vec->mode ) + { +#ifdef HAS_MEM_PAGING + case XEN_DOMCTL_VM_EVENT_OP_PAGING: + { + struct vm_event_domain *ved = &d->vm_event->paging; + rc = -EINVAL; + + switch( vec->op ) + { + case XEN_DOMCTL_VM_EVENT_OP_PAGING_ENABLE: + { + struct p2m_domain *p2m = p2m_get_hostp2m(d); + + rc = -EOPNOTSUPP; + /* pvh fixme: p2m_is_foreign types need addressing */ + if ( is_pvh_vcpu(current) || is_pvh_domain(hardware_domain) ) + break; + + rc = -ENODEV; + /* Only HAP is supported */ + if ( !hap_enabled(d) ) + break; + + /* No paging if iommu is used */ + rc = -EMLINK; + if ( unlikely(need_iommu(d)) ) + break; + + rc = -EXDEV; + /* Disallow paging in a PoD guest */ + if ( p2m->pod.entry_count ) + break; + + rc = vm_event_enable(d, vec, ved, _VPF_mem_paging, + HVM_PARAM_PAGING_RING_PFN, + mem_paging_notification); + } + break; + + case XEN_DOMCTL_VM_EVENT_OP_PAGING_DISABLE: + { + if ( ved->ring_page ) + rc = vm_event_disable(d, ved); + } + break; + + default: + rc = -ENOSYS; + break; + } + } + break; +#endif + +#ifdef HAS_MEM_ACCESS + case XEN_DOMCTL_VM_EVENT_OP_MONITOR: + { + struct vm_event_domain *ved = &d->vm_event->monitor; + rc = -EINVAL; + + switch( vec->op ) + { + case XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE: + case XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE_INTROSPECTION: + { + rc = vm_event_enable(d, vec, ved, _VPF_mem_access, + HVM_PARAM_MONITOR_RING_PFN, + mem_access_notification); + + if ( vec->op == XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE_INTROSPECTION + && !rc ) + p2m_setup_introspection(d); + + } + break; + + case XEN_DOMCTL_VM_EVENT_OP_MONITOR_DISABLE: + { + if ( ved->ring_page ) + { + rc = vm_event_disable(d, ved); + d->arch.hvm_domain.introspection_enabled = 0; + } + } + break; + + default: + rc = -ENOSYS; + break; + } + } + break; +#endif + +#ifdef HAS_MEM_SHARING + case XEN_DOMCTL_VM_EVENT_OP_SHARING: + { + struct vm_event_domain *ved = &d->vm_event->share; + rc = -EINVAL; + + switch( vec->op ) + { + case XEN_DOMCTL_VM_EVENT_OP_SHARING_ENABLE: + { + rc = -EOPNOTSUPP; + /* pvh fixme: p2m_is_foreign types need addressing */ + if ( is_pvh_vcpu(current) || is_pvh_domain(hardware_domain) ) + break; + + rc = -ENODEV; + /* Only HAP is supported */ + if ( !hap_enabled(d) ) + break; + + rc = vm_event_enable(d, vec, ved, _VPF_mem_sharing, + HVM_PARAM_SHARING_RING_PFN, + mem_sharing_notification); + } + break; + + case XEN_DOMCTL_VM_EVENT_OP_SHARING_DISABLE: + { + if ( ved->ring_page ) + rc = vm_event_disable(d, ved); + } + break; + + default: + rc = -ENOSYS; + break; + } + } + break; +#endif + + default: + rc = -ENOSYS; + } + + return rc; +} + +void vm_event_vcpu_pause(struct vcpu *v) +{ + ASSERT(v == current); + + atomic_inc(&v->vm_event_pause_count); + vcpu_pause_nosync(v); +} + +void vm_event_vcpu_unpause(struct vcpu *v) +{ + int old, new, prev = v->vm_event_pause_count.counter; + + /* All unpause requests as a result of toolstack responses. Prevent + * underflow of the vcpu pause count. */ + do + { + old = prev; + new = old - 1; + + if ( new < 0 ) + { + printk(XENLOG_G_WARNING + "%pv vm_event: Too many unpause attempts\n", v); + return; + } + + prev = cmpxchg(&v->vm_event_pause_count.counter, old, new); + } while ( prev != old ); + + vcpu_unpause(v); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ + diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index 596a624..a7d3e94 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -835,6 +835,84 @@ typedef struct xen_domctl_mem_event_op xen_domctl_mem_event_op_t; DEFINE_XEN_GUEST_HANDLE(xen_domctl_mem_event_op_t); /* + * VM event operations + */ + +/* XEN_DOMCTL_vm_event_op */ + +/* + * Domain memory paging + * Page memory in and out. + * Domctl interface to set up and tear down the + * pager<->hypervisor interface. Use XENMEM_paging_op* + * to perform per-page operations. + * + * The XEN_DOMCTL_MEM_EVENT_OP_PAGING_ENABLE domctl returns several + * non-standard error codes to indicate why paging could not be enabled: + * ENODEV - host lacks HAP support (EPT/NPT) or HAP is disabled in guest + * EMLINK - guest has iommu passthrough enabled + * EXDEV - guest has PoD enabled + * EBUSY - guest has or had paging enabled, ring buffer still active + */ +#define XEN_DOMCTL_VM_EVENT_OP_PAGING 1 + +#define XEN_DOMCTL_VM_EVENT_OP_PAGING_ENABLE 0 +#define XEN_DOMCTL_VM_EVENT_OP_PAGING_DISABLE 1 + +/* + * Monitor permissions. + * + * As with paging, use the domctl for teardown/setup of the + * helper<->hypervisor interface. + * + * There are HVM hypercalls to set the per-page access permissions of every + * page in a domain. When one of these permissions--independent, read, + * write, and execute--is violated, the VCPU is paused and a memory event + * is sent with what happened. (See public/vm_event.h) . + * + * The memory event handler can then resume the VCPU and redo the access + * with a XENMEM_access_op_resume hypercall. + * + * The XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE domctl returns several + * non-standard error codes to indicate why access could not be enabled: + * EBUSY - guest has or had access enabled, ring buffer still active + */ +#define XEN_DOMCTL_VM_EVENT_OP_MONITOR 2 + +#define XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE 0 +#define XEN_DOMCTL_VM_EVENT_OP_MONITOR_DISABLE 1 +#define XEN_DOMCTL_VM_EVENT_OP_MONITOR_ENABLE_INTROSPECTION 2 + +/* + * Sharing ENOMEM helper. + * + * As with paging, use the domctl for teardown/setup of the + * helper<->hypervisor interface. + * + * If setup, this ring is used to communicate failed allocations + * in the unshare path. XENMEM_sharing_op_resume is used to wake up + * vcpus that could not unshare. + * + * Note that shring can be turned on (as per the domctl below) + * *without* this ring being setup. + */ +#define XEN_DOMCTL_VM_EVENT_OP_SHARING 3 + +#define XEN_DOMCTL_VM_EVENT_OP_SHARING_ENABLE 0 +#define XEN_DOMCTL_VM_EVENT_OP_SHARING_DISABLE 1 + +/* Use for teardown/setup of helper<->hypervisor interface for paging, + * access and sharing.*/ +struct xen_domctl_vm_event_op { + uint32_t op; /* XEN_DOMCTL_VM_EVENT_OP_*_* */ + uint32_t mode; /* XEN_DOMCTL_VM_EVENT_OP_* */ + + uint32_t port; /* OUT: event channel for ring */ +}; +typedef struct xen_domctl_vm_event_op xen_domctl_vm_event_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_domctl_vm_event_op_t); + +/* * Memory sharing operations */ /* XEN_DOMCTL_mem_sharing_op. @@ -1123,6 +1201,7 @@ struct xen_domctl { struct xen_domctl_set_target set_target; struct xen_domctl_subscribe subscribe; struct xen_domctl_debug_op debug_op; + struct xen_domctl_vm_event_op vm_event_op; struct xen_domctl_mem_event_op mem_event_op; struct xen_domctl_mem_sharing_op mem_sharing_op; #if defined(__i386__) || defined(__x86_64__) diff --git a/xen/include/public/vm_event.h b/xen/include/public/vm_event.h new file mode 100644 index 0000000..8fba3d1b --- /dev/null +++ b/xen/include/public/vm_event.h @@ -0,0 +1,193 @@ +/****************************************************************************** + * vm_event.h + * + * VM event common structures. + * + * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp) + * + * 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_PUBLIC_VM_EVENT_H +#define _XEN_PUBLIC_VM_EVENT_H + +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#include "xen.h" +#include "io/ring.h" + +#define VM_EVENT_INTERFACE_VERSION 0x00000001 + +/* Memory event flags */ +#define VM_EVENT_FLAG_VCPU_PAUSED (1 << 0) +#define VM_EVENT_FLAG_DROP_PAGE (1 << 1) +#define VM_EVENT_FLAG_EVICT_FAIL (1 << 2) +#define VM_EVENT_FLAG_FOREIGN (1 << 3) +#define VM_EVENT_FLAG_DUMMY (1 << 4) +/* + * Emulate the fault-causing instruction (if set in the event response flags). + * This will allow the guest to continue execution without lifting the page + * access restrictions. + */ +#define VM_EVENT_FLAG_EMULATE (1 << 5) +/* + * Same as VM_EVENT_FLAG_EMULATE, but with write operations or operations + * potentially having side effects (like memory mapped or port I/O) disabled. + */ +#define VM_EVENT_FLAG_EMULATE_NOWRITE (1 << 6) + +/* Reasons for the vm event request */ +/* Default case */ +#define VM_EVENT_REASON_UNKNOWN 0 +/* Memory access violation */ +#define VM_EVENT_REASON_MEM_ACCESS 1 +/* Memory sharing event */ +#define VM_EVENT_REASON_MEM_SHARING 2 +/* Memory paging event */ +#define VM_EVENT_REASON_MEM_PAGING 3 +/* CR0 was updated */ +#define VM_EVENT_REASON_MOV_TO_CR0 4 +/* CR3 was updated */ +#define VM_EVENT_REASON_MOV_TO_CR3 5 +/* CR4 was updated */ +#define VM_EVENT_REASON_MOV_TO_CR4 6 +/* An MSR was updated. Does NOT honour HVMPME_onchangeonly */ +#define VM_EVENT_REASON_MOV_TO_MSR 9 +/* Debug operation executed (int3) */ +#define VM_EVENT_REASON_SOFTWARE_BREAKPOINT 7 +/* Single-step (MTF) */ +#define VM_EVENT_REASON_SINGLESTEP 8 + +/* Using a custom struct (not hvm_hw_cpu) so as to not fill + * the vm_event ring buffer too quickly. */ +struct vm_event_regs_x86 { + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rflags; + uint64_t dr7; + uint64_t rip; + uint64_t cr0; + uint64_t cr2; + uint64_t cr3; + uint64_t cr4; + uint64_t sysenter_cs; + uint64_t sysenter_esp; + uint64_t sysenter_eip; + uint64_t msr_efer; + uint64_t msr_star; + uint64_t msr_lstar; + uint64_t fs_base; + uint64_t gs_base; + uint32_t cs_arbytes; + uint32_t _pad; +}; + +struct vm_event_mem_access_data { + uint64_t gfn; + uint64_t offset; + uint64_t gla; /* if gla_valid */ + uint8_t access_r; + uint8_t access_w; + uint8_t access_x; + uint8_t gla_valid; + uint8_t fault_with_gla; + uint8_t fault_in_gpt; + uint16_t _pad; +}; + +struct vm_event_mov_to_cr_data { + uint64_t new_value; + uint64_t old_value; +}; + +struct vm_event_software_breakpoint_data { + uint64_t gfn; +}; + +struct vm_event_singlestep_data { + uint64_t gfn; +}; + +struct vm_event_mov_to_msr_data { + uint64_t msr; + uint64_t value; +}; + +struct vm_event_paging_data { + uint64_t gfn; + uint32_t p2mt; + uint32_t _pad; +}; + +struct vm_event_sharing_data { + uint64_t gfn; + uint32_t p2mt; + uint32_t _pad; +}; + +typedef struct vm_event_st { + uint32_t version; /* VM_EVENT_INTERFACE_VERSION */ + uint32_t flags; + uint32_t vcpu_id; + uint32_t reason; /* VM_EVENT_REASON_* */ + + union { + struct vm_event_paging_data mem_paging; + struct vm_event_sharing_data mem_sharing; + struct vm_event_mem_access_data mem_access; + struct vm_event_mov_to_cr_data mov_to_cr; + struct vm_event_mov_to_msr_data mov_to_msr; + struct vm_event_software_breakpoint_data software_breakpoint; + struct vm_event_singlestep_data singlestep; + } data; + + union { + struct vm_event_regs_x86 x86; + } regs; +} vm_event_request_t, vm_event_response_t; + +DEFINE_RING_TYPES(vm_event, vm_event_request_t, vm_event_response_t); + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ +#endif /* _XEN_PUBLIC_VM_EVENT_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ + diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index 64a2bd3..681efa9 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -24,6 +24,7 @@ #include <public/sysctl.h> #include <public/vcpu.h> #include <public/mem_event.h> +#include <public/vm_event.h> #include <public/event_channel.h> #ifdef CONFIG_COMPAT @@ -216,6 +217,8 @@ struct vcpu /* VCPU paused for mem_event replies. */ atomic_t mem_event_pause_count; + /* VCPU paused for vm_event replies. */ + atomic_t vm_event_pause_count; /* VCPU paused by system controller. */ int controller_pause_count; @@ -292,6 +295,41 @@ struct mem_event_per_domain struct mem_event_domain monitor; }; +/* VM event */ +struct vm_event_domain +{ + /* ring lock */ + spinlock_t ring_lock; + /* The ring has 64 entries */ + unsigned char foreign_producers; + unsigned char target_producers; + /* shared ring page */ + void *ring_page; + struct page_info *ring_pg_struct; + /* front-end ring */ + vm_event_front_ring_t front_ring; + /* event channel port (vcpu0 only) */ + int xen_port; + /* vm_event bit for vcpu->pause_flags */ + int pause_flag; + /* list of vcpus waiting for room in the ring */ + struct waitqueue_head wq; + /* the number of vCPUs blocked */ + unsigned int blocked; + /* The last vcpu woken up */ + unsigned int last_vcpu_wake_up; +}; + +struct vm_event_per_domain +{ + /* Memory sharing support */ + struct vm_event_domain share; + /* Memory paging support */ + struct vm_event_domain paging; + /* VM event monitor support */ + struct vm_event_domain monitor; +}; + struct evtchn_port_ops; /* @@ -445,6 +483,9 @@ struct domain /* Various mem_events */ struct mem_event_per_domain *mem_event; + /* Various vm_events */ + struct vm_event_per_domain *vm_event; + /* * Can be specified by the user. If that is not the case, it is * computed from the union of all the vcpu cpu-affinity masks. diff --git a/xen/include/xen/vm_event.h b/xen/include/xen/vm_event.h new file mode 100644 index 0000000..988ea42 --- /dev/null +++ b/xen/include/xen/vm_event.h @@ -0,0 +1,143 @@ +/****************************************************************************** + * vm_event.h + * + * Common interface for memory event support. + * + * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __VM_EVENT_H__ +#define __VM_EVENT_H__ + +#include <xen/sched.h> + +#ifdef HAS_MEM_ACCESS + +/* Clean up on domain destruction */ +void vm_event_cleanup(struct domain *d); + +/* Returns whether a ring has been set up */ +bool_t vm_event_check_ring(struct vm_event_domain *med); + +/* Returns 0 on success, -ENOSYS if there is no ring, -EBUSY if there is no + * available space and the caller is a foreign domain. If the guest itself + * is the caller, -EBUSY is avoided by sleeping on a wait queue to ensure + * that the ring does not lose future events. + * + * However, the allow_sleep flag can be set to false in cases in which it is ok + * to lose future events, and thus -EBUSY can be returned to guest vcpus + * (handle with care!). + * + * In general, you must follow a claim_slot() call with either put_request() or + * cancel_slot(), both of which are guaranteed to + * succeed. + */ +int __vm_event_claim_slot(struct domain *d, struct vm_event_domain *med, + bool_t allow_sleep); +static inline int vm_event_claim_slot(struct domain *d, + struct vm_event_domain *med) +{ + return __vm_event_claim_slot(d, med, 1); +} + +static inline int vm_event_claim_slot_nosleep(struct domain *d, + struct vm_event_domain *med) +{ + return __vm_event_claim_slot(d, med, 0); +} + +void vm_event_cancel_slot(struct domain *d, struct vm_event_domain *med); + +void vm_event_put_request(struct domain *d, struct vm_event_domain *med, + vm_event_request_t *req); + +int vm_event_get_response(struct domain *d, struct vm_event_domain *med, + vm_event_response_t *rsp); + +int do_vm_event_op(int op, uint32_t domain, void *arg); +int vm_event_domctl(struct domain *d, xen_domctl_vm_event_op_t *mec, + XEN_GUEST_HANDLE_PARAM(void) u_domctl); + +void vm_event_vcpu_pause(struct vcpu *v); +void vm_event_vcpu_unpause(struct vcpu *v); + +#else + +static inline void vm_event_cleanup(struct domain *d) {} + +static inline bool_t vm_event_check_ring(struct vm_event_domain *med) +{ + return 0; +} + +static inline int vm_event_claim_slot(struct domain *d, + struct vm_event_domain *med) +{ + return -ENOSYS; +} + +static inline int vm_event_claim_slot_nosleep(struct domain *d, + struct vm_event_domain *med) +{ + return -ENOSYS; +} + +static inline +void vm_event_cancel_slot(struct domain *d, struct vm_event_domain *med) +{} + +static inline +void vm_event_put_request(struct domain *d, struct vm_event_domain *med, + vm_event_request_t *req) +{} + +static inline +int vm_event_get_response(struct domain *d, struct vm_event_domain *med, + vm_event_response_t *rsp) +{ + return -ENOSYS; +} + +static inline int do_vm_event_op(int op, uint32_t domain, void *arg) +{ + return -ENOSYS; +} + +static inline +int vm_event_domctl(struct domain *d, xen_domctl_vm_event_op_t *mec, + XEN_GUEST_HANDLE_PARAM(void) u_domctl) +{ + return -ENOSYS; +} + +static inline void vm_event_vcpu_pause(struct vcpu *v) {} +static inline void vm_event_vcpu_unpause(struct vcpu *v) {} + +#endif /* HAS_MEM_ACCESS */ + +#endif /* __VM_EVENT_H__ */ + + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h index f20e89c..d6d403a 100644 --- a/xen/include/xsm/dummy.h +++ b/xen/include/xsm/dummy.h @@ -525,6 +525,18 @@ static XSM_INLINE int xsm_mem_event_op(XSM_DEFAULT_ARG struct domain *d, int op) XSM_ASSERT_ACTION(XSM_DM_PRIV); return xsm_default_action(action, current->domain, d); } + +static XSM_INLINE int xsm_vm_event_control(XSM_DEFAULT_ARG struct domain *d, int mode, int op) +{ + XSM_ASSERT_ACTION(XSM_PRIV); + return xsm_default_action(action, current->domain, d); +} + +static XSM_INLINE int xsm_vm_event_op(XSM_DEFAULT_ARG struct domain *d, int op) +{ + XSM_ASSERT_ACTION(XSM_DM_PRIV); + return xsm_default_action(action, current->domain, d); +} #endif #ifdef CONFIG_X86 diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h index 4ce089f..581e712 100644 --- a/xen/include/xsm/xsm.h +++ b/xen/include/xsm/xsm.h @@ -144,6 +144,8 @@ struct xsm_operations { #ifdef HAS_MEM_ACCESS int (*mem_event_control) (struct domain *d, int mode, int op); int (*mem_event_op) (struct domain *d, int op); + int (*vm_event_control) (struct domain *d, int mode, int op); + int (*vm_event_op) (struct domain *d, int op); #endif #ifdef CONFIG_X86 @@ -553,6 +555,16 @@ static inline int xsm_mem_event_op (xsm_default_t def, struct domain *d, int op) { return xsm_ops->mem_event_op(d, op); } + +static inline int xsm_vm_event_control (xsm_default_t def, struct domain *d, int mode, int op) +{ + return xsm_ops->vm_event_control(d, mode, op); +} + +static inline int xsm_vm_event_op (xsm_default_t def, struct domain *d, int op) +{ + return xsm_ops->vm_event_op(d, op); +} #endif #ifdef CONFIG_X86 diff --git a/xen/xsm/dummy.c b/xen/xsm/dummy.c index 8eb3050..3cf5126 100644 --- a/xen/xsm/dummy.c +++ b/xen/xsm/dummy.c @@ -121,6 +121,8 @@ void xsm_fixup_ops (struct xsm_operations *ops) #ifdef HAS_MEM_ACCESS set_to_dummy_if_null(ops, mem_event_control); set_to_dummy_if_null(ops, mem_event_op); + set_to_dummy_if_null(ops, vm_event_control); + set_to_dummy_if_null(ops, vm_event_op); #endif #ifdef CONFIG_X86 diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c index d48463f..05ebf03 100644 --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -1212,6 +1212,16 @@ static int flask_mem_event_op(struct domain *d, int op) { return current_has_perm(d, SECCLASS_HVM, HVM__MEM_EVENT); } + +static int flask_vm_event_control(struct domain *d, int mode, int op) +{ + return current_has_perm(d, SECCLASS_HVM, HVM__VM_EVENT); +} + +static int flask_vm_event_op(struct domain *d, int op) +{ + return current_has_perm(d, SECCLASS_HVM, HVM__VM_EVENT); +} #endif /* HAS_MEM_ACCESS */ #ifdef CONFIG_X86 @@ -1599,6 +1609,8 @@ static struct xsm_operations flask_ops = { #ifdef HAS_MEM_ACCESS .mem_event_control = flask_mem_event_control, .mem_event_op = flask_mem_event_op, + .vm_event_control = flask_vm_event_control, + .vm_event_op = flask_vm_event_op, #endif #ifdef CONFIG_X86 diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors index 1da9f63..a4241b5 100644 --- a/xen/xsm/flask/policy/access_vectors +++ b/xen/xsm/flask/policy/access_vectors @@ -250,6 +250,7 @@ class hvm hvmctl # XEN_DOMCTL_set_access_required mem_event + vm_event # XEN_DOMCTL_mem_sharing_op and XENMEM_sharing_op_{share,add_physmap} with: # source = the domain making the hypercall # target = domain whose memory is being shared -- 2.1.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |