[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH V5 07/12] xen: Introduce monitor_op domctl
In preparation for allowing for introspecting ARM and PV domains the old control interface via the hvm_op hypercall is retired. A new control mechanism is introduced via the domctl hypercall: monitor_op. This patch aims to establish a base API on which future applications can build on. Suggested-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Signed-off-by: Tamas K Lengyel <tamas.lengyel@xxxxxxxxxxxx> Acked-by: Ian Campbell <ian.campbell@xxxxxxxxxx> Acked-by: Kevin Tian <kevin.tian@xxxxxxxxx> Acked-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> --- v5: p2m_vm_event_sanity_check is moved into the monitor_op handler v4: Style fixes Only defining struct mov_to_cr and struct debug_event in asm-x86/domain.h Add pause/unpause domain wrapper when enabled a monitor option. --- tools/libxc/Makefile | 1 + tools/libxc/include/xenctrl.h | 19 ++++ tools/libxc/xc_mem_access.c | 9 +- tools/libxc/xc_monitor.c | 118 ++++++++++++++++++++ tools/libxc/xc_private.h | 2 +- tools/libxc/xc_vm_event.c | 7 +- tools/tests/xen-access/xen-access.c | 14 +-- xen/arch/x86/Makefile | 1 + xen/arch/x86/hvm/emulate.c | 3 +- xen/arch/x86/hvm/event.c | 69 ++++++------ xen/arch/x86/hvm/hvm.c | 38 +------ xen/arch/x86/hvm/vmx/vmcs.c | 6 +- xen/arch/x86/hvm/vmx/vmx.c | 2 +- xen/arch/x86/mm/p2m.c | 9 -- xen/arch/x86/monitor.c | 210 ++++++++++++++++++++++++++++++++++++ xen/common/domctl.c | 9 ++ xen/common/vm_event.c | 23 +--- xen/include/asm-arm/monitor.h | 13 +++ xen/include/asm-x86/domain.h | 28 +++++ xen/include/asm-x86/hvm/domain.h | 1 - xen/include/asm-x86/monitor.h | 30 ++++++ xen/include/asm-x86/p2m.h | 8 +- xen/include/public/domctl.h | 50 ++++++++- xen/include/public/hvm/params.h | 15 --- xen/include/public/vm_event.h | 2 +- xen/xsm/flask/hooks.c | 3 + xen/xsm/flask/policy/access_vectors | 2 + 27 files changed, 539 insertions(+), 153 deletions(-) create mode 100644 tools/libxc/xc_monitor.c create mode 100644 xen/arch/x86/monitor.c create mode 100644 xen/include/asm-arm/monitor.h create mode 100644 xen/include/asm-x86/monitor.h diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile index 22ba2a1..8b609cf 100644 --- a/tools/libxc/Makefile +++ b/tools/libxc/Makefile @@ -32,6 +32,7 @@ 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_monitor.c CTRL_SRCS-y += xc_mem_paging.c CTRL_SRCS-y += xc_mem_access.c CTRL_SRCS-y += xc_memshr.c diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h index 790db53..3324132 100644 --- a/tools/libxc/include/xenctrl.h +++ b/tools/libxc/include/xenctrl.h @@ -2308,6 +2308,25 @@ int xc_get_mem_access(xc_interface *xch, domid_t domain_id, uint64_t pfn, xenmem_access_t *access); /*** + * Monitor control operations. + */ +int xc_monitor_mov_to_cr0(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int sync, + unsigned int onchangeonly); +int xc_monitor_mov_to_cr3(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int sync, + unsigned int onchangeonly); +int xc_monitor_mov_to_cr4(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int sync, + unsigned int onchangeonly); +int xc_monitor_mov_to_msr(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int extended_capture); +int xc_monitor_singlestep(xc_interface *xch, domid_t domain_id, + unsigned int op); +int xc_monitor_software_breakpoint(xc_interface *xch, domid_t domain_id, + unsigned int op); + +/*** * Memory sharing operations. * * Unles otherwise noted, these calls return 0 on succes, -1 and errno on diff --git a/tools/libxc/xc_mem_access.c b/tools/libxc/xc_mem_access.c index 0a3f0e6..37e776c 100644 --- a/tools/libxc/xc_mem_access.c +++ b/tools/libxc/xc_mem_access.c @@ -27,14 +27,7 @@ void *xc_mem_access_enable(xc_interface *xch, domid_t domain_id, uint32_t *port) { return xc_vm_event_enable(xch, domain_id, HVM_PARAM_MONITOR_RING_PFN, - port, 0); -} - -void *xc_mem_access_enable_introspection(xc_interface *xch, domid_t domain_id, - uint32_t *port) -{ - return xc_vm_event_enable(xch, domain_id, HVM_PARAM_MONITOR_RING_PFN, - port, 1); + port); } int xc_mem_access_disable(xc_interface *xch, domid_t domain_id) diff --git a/tools/libxc/xc_monitor.c b/tools/libxc/xc_monitor.c new file mode 100644 index 0000000..9e807d1 --- /dev/null +++ b/tools/libxc/xc_monitor.c @@ -0,0 +1,118 @@ +/****************************************************************************** + * + * xc_monitor.c + * + * Interface to VM event monitor + * + * Copyright (c) 2015 Tamas K Lengyel (tamas@xxxxxxxxxxxxx) + * + * 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_monitor_mov_to_cr0(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int sync, + unsigned int onchangeonly) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_monitor_op; + domctl.domain = domain_id; + domctl.u.monitor_op.op = op ? XEN_DOMCTL_MONITOR_OP_ENABLE + : XEN_DOMCTL_MONITOR_OP_DISABLE; + domctl.u.monitor_op.subop = XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR0; + domctl.u.monitor_op.u.mov_to_cr.sync = sync; + domctl.u.monitor_op.u.mov_to_cr.onchangeonly = onchangeonly; + + return do_domctl(xch, &domctl); +} + +int xc_monitor_mov_to_cr3(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int sync, + unsigned int onchangeonly) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_monitor_op; + domctl.domain = domain_id; + domctl.u.monitor_op.op = op ? XEN_DOMCTL_MONITOR_OP_ENABLE + : XEN_DOMCTL_MONITOR_OP_DISABLE; + domctl.u.monitor_op.subop = XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR3; + domctl.u.monitor_op.u.mov_to_cr.sync = sync; + domctl.u.monitor_op.u.mov_to_cr.onchangeonly = onchangeonly; + + return do_domctl(xch, &domctl); +} + +int xc_monitor_mov_to_cr4(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int sync, + unsigned int onchangeonly) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_monitor_op; + domctl.domain = domain_id; + domctl.u.monitor_op.op = op ? XEN_DOMCTL_MONITOR_OP_ENABLE + : XEN_DOMCTL_MONITOR_OP_DISABLE; + domctl.u.monitor_op.subop = XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR4; + domctl.u.monitor_op.u.mov_to_cr.sync = sync; + domctl.u.monitor_op.u.mov_to_cr.onchangeonly = onchangeonly; + + return do_domctl(xch, &domctl); +} + +int xc_monitor_mov_to_msr(xc_interface *xch, domid_t domain_id, + unsigned int op, unsigned int extended_capture) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_monitor_op; + domctl.domain = domain_id; + domctl.u.monitor_op.op = op ? XEN_DOMCTL_MONITOR_OP_ENABLE + : XEN_DOMCTL_MONITOR_OP_DISABLE; + domctl.u.monitor_op.subop = XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_MSR; + domctl.u.monitor_op.u.mov_to_msr.extended_capture = extended_capture; + + return do_domctl(xch, &domctl); +} + +int xc_monitor_software_breakpoint(xc_interface *xch, domid_t domain_id, + unsigned int op) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_monitor_op; + domctl.domain = domain_id; + domctl.u.monitor_op.op = op ? XEN_DOMCTL_MONITOR_OP_ENABLE + : XEN_DOMCTL_MONITOR_OP_DISABLE; + domctl.u.monitor_op.subop = XEN_DOMCTL_MONITOR_SUBOP_SOFTWARE_BREAKPOINT; + + return do_domctl(xch, &domctl); +} + +int xc_monitor_singlestep(xc_interface *xch, domid_t domain_id, + unsigned int op) +{ + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_monitor_op; + domctl.domain = domain_id; + domctl.u.monitor_op.op = op ? XEN_DOMCTL_MONITOR_OP_ENABLE + : XEN_DOMCTL_MONITOR_OP_DISABLE; + domctl.u.monitor_op.subop = XEN_DOMCTL_MONITOR_SUBOP_SINGLESTEP; + + return do_domctl(xch, &domctl); +} diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h index 843540c..9f55309 100644 --- a/tools/libxc/xc_private.h +++ b/tools/libxc/xc_private.h @@ -430,6 +430,6 @@ int xc_vm_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, * 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); + uint32_t *port); #endif /* __XC_PRIVATE_H__ */ diff --git a/tools/libxc/xc_vm_event.c b/tools/libxc/xc_vm_event.c index d458b9a..7277e86 100644 --- a/tools/libxc/xc_vm_event.c +++ b/tools/libxc/xc_vm_event.c @@ -41,7 +41,7 @@ int xc_vm_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, } void *xc_vm_event_enable(xc_interface *xch, domid_t domain_id, int param, - uint32_t *port, int enable_introspection) + uint32_t *port) { void *ring_page = NULL; uint64_t pfn; @@ -104,10 +104,7 @@ void *xc_vm_event_enable(xc_interface *xch, domid_t domain_id, int param, break; case HVM_PARAM_MONITOR_RING_PFN: - if ( enable_introspection ) - op = XEN_VM_EVENT_MONITOR_ENABLE_INTROSPECTION; - else - op = XEN_VM_EVENT_MONITOR_ENABLE; + op = XEN_VM_EVENT_MONITOR_ENABLE; mode = XEN_DOMCTL_VM_EVENT_OP_MONITOR; break; diff --git a/tools/tests/xen-access/xen-access.c b/tools/tests/xen-access/xen-access.c index d951703..d52b175 100644 --- a/tools/tests/xen-access/xen-access.c +++ b/tools/tests/xen-access/xen-access.c @@ -432,13 +432,13 @@ int main(int argc, char *argv[]) } if ( int3 ) - rc = xc_hvm_param_set(xch, domain_id, HVM_PARAM_MEMORY_EVENT_INT3, HVMPME_mode_sync); - else - rc = xc_hvm_param_set(xch, domain_id, HVM_PARAM_MEMORY_EVENT_INT3, HVMPME_mode_disabled); - if ( rc < 0 ) { - ERROR("Error %d setting int3 vm_event\n", rc); - goto exit; + rc = xc_monitor_software_breakpoint(xch, domain_id, 1); + if ( rc < 0 ) + { + ERROR("Error %d setting int3 vm_event\n", rc); + goto exit; + } } /* Wait for access */ @@ -452,7 +452,7 @@ int main(int argc, char *argv[]) rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, ~0ull, 0); rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, 0, xenaccess->domain_info->max_pages); - rc = xc_hvm_param_set(xch, domain_id, HVM_PARAM_MEMORY_EVENT_INT3, HVMPME_mode_disabled); + rc = xc_monitor_software_breakpoint(xch, domain_id, 0); shutting_down = 1; } diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index 86ca5f8..37e547c 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -36,6 +36,7 @@ obj-y += microcode_intel.o # This must come after the vendor specific files. obj-y += microcode.o obj-y += mm.o +obj-y += monitor.o obj-y += mpparse.o obj-y += nmi.o obj-y += numa.o diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index a2b3088..d195f45 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -411,7 +411,8 @@ static int hvmemul_virtual_to_linear( * being triggered for repeated writes to a whole page. */ *reps = min_t(unsigned long, *reps, - unlikely(current->domain->arch.hvm_domain.introspection_enabled) + unlikely(current->domain->arch + .monitor_options.mov_to_msr.extended_capture) ? 1 : 4096); reg = hvmemul_get_seg_reg(seg, hvmemul_ctxt); diff --git a/xen/arch/x86/hvm/event.c b/xen/arch/x86/hvm/event.c index 3fdd28e..0b59f7a 100644 --- a/xen/arch/x86/hvm/event.c +++ b/xen/arch/x86/hvm/event.c @@ -55,15 +55,12 @@ static void hvm_event_fill_regs(vm_event_request_t *req) req->regs.x86.cr4 = curr->arch.hvm_vcpu.guest_cr[4]; } -static int hvm_event_traps(uint64_t parameters, vm_event_request_t *req) +static int hvm_event_traps(uint8_t sync, vm_event_request_t *req) { int rc; struct vcpu *curr = current; struct domain *currd = curr->domain; - if ( !(parameters & HVMPME_MODE_MASK) ) - return 0; - rc = vm_event_claim_slot(currd, &currd->vm_event->monitor); switch ( rc ) { @@ -79,7 +76,7 @@ static int hvm_event_traps(uint64_t parameters, vm_event_request_t *req) return rc; }; - if ( (parameters & HVMPME_MODE_MASK) == HVMPME_mode_sync ) + if ( sync ) { req->flags |= VM_EVENT_FLAG_VCPU_PAUSED; vm_event_vcpu_pause(curr); @@ -92,7 +89,7 @@ static int hvm_event_traps(uint64_t parameters, vm_event_request_t *req) } static void hvm_event_cr(uint32_t reason, unsigned long value, - unsigned long old) + unsigned long old, struct mov_to_cr *option) { vm_event_request_t req = { .reason = reason, @@ -100,43 +97,38 @@ static void hvm_event_cr(uint32_t reason, unsigned long value, .u.mov_to_cr.new_value = value, .u.mov_to_cr.old_value = old }; - uint64_t parameters = 0; - - switch(reason) - { - case VM_EVENT_REASON_MOV_TO_CR0: - parameters = current->domain->arch.hvm_domain - .params[HVM_PARAM_MEMORY_EVENT_CR0]; - break; - case VM_EVENT_REASON_MOV_TO_CR3: - parameters = current->domain->arch.hvm_domain - .params[HVM_PARAM_MEMORY_EVENT_CR3]; - break; - case VM_EVENT_REASON_MOV_TO_CR4: - parameters = current->domain->arch.hvm_domain - .params[HVM_PARAM_MEMORY_EVENT_CR4]; - break; - }; - if ( (parameters & HVMPME_onchangeonly) && (value == old) ) + if ( option->onchangeonly && value == old ) return; - hvm_event_traps(parameters, &req); + hvm_event_traps(option->sync, &req); } void hvm_event_cr0(unsigned long value, unsigned long old) { - hvm_event_cr(VM_EVENT_REASON_MOV_TO_CR0, value, old); + struct domain *currd = current->domain; + + if ( currd->arch.monitor_options.mov_to_cr0.enabled ) + hvm_event_cr(VM_EVENT_REASON_MOV_TO_CR0, value, old, + &currd->arch.monitor_options.mov_to_cr0); } void hvm_event_cr3(unsigned long value, unsigned long old) { - hvm_event_cr(VM_EVENT_REASON_MOV_TO_CR3, value, old); + struct domain *currd = current->domain; + + if ( currd->arch.monitor_options.mov_to_cr3.enabled ) + hvm_event_cr(VM_EVENT_REASON_MOV_TO_CR3, value, old, + &currd->arch.monitor_options.mov_to_cr3); } void hvm_event_cr4(unsigned long value, unsigned long old) { - hvm_event_cr(VM_EVENT_REASON_MOV_TO_CR4, value, old); + struct domain *currd = current->domain; + + if ( currd->arch.monitor_options.mov_to_cr4.enabled ) + hvm_event_cr(VM_EVENT_REASON_MOV_TO_CR4, value, old, + &currd->arch.monitor_options.mov_to_cr4); } void hvm_event_msr(unsigned int msr, uint64_t value) @@ -148,14 +140,14 @@ void hvm_event_msr(unsigned int msr, uint64_t value) .u.mov_to_msr.msr = msr, .u.mov_to_msr.value = value, }; - uint64_t params = current->domain->arch.hvm_domain - .params[HVM_PARAM_MEMORY_EVENT_MSR]; - hvm_event_traps(params, &req); + if ( curr->domain->arch.monitor_options.mov_to_msr.enabled ) + hvm_event_traps(1, &req); } int hvm_event_int3(unsigned long gla) { + int rc = 0; uint32_t pfec = PFEC_page_present; struct vcpu *curr = current; vm_event_request_t req = { @@ -163,14 +155,16 @@ int hvm_event_int3(unsigned long gla) .vcpu_id = curr->vcpu_id, .u.software_breakpoint.gfn = paging_gva_to_gfn(curr, gla, &pfec) }; - uint64_t params = curr->domain->arch.hvm_domain - .params[HVM_PARAM_MEMORY_EVENT_INT3]; - return hvm_event_traps(params, &req); + if ( curr->domain->arch.monitor_options.software_breakpoint.enabled ) + rc = hvm_event_traps(1, &req); + + return rc; } int hvm_event_single_step(unsigned long gla) { + int rc = 0; uint32_t pfec = PFEC_page_present; struct vcpu *curr = current; vm_event_request_t req = { @@ -178,10 +172,11 @@ int hvm_event_single_step(unsigned long gla) .vcpu_id = curr->vcpu_id, .u.singlestep.gfn = paging_gva_to_gfn(curr, gla, &pfec) }; - uint64_t params = curr->domain->arch.hvm_domain - .params[HVM_PARAM_MEMORY_EVENT_SINGLE_STEP]; - return hvm_event_traps(params, &req); + if ( curr->domain->arch.monitor_options.singlestep.enabled ) + rc = hvm_event_traps(1, &req); + + return rc; } /* diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 2c4d0ff..4a05fb2 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -5785,23 +5785,6 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE_PARAM(void) arg) case HVM_PARAM_ACPI_IOPORTS_LOCATION: rc = pmtimer_change_ioport(d, a.value); break; - case HVM_PARAM_MEMORY_EVENT_CR0: - case HVM_PARAM_MEMORY_EVENT_CR3: - case HVM_PARAM_MEMORY_EVENT_CR4: - if ( d == current->domain ) - rc = -EPERM; - break; - case HVM_PARAM_MEMORY_EVENT_INT3: - case HVM_PARAM_MEMORY_EVENT_SINGLE_STEP: - case HVM_PARAM_MEMORY_EVENT_MSR: - if ( d == current->domain ) - { - rc = -EPERM; - break; - } - if ( a.value & HVMPME_onchangeonly ) - rc = -EINVAL; - break; case HVM_PARAM_NESTEDHVM: rc = xsm_hvm_param_nested(XSM_PRIV, d); if ( rc ) @@ -5860,29 +5843,10 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE_PARAM(void) arg) } } - if ( rc == 0 ) + if ( rc == 0 ) { d->arch.hvm_domain.params[a.index] = a.value; - - switch( a.index ) - { - case HVM_PARAM_MEMORY_EVENT_INT3: - case HVM_PARAM_MEMORY_EVENT_SINGLE_STEP: - { - domain_pause(d); - domain_unpause(d); /* Causes guest to latch new status */ - break; - } - case HVM_PARAM_MEMORY_EVENT_CR3: - { - for_each_vcpu ( d, v ) - hvm_funcs.update_guest_cr(v, 0); /* Latches new CR3 mask through CR0 code */ - break; - } - } - } - } else { diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c index 63007a9..17b2ab0 100644 --- a/xen/arch/x86/hvm/vmx/vmcs.c +++ b/xen/arch/x86/hvm/vmx/vmcs.c @@ -714,7 +714,7 @@ void vmx_disable_intercept_for_msr(struct vcpu *v, u32 msr, int type) if ( msr_bitmap == NULL ) return; - if ( unlikely(d->arch.hvm_domain.introspection_enabled) && + if ( unlikely(d->arch.monitor_options.mov_to_msr.extended_capture) && vm_event_check_ring(&d->vm_event->monitor) ) { unsigned int i; @@ -1373,8 +1373,8 @@ void vmx_do_resume(struct vcpu *v) } debug_state = v->domain->debugger_attached - || v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_INT3] - || v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_SINGLE_STEP]; + || v->domain->arch.monitor_options.software_breakpoint.enabled + || v->domain->arch.monitor_options.singlestep.enabled; if ( unlikely(v->arch.hvm_vcpu.debug_state_latch != debug_state) ) { diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 3f2a18f..fcd25df 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -1232,7 +1232,7 @@ static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr) v->arch.hvm_vmx.exec_control |= cr3_ctls; /* Trap CR3 updates if CR3 memory events are enabled. */ - if ( v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_CR3] ) + if ( v->domain->arch.monitor_options.mov_to_cr3.enabled ) v->arch.hvm_vmx.exec_control |= CPU_BASED_CR3_LOAD_EXITING; vmx_update_cpu_exec_control(v); diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index 1974bfb..5ce852e 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -1453,15 +1453,6 @@ void p2m_vm_event_emulate_check(struct vcpu *v, const vm_event_response_t *rsp) } } -void p2m_setup_introspection(struct domain *d) -{ - if ( hvm_funcs.enable_msr_exit_interception ) - { - d->arch.hvm_domain.introspection_enabled = 1; - hvm_funcs.enable_msr_exit_interception(d); - } -} - bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla, struct npfec npfec, vm_event_request_t **req_ptr) diff --git a/xen/arch/x86/monitor.c b/xen/arch/x86/monitor.c new file mode 100644 index 0000000..be4bb20 --- /dev/null +++ b/xen/arch/x86/monitor.c @@ -0,0 +1,210 @@ +/* + * arch/x86/monitor.c + * + * Architecture-specific monitor_op domctl handler. + * + * Copyright (c) 2015 Tamas K Lengyel (tamas@xxxxxxxxxxxxx) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * 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 021110-1307, USA. + */ + +#include <xen/config.h> +#include <xen/sched.h> +#include <xen/mm.h> +#include <asm/domain.h> + +#define DISABLE_OPTION(option) \ + do { \ + if ( !option->enabled ) \ + return -EFAULT; \ + domain_pause(d); \ + option->enabled = 0; \ + domain_unpause(d); \ + } while (0) + +#define ENABLE_OPTION(option) \ + do { \ + domain_pause(d); \ + option->enabled = 1; \ + domain_unpause(d); \ + } while (0) + +int monitor_domctl(struct xen_domctl_monitor_op *domctl, struct domain *d) +{ + /* + * At the moment only Intel HVM domains are supported. However, event + * delivery could be extended to AMD and PV domains. See comments below. + */ + if ( !is_hvm_domain(d) || !cpu_has_vmx) + return -ENOSYS; + + if ( domctl->op != XEN_DOMCTL_MONITOR_OP_ENABLE && + domctl->op != XEN_DOMCTL_MONITOR_OP_DISABLE ) + return -EFAULT; + + switch ( domctl->subop ) + { + case XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR0: + { + /* Note: could be supported on PV domains. */ + struct mov_to_cr *options = &d->arch.monitor_options.mov_to_cr0; + + if ( domctl->op == XEN_DOMCTL_MONITOR_OP_ENABLE ) + { + if ( options->enabled ) + return -EBUSY; + + options->sync = domctl->u.mov_to_cr.sync; + options->onchangeonly = domctl->u.mov_to_cr.onchangeonly; + ENABLE_OPTION(options); + } + else + { + DISABLE_OPTION(options); + } + break; + } + + case XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR3: + { + /* Note: could be supported on PV domains. */ + struct vcpu *v; + struct mov_to_cr *options = &d->arch.monitor_options.mov_to_cr3; + + if ( domctl->op == XEN_DOMCTL_MONITOR_OP_ENABLE ) + { + if ( options->enabled ) + return -EBUSY; + + options->sync = domctl->u.mov_to_cr.sync; + options->onchangeonly = domctl->u.mov_to_cr.onchangeonly; + ENABLE_OPTION(options); + } + else + { + DISABLE_OPTION(options); + } + + /* Latches new CR3 mask through CR0 code */ + for_each_vcpu ( d, v ) + hvm_funcs.update_guest_cr(v, 0); + break; + } + + case XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR4: + { + /* Note: could be supported on PV domains. */ + struct mov_to_cr *options = &d->arch.monitor_options.mov_to_cr4; + + if ( domctl->op == XEN_DOMCTL_MONITOR_OP_ENABLE ) + { + if ( options->enabled ) + return -EBUSY; + + options->sync = domctl->u.mov_to_cr.sync; + options->onchangeonly = domctl->u.mov_to_cr.onchangeonly; + ENABLE_OPTION(options); + } + else + { + DISABLE_OPTION(options); + } + break; + } + case XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_MSR: + { + struct mov_to_msr *options = &d->arch.monitor_options.mov_to_msr; + + if ( domctl->op == XEN_DOMCTL_MONITOR_OP_ENABLE ) + { + if ( options->enabled ) + return -EBUSY; + + if ( domctl->u.mov_to_msr.extended_capture ) + { + if ( hvm_funcs.enable_msr_exit_interception ) + { + options->extended_capture = 1; + hvm_funcs.enable_msr_exit_interception(d); + } + else + { + return -ENOSYS; + } + } + + ENABLE_OPTION(options); + } + else + { + DISABLE_OPTION(options); + } + + break; + } + case XEN_DOMCTL_MONITOR_SUBOP_SINGLESTEP: + { + struct debug_event *options = &d->arch.monitor_options.singlestep; + + if ( domctl->op == XEN_DOMCTL_MONITOR_OP_ENABLE ) + { + if ( options->enabled ) + return -EBUSY; + + ENABLE_OPTION(options); + } + else + { + DISABLE_OPTION(options); + } + + break; + } + case XEN_DOMCTL_MONITOR_SUBOP_SOFTWARE_BREAKPOINT: + { + struct debug_event *options = + &d->arch.monitor_options.software_breakpoint; + + if ( domctl->op == XEN_DOMCTL_MONITOR_OP_ENABLE ) + { + if ( options->enabled ) + return -EBUSY; + + ENABLE_OPTION(options); + } + else + { + DISABLE_OPTION(options); + } + + break; + } + + default: + return -ENOSYS; + + }; + + return 0; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/domctl.c b/xen/common/domctl.c index 85afd68..d06f4b6 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -29,6 +29,7 @@ #include <asm/irq.h> #include <asm/page.h> #include <asm/p2m.h> +#include <asm/monitor.h> #include <public/domctl.h> #include <xsm/xsm.h> @@ -1178,6 +1179,14 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) } break; + case XEN_DOMCTL_monitor_op: + ret = -EPERM; + if ( current->domain == d ) + break; + + ret = monitor_domctl(&op->u.monitor_op, d); + break; + default: ret = arch_do_domctl(op, d, u_domctl); break; diff --git a/xen/common/vm_event.c b/xen/common/vm_event.c index 195739e..f988291 100644 --- a/xen/common/vm_event.c +++ b/xen/common/vm_event.c @@ -621,32 +621,17 @@ int vm_event_domctl(struct domain *d, xen_domctl_vm_event_op_t *vec, switch( vec->op ) { case XEN_VM_EVENT_MONITOR_ENABLE: - case XEN_VM_EVENT_MONITOR_ENABLE_INTROSPECTION: - { - rc = -ENODEV; - if ( !p2m_vm_event_sanity_check(d) ) - break; - rc = vm_event_enable(d, vec, ved, _VPF_mem_access, - HVM_PARAM_MONITOR_RING_PFN, - mem_access_notification); - - if ( vec->op == XEN_VM_EVENT_MONITOR_ENABLE_INTROSPECTION - && !rc ) - p2m_setup_introspection(d); - - } - break; + HVM_PARAM_MONITOR_RING_PFN, + mem_access_notification); + break; case XEN_VM_EVENT_MONITOR_DISABLE: - { if ( ved->ring_page ) { rc = vm_event_disable(d, ved); - d->arch.hvm_domain.introspection_enabled = 0; } - } - break; + break; default: rc = -ENOSYS; diff --git a/xen/include/asm-arm/monitor.h b/xen/include/asm-arm/monitor.h new file mode 100644 index 0000000..ef8f38a --- /dev/null +++ b/xen/include/asm-arm/monitor.h @@ -0,0 +1,13 @@ +#ifndef __ASM_ARM_MONITOR_H__ +#define __ASM_ARM_MONITOR_H__ + +#include <xen/config.h> +#include <public/domctl.h> + +static inline +int monitor_domctl(struct xen_domctl_monitor_op *op, struct domain *d) +{ + return -ENOSYS; +} + +#endif /* __ASM_X86_MONITOR_H__ */ diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h index 85f05a5..ae56cc4 100644 --- a/xen/include/asm-x86/domain.h +++ b/xen/include/asm-x86/domain.h @@ -241,6 +241,24 @@ struct time_scale { u32 mul_frac; }; +/************************************************/ +/* monitor event options */ +/************************************************/ +struct mov_to_cr { + uint8_t enabled; + uint8_t sync; + uint8_t onchangeonly; +}; + +struct mov_to_msr { + uint8_t enabled; + uint8_t extended_capture; +}; + +struct debug_event { + uint8_t enabled; +}; + struct pv_domain { l1_pgentry_t **gdt_ldt_l1tab; @@ -335,6 +353,16 @@ struct arch_domain unsigned long pirq_eoi_map_mfn; unsigned int psr_rmid; /* RMID assigned to the domain for CMT */ + + /* Monitor options */ + struct { + struct mov_to_cr mov_to_cr0; + struct mov_to_cr mov_to_cr3; + struct mov_to_cr mov_to_cr4; + struct mov_to_msr mov_to_msr; + struct debug_event singlestep; + struct debug_event software_breakpoint; + } monitor_options; } __cacheline_aligned; #define has_arch_pdevs(d) (!list_empty(&(d)->arch.pdev_list)) diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h index 2757c7f..0f8b19a 100644 --- a/xen/include/asm-x86/hvm/domain.h +++ b/xen/include/asm-x86/hvm/domain.h @@ -134,7 +134,6 @@ struct hvm_domain { bool_t mem_sharing_enabled; bool_t qemu_mapcache_invalidate; bool_t is_s3_suspended; - bool_t introspection_enabled; /* * TSC value that VCPUs use to calculate their tsc_offset value. diff --git a/xen/include/asm-x86/monitor.h b/xen/include/asm-x86/monitor.h new file mode 100644 index 0000000..d1fa859 --- /dev/null +++ b/xen/include/asm-x86/monitor.h @@ -0,0 +1,30 @@ +/* + * include/asm-x86/monitor.h + * + * Architecture-specific monitor_op domctl handler. + * + * Copyright (c) 2015 Tamas K Lengyel (tamas@xxxxxxxxxxxxx) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * 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 021110-1307, USA. + */ + +#ifndef __ASM_X86_MONITOR_H__ +#define __ASM_X86_MONITOR_H__ + +#include <public/domctl.h> + +int monitor_domctl(struct xen_domctl_monitor_op *op, struct domain *d); + +#endif /* __ASM_X86_MONITOR_H__ */ diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h index 6266f9a..bd84e60 100644 --- a/xen/include/asm-x86/p2m.h +++ b/xen/include/asm-x86/p2m.h @@ -615,16 +615,10 @@ void p2m_vm_event_emulate_check(struct vcpu *v, /* Enable arch specific introspection options (such as MSR interception). */ void p2m_setup_introspection(struct domain *d); -/* Sanity check for vm_event hardware support */ -static inline bool_t p2m_vm_event_sanity_check(struct domain *d) -{ - return hap_enabled(d) && cpu_has_vmx; -} - /* Sanity check for mem_access hardware support */ static inline bool_t p2m_mem_access_sanity_check(struct domain *d) { - return is_hvm_domain(d); + return is_hvm_domain(d) && cpu_has_vmx && hap_enabled(d); } /* diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index 9d4972a..0242914 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -804,7 +804,6 @@ struct xen_domctl_gdbsx_domstatus { #define XEN_VM_EVENT_MONITOR_ENABLE 0 #define XEN_VM_EVENT_MONITOR_DISABLE 1 -#define XEN_VM_EVENT_MONITOR_ENABLE_INTROSPECTION 2 /* * Sharing ENOMEM helper. @@ -1002,6 +1001,53 @@ struct xen_domctl_psr_cmt_op { typedef struct xen_domctl_psr_cmt_op xen_domctl_psr_cmt_op_t; DEFINE_XEN_GUEST_HANDLE(xen_domctl_psr_cmt_op_t); +/* XEN_DOMCTL_MONITOR_* + * + * Enable/disable monitoring various VM events. + * This domctl configures what events will be reported to helper apps + * via the ring buffer "MONITOR". The ring has to be first enabled + * with the domctl XEN_DOMCTL_VM_EVENT_OP_MONITOR. + * + * NOTICE: mem_access events are also delivered via the "MONITOR" ring buffer; + * however, enabling/disabling those events is performed with the use of + * memory_op hypercalls! + */ +#define XEN_DOMCTL_MONITOR_OP_ENABLE 0 +#define XEN_DOMCTL_MONITOR_OP_DISABLE 1 + +#define XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR0 0 +#define XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR3 1 +#define XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_CR4 2 +#define XEN_DOMCTL_MONITOR_SUBOP_MOV_TO_MSR 3 +#define XEN_DOMCTL_MONITOR_SUBOP_SINGLESTEP 4 +#define XEN_DOMCTL_MONITOR_SUBOP_SOFTWARE_BREAKPOINT 5 + +struct xen_domctl_monitor_op { + uint32_t op; /* XEN_DOMCTL_MONITOR_OP_* */ + uint32_t subop; /* XEN_DOMCTL_MONITOR_SUBOP_* */ + + /* + * Further options when issuing XEN_DOMCTL_MONITOR_OP_ENABLE. + */ + union { + struct { + /* Pause vCPU until response */ + uint8_t sync; + /* Send event only on a change of value */ + uint8_t onchangeonly; + uint8_t _pad[6]; + } mov_to_cr; + + struct { + /* Enable the capture of an extended set of MSRs */ + uint8_t extended_capture; + uint8_t _pad[7]; + } mov_to_msr; + } u; +}; +typedef struct xen_domctl__op xen_domctl_monitor_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_domctl_monitor_op_t); + struct xen_domctl { uint32_t cmd; #define XEN_DOMCTL_createdomain 1 @@ -1077,6 +1123,7 @@ struct xen_domctl { #define XEN_DOMCTL_setvnumainfo 74 #define XEN_DOMCTL_psr_cmt_op 75 #define XEN_DOMCTL_arm_configure_domain 76 +#define XEN_DOMCTL_monitor_op 77 #define XEN_DOMCTL_gdbsx_guestmemio 1000 #define XEN_DOMCTL_gdbsx_pausevcpu 1001 #define XEN_DOMCTL_gdbsx_unpausevcpu 1002 @@ -1142,6 +1189,7 @@ struct xen_domctl { struct xen_domctl_gdbsx_domstatus gdbsx_domstatus; struct xen_domctl_vnuma vnuma; struct xen_domctl_psr_cmt_op psr_cmt_op; + struct xen_domctl_monitor_op monitor_op; uint8_t pad[128]; } u; }; diff --git a/xen/include/public/hvm/params.h b/xen/include/public/hvm/params.h index 6efcc0b..5de6a4b 100644 --- a/xen/include/public/hvm/params.h +++ b/xen/include/public/hvm/params.h @@ -162,21 +162,6 @@ */ #define HVM_PARAM_ACPI_IOPORTS_LOCATION 19 -/* Enable blocking memory events, async or sync (pause vcpu until response) - * onchangeonly indicates messages only on a change of value */ -#define HVM_PARAM_MEMORY_EVENT_CR0 20 -#define HVM_PARAM_MEMORY_EVENT_CR3 21 -#define HVM_PARAM_MEMORY_EVENT_CR4 22 -#define HVM_PARAM_MEMORY_EVENT_INT3 23 -#define HVM_PARAM_MEMORY_EVENT_SINGLE_STEP 25 -#define HVM_PARAM_MEMORY_EVENT_MSR 30 - -#define HVMPME_MODE_MASK (3 << 0) -#define HVMPME_mode_disabled 0 -#define HVMPME_mode_async 1 -#define HVMPME_mode_sync 2 -#define HVMPME_onchangeonly (1 << 2) - /* Boolean: Enable nestedhvm (hvm only) */ #define HVM_PARAM_NESTEDHVM 24 diff --git a/xen/include/public/vm_event.h b/xen/include/public/vm_event.h index b619bff..30e9a32 100644 --- a/xen/include/public/vm_event.h +++ b/xen/include/public/vm_event.h @@ -67,7 +67,7 @@ #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 */ +/* An MSR was updated. */ #define VM_EVENT_REASON_MOV_TO_MSR 7 /* Debug operation executed (e.g. int3) */ #define VM_EVENT_REASON_SOFTWARE_BREAKPOINT 8 diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c index 475ef6c..2179cc3 100644 --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -691,6 +691,9 @@ static int flask_domctl(struct domain *d, int cmd) case XEN_DOMCTL_set_access_required: return current_has_perm(d, SECCLASS_HVM, HVM__VM_EVENT); + case XEN_DOMCTL_monitor_op: + return current_has_perm(d, SECCLASS_HVM, HVM__VM_EVENT); + case XEN_DOMCTL_debug_op: case XEN_DOMCTL_gdbsx_guestmemio: case XEN_DOMCTL_gdbsx_pausevcpu: diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors index 23b47bf..ebe690a 100644 --- a/xen/xsm/flask/policy/access_vectors +++ b/xen/xsm/flask/policy/access_vectors @@ -250,6 +250,8 @@ class hvm # HVMOP_inject_trap hvmctl # XEN_DOMCTL_set_access_required +# XEN_DOMCTL_monitor_op +# XEN_DOMCTL_vm_event_op vm_event # XEN_DOMCTL_mem_sharing_op and XENMEM_sharing_op_{share,add_physmap} with: # source = the domain making the hypercall -- 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 |