[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v2 4/7] x86/vmx: add do_vmtrace_op
Provide an interface for privileged domains to manage external IPT monitoring. Guest IPT state will be preserved across vmentry/vmexit using ipt_state structure. Signed-off-by: Michal Leszczynski <michal.leszczynski@xxxxxxx> --- xen/arch/x86/hvm/hvm.c | 167 +++++++++++++++++++++++++++++ xen/arch/x86/hvm/vmx/vmx.c | 24 +++++ xen/arch/x86/mm.c | 37 +++++++ xen/common/domain.c | 1 + xen/include/asm-x86/hvm/vmx/vmcs.h | 16 +++ xen/include/public/hvm/hvm_op.h | 23 ++++ xen/include/public/hvm/params.h | 5 +- xen/include/public/memory.h | 1 + xen/include/xen/sched.h | 3 + 9 files changed, 276 insertions(+), 1 deletion(-) diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 5bb47583b3..145ad053d2 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -1612,6 +1612,24 @@ int hvm_vcpu_initialise(struct vcpu *v) return rc; } +void hvm_vmtrace_destroy(struct vcpu *v) +{ + unsigned int i; + struct page_info *pg; + struct ipt_state *ipt = v->arch.hvm.vmx.ipt_state; + mfn_t buf_mfn = ipt->output_base >> PAGE_SHIFT; + size_t buf_size = ipt->output_mask.size + 1; + + xfree(ipt); + v->arch.hvm.vmx.ipt_state = NULL; + + for ( i = 0; i < (buf_size >> PAGE_SHIFT); i++ ) + { + pg = mfn_to_page(_mfn(mfn_add(buf_mfn, i))); + free_domheap_page(pg); + } +} + void hvm_vcpu_destroy(struct vcpu *v) { viridian_vcpu_deinit(v); @@ -1631,6 +1649,8 @@ void hvm_vcpu_destroy(struct vcpu *v) vlapic_destroy(v); hvm_vcpu_cacheattr_destroy(v); + + hvm_vmtrace_destroy(v); } void hvm_vcpu_down(struct vcpu *v) @@ -4066,6 +4086,51 @@ static int hvmop_set_evtchn_upcall_vector( return 0; } +static int hvm_set_vmtrace_pt_size(struct domain *d, uint64_t value) +{ + void *buf; + unsigned int buf_order; + struct page_info *pg; + struct ipt_state *ipt; + struct vcpu *v; + + if ( value < PAGE_SIZE || + value > GB(4) || + ( value & (value - 1) ) ) { + /* we don't accept trace buffer size smaller than single page + * and the upper bound is defined as 4GB in the specification */ + return -EINVAL; + } + + for_each_vcpu ( d, v ) + { + buf_order = get_order_from_bytes(value); + pg = alloc_domheap_pages(d, buf_order, MEMF_no_refcount); + + if ( !pg ) + return -EFAULT; + + buf = page_to_virt(pg); + + if ( vmx_add_host_load_msr(v, MSR_RTIT_CTL, 0) ) + return -EFAULT; + + ipt = xmalloc(struct ipt_state); + + if ( !ipt ) + return -EFAULT; + + ipt->output_base = virt_to_mfn(buf) << PAGE_SHIFT; + ipt->output_mask.raw = value - 1; + ipt->status = 0; + ipt->active = 0; + + v->arch.hvm.vmx.ipt_state = ipt; + } + + return 0; +} + static int hvm_allow_set_param(struct domain *d, uint32_t index, uint64_t new_value) @@ -4127,6 +4192,7 @@ static int hvm_allow_set_param(struct domain *d, case HVM_PARAM_NR_IOREQ_SERVER_PAGES: case HVM_PARAM_ALTP2M: case HVM_PARAM_MCA_CAP: + case HVM_PARAM_VMTRACE_PT_SIZE: if ( value != 0 && new_value != value ) rc = -EEXIST; break; @@ -4328,6 +4394,9 @@ static int hvm_set_param(struct domain *d, uint32_t index, uint64_t value) case HVM_PARAM_MCA_CAP: rc = vmce_enable_mca_cap(d, value); break; + case HVM_PARAM_VMTRACE_PT_SIZE: + rc = hvm_set_vmtrace_pt_size(d, value); + break; } if ( !rc ) @@ -4949,6 +5018,100 @@ static int compat_altp2m_op( return rc; } +static int do_vmtrace_op(XEN_GUEST_HANDLE_PARAM(void) arg) +{ + struct xen_hvm_vmtrace_op a; + struct domain *d; + int rc; + struct vcpu *v; + struct ipt_state *ipt; + + if ( !hvm_pt_supported() ) + return -EOPNOTSUPP; + + if ( copy_from_guest(&a, arg, 1) ) + return -EFAULT; + + if ( a.version != HVMOP_VMTRACE_INTERFACE_VERSION ) + return -EINVAL; + + d = rcu_lock_domain_by_any_id(a.domain); + spin_lock(&d->vmtrace_lock); + + if ( d == NULL ) + return -ESRCH; + + if ( !is_hvm_domain(d) ) + { + rc = -EOPNOTSUPP; + goto out; + } + + domain_pause(d); + + if ( a.vcpu >= d->max_vcpus ) + { + rc = -EINVAL; + goto out; + } + + v = d->vcpu[a.vcpu]; + ipt = v->arch.hvm.vmx.ipt_state; + + if ( !ipt ) + { + /* + * PT must be first initialized upon domain creation. + */ + rc = -EINVAL; + goto out; + } + + switch ( a.cmd ) + { + case HVMOP_vmtrace_ipt_enable: + if ( vmx_add_guest_msr(v, MSR_RTIT_CTL, + RTIT_CTL_TRACEEN | RTIT_CTL_OS | + RTIT_CTL_USR | RTIT_CTL_BRANCH_EN) ) + { + rc = -EFAULT; + goto out; + } + + ipt->active = 1; + break; + case HVMOP_vmtrace_ipt_disable: + if ( vmx_add_guest_msr(v, MSR_RTIT_CTL, 0) ) + { + rc = -EFAULT; + goto out; + } + + ipt->active = 0; + break; + case HVMOP_vmtrace_ipt_get_offset: + a.offset = ipt->output_mask.offset; + break; + default: + rc = -EOPNOTSUPP; + goto out; + } + + rc = -EFAULT; + if ( __copy_to_guest(arg, &a, 1) ) + goto out; + rc = 0; + + out: + domain_unpause(d); + spin_unlock(&d->vmtrace_lock); + rcu_unlock_domain(d); + + return rc; +} + +DEFINE_XEN_GUEST_HANDLE(compat_hvm_vmtrace_op_t); + static int hvmop_get_mem_type( XEN_GUEST_HANDLE_PARAM(xen_hvm_get_mem_type_t) arg) { @@ -5101,6 +5264,10 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE_PARAM(void) arg) rc = current->hcall_compat ? compat_altp2m_op(arg) : do_altp2m_op(arg); break; + case HVMOP_vmtrace: + rc = do_vmtrace_op(arg); + break; + default: { gdprintk(XENLOG_DEBUG, "Bad HVM op %ld.\n", op); diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index ab19d9424e..51f0046483 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -508,11 +508,25 @@ static void vmx_restore_host_msrs(void) static void vmx_save_guest_msrs(struct vcpu *v) { + uint64_t rtit_ctl; + /* * We cannot cache SHADOW_GS_BASE while the VCPU runs, as it can * be updated at any time via SWAPGS, which we cannot trap. */ v->arch.hvm.vmx.shadow_gs = rdgsshadow(); + + if ( unlikely(v->arch.hvm.vmx.ipt_state && v->arch.hvm.vmx.ipt_state->active) ) + { + smp_rmb(); + rdmsrl(MSR_RTIT_CTL, rtit_ctl); + + if ( rtit_ctl & RTIT_CTL_TRACEEN ) + BUG(); + + rdmsrl(MSR_RTIT_STATUS, v->arch.hvm.vmx.ipt_state->status); + rdmsrl(MSR_RTIT_OUTPUT_MASK, v->arch.hvm.vmx.ipt_state->output_mask.raw); + } } static void vmx_restore_guest_msrs(struct vcpu *v) @@ -524,6 +538,16 @@ static void vmx_restore_guest_msrs(struct vcpu *v) if ( cpu_has_msr_tsc_aux ) wrmsr_tsc_aux(v->arch.msrs->tsc_aux); + + if ( unlikely(v->arch.hvm.vmx.ipt_state && v->arch.hvm.vmx.ipt_state->active) ) + { + wrmsrl(MSR_RTIT_OUTPUT_BASE, + v->arch.hvm.vmx.ipt_state->output_base); + wrmsrl(MSR_RTIT_OUTPUT_MASK, + v->arch.hvm.vmx.ipt_state->output_mask.raw); + wrmsrl(MSR_RTIT_STATUS, + v->arch.hvm.vmx.ipt_state->status); + } } void vmx_update_cpu_exec_control(struct vcpu *v) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index e376fc7e8f..e4658dc27f 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -4624,6 +4624,43 @@ int arch_acquire_resource(struct domain *d, unsigned int type, } break; } + + case XENMEM_resource_vmtrace_buf: + { + mfn_t mfn; + unsigned int i; + struct ipt_state *ipt; + + if ( id >= d->max_vcpus ) + { + rc = -EINVAL; + break; + } + + ipt = d->vcpu[id]->arch.hvm.vmx.ipt_state; + + if ( !ipt ) + { + rc = -EINVAL; + break; + } + + mfn = mfn_x(ipt->output_base >> PAGE_SHIFT); + + if (nr_frames > ( ( ipt->output_mask.size + 1 ) >> PAGE_SHIFT )) + { + rc = -EINVAL; + break; + } + + rc = 0; + for ( i = 0; i < nr_frames; i++ ) + { + mfn_list[i] = mfn_add(mfn, i); + } + + break; + } #endif default: diff --git a/xen/common/domain.c b/xen/common/domain.c index 7cc9526139..6f6458cd5b 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -414,6 +414,7 @@ struct domain *domain_create(domid_t domid, d->shutdown_code = SHUTDOWN_CODE_INVALID; spin_lock_init(&d->pbuf_lock); + spin_lock_init(&d->vmtrace_lock); rwlock_init(&d->vnuma_rwlock); diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h index 4c81093aba..9fc4653777 100644 --- a/xen/include/asm-x86/hvm/vmx/vmcs.h +++ b/xen/include/asm-x86/hvm/vmx/vmcs.h @@ -104,6 +104,19 @@ struct pi_blocking_vcpu { spinlock_t *lock; }; +struct ipt_state { + uint64_t active; + uint64_t status; + uint64_t output_base; + union { + uint64_t raw; + struct { + uint32_t size; + uint32_t offset; + }; + } output_mask; +}; + struct vmx_vcpu { /* Physical address of VMCS. */ paddr_t vmcs_pa; @@ -186,6 +199,9 @@ struct vmx_vcpu { * pCPU and wakeup the related vCPU. */ struct pi_blocking_vcpu pi_blocking; + + /* State of Intel Processor Trace feature */ + struct ipt_state *ipt_state; }; int vmx_create_vmcs(struct vcpu *v); diff --git a/xen/include/public/hvm/hvm_op.h b/xen/include/public/hvm/hvm_op.h index 870ec52060..8cd0b6ea49 100644 --- a/xen/include/public/hvm/hvm_op.h +++ b/xen/include/public/hvm/hvm_op.h @@ -382,6 +382,29 @@ struct xen_hvm_altp2m_op { typedef struct xen_hvm_altp2m_op xen_hvm_altp2m_op_t; DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_op_t); +/* HVMOP_vmtrace: Perform VM tracing related operation */ +#define HVMOP_vmtrace 26 + +#define HVMOP_VMTRACE_INTERFACE_VERSION 0x00000001 + +struct xen_hvm_vmtrace_op { + /* IN variable */ + uint32_t version; /* HVMOP_VMTRACE_INTERFACE_VERSION */ + uint32_t cmd; +/* Enable/disable external vmtrace for given domain */ +#define HVMOP_vmtrace_ipt_enable 1 +#define HVMOP_vmtrace_ipt_disable 2 +#define HVMOP_vmtrace_ipt_get_offset 3 + domid_t domain; + uint32_t vcpu; + uint64_t size; + + /* OUT variable */ + uint64_t offset; +}; +typedef struct xen_hvm_vmtrace_op xen_hvm_vmtrace_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_vmtrace_op_t); + #endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */ /* diff --git a/xen/include/public/hvm/params.h b/xen/include/public/hvm/params.h index 0a91bfa749..adbc7e5d08 100644 --- a/xen/include/public/hvm/params.h +++ b/xen/include/public/hvm/params.h @@ -300,6 +300,9 @@ #define XEN_HVM_MCA_CAP_LMCE (xen_mk_ullong(1) << 0) #define XEN_HVM_MCA_CAP_MASK XEN_HVM_MCA_CAP_LMCE -#define HVM_NR_PARAMS 39 +/* VM trace capabilities */ +#define HVM_PARAM_VMTRACE_PT_SIZE 39 + +#define HVM_NR_PARAMS 40 #endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */ diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h index dbd35305df..f823c784c3 100644 --- a/xen/include/public/memory.h +++ b/xen/include/public/memory.h @@ -620,6 +620,7 @@ struct xen_mem_acquire_resource { #define XENMEM_resource_ioreq_server 0 #define XENMEM_resource_grant_table 1 +#define XENMEM_resource_vmtrace_buf 2 /* * IN - a type-specific resource identifier, which must be zero diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index ac53519d7f..b3a36f3788 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -457,6 +457,9 @@ struct domain unsigned pbuf_idx; spinlock_t pbuf_lock; + /* Used by vmtrace domctls */ + spinlock_t vmtrace_lock; + /* OProfile support. */ struct xenoprof *xenoprof; -- 2.20.1
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |