From mboxrd@z Thu Jan 1 00:00:00 1970 From: Don Slutz Subject: Re: [PATCH for-4.5 v7 4/7] xen: Add vmware_port support Date: Thu, 02 Oct 2014 17:58:42 -0400 Message-ID: <542DCA92.1030701@terremark.com> References: <1412285417-19180-1-git-send-email-dslutz@verizon.com> <1412285417-19180-5-git-send-email-dslutz@verizon.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii"; Format="flowed" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1412285417-19180-5-git-send-email-dslutz@verizon.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: Don Slutz , xen-devel@lists.xen.org Cc: Kevin Tian , Keir Fraser , Ian Campbell , Stefano Stabellini , Jun Nakajima , Eddie Dong , Ian Jackson , Tim Deegan , George Dunlap , Aravind Gopalakrishnan , Jan Beulich , Andrew Cooper , Boris Ostrovsky , Suravee Suthikulpanit List-Id: xen-devel@lists.xenproject.org Andrew Cooper just pointed me to "regs->_ebx", will be changing this patch to use them. -Don Slutz On 10/02/14 17:30, Don Slutz wrote: > This includes adding is_vmware_port_enabled > > This is a new domain_create() flag, DOMCRF_vmware_port. It is > passed to domctl as XEN_DOMCTL_CDF_vmware_port. > > This enables limited support of VMware's hyper-call. > > This is both a more complete support then in currently provided by > QEMU and/or KVM and less. The missing part requires QEMU changes > and has been left out until the QEMU patches are accepted upstream. > > VMware's hyper-call is also known as VMware Backdoor I/O Port. > > Note: this support does not depend on vmware_hw being non-zero. > > Summary is that VMware treats "in (%dx),%eax" (or "out %eax,(%dx)") > to port 0x5658 specially. Note: since many operations return data > in EAX, "in (%dx),%eax" is the one to use. The other lengths like > "in (%dx),%al" will still do things, only AL part of EAX will be > changed. For "out %eax,(%dx)" of all lengths, EAX will remain > unchanged. > > Also this instruction is allowed to be used from ring 3. To > support this the vmexit for GP needs to be enabled. I have not > fully tested that nested HVM is doing the right thing for this. > > An open source example of using this is: > > http://open-vm-tools.sourceforge.net/ > > Which only uses "inl (%dx)". Also > > http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 > > The support included is enough to allow VMware tools to install in a > HVM domU. > > For AMD (svm) the max instruction length of 15 is hard coded. This > is because __get_instruction_length_from_list() has issues that when > called from #GP handler NRIP is not available, or that NRIP may not > be available at all on a particular HW, leading to the need read the > instruction twice --- once in __get_instruction_length_from_list() > and then again in vmport_gp_check(). Which is bad because memory may > change between the reads. > > Signed-off-by: Don Slutz > --- > v7: > More on AMD in the commit message. > Switch to only change 32bit part of registers, what VMware > does. > Too much logging and tracing. > Dropped a lot of it. This includes vmport_debug= > > v6: > Dropped the attempt to use svm_nextrip_insn_length via > __get_instruction_length (added in v2). Just always look > at upto 15 bytes on AMD. > > v5: > we should make sure that svm_vmexit_gp_intercept is not executed for > any other guest. > Added an ASSERT on is_vmware_port_enabled. > magic integers? > Added #define for them. > I am fairly certain that you need some brackets here. > Added brackets. > > xen/arch/x86/domain.c | 2 + > xen/arch/x86/hvm/hvm.c | 4 + > xen/arch/x86/hvm/svm/emulate.c | 2 +- > xen/arch/x86/hvm/svm/svm.c | 30 ++++ > xen/arch/x86/hvm/svm/vmcb.c | 2 + > xen/arch/x86/hvm/vmware/Makefile | 1 + > xen/arch/x86/hvm/vmware/vmport.c | 274 ++++++++++++++++++++++++++++++++++ > xen/arch/x86/hvm/vmx/vmcs.c | 2 + > xen/arch/x86/hvm/vmx/vmx.c | 63 +++++++- > xen/arch/x86/hvm/vmx/vvmx.c | 3 + > xen/common/domctl.c | 3 + > xen/include/asm-x86/hvm/domain.h | 3 + > xen/include/asm-x86/hvm/io.h | 2 +- > xen/include/asm-x86/hvm/svm/emulate.h | 1 + > xen/include/asm-x86/hvm/vmport.h | 52 +++++++ > xen/include/public/domctl.h | 3 + > xen/include/xen/sched.h | 3 + > 17 files changed, 445 insertions(+), 5 deletions(-) > create mode 100644 xen/arch/x86/hvm/vmware/vmport.c > create mode 100644 xen/include/asm-x86/hvm/vmport.h > > diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c > index 8cfd1ca..a71da52 100644 > --- a/xen/arch/x86/domain.c > +++ b/xen/arch/x86/domain.c > @@ -524,6 +524,8 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags) > d->arch.hvm_domain.mem_sharing_enabled = 0; > > d->arch.s3_integrity = !!(domcr_flags & DOMCRF_s3_integrity); > + d->arch.hvm_domain.is_vmware_port_enabled = > + !!(domcr_flags & DOMCRF_vmware_port); > > INIT_LIST_HEAD(&d->arch.pdev_list); > > diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c > index 4039061..1357079 100644 > --- a/xen/arch/x86/hvm/hvm.c > +++ b/xen/arch/x86/hvm/hvm.c > @@ -61,6 +61,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -1444,6 +1445,9 @@ int hvm_domain_initialise(struct domain *d) > goto fail1; > d->arch.hvm_domain.io_handler->num_slot = 0; > > + if ( d->arch.hvm_domain.is_vmware_port_enabled ) > + vmport_register(d); > + > if ( is_pvh_domain(d) ) > { > register_portio_handler(d, 0, 0x10003, handle_pvh_io); > diff --git a/xen/arch/x86/hvm/svm/emulate.c b/xen/arch/x86/hvm/svm/emulate.c > index 37a1ece..cfad9ab 100644 > --- a/xen/arch/x86/hvm/svm/emulate.c > +++ b/xen/arch/x86/hvm/svm/emulate.c > @@ -50,7 +50,7 @@ static unsigned int is_prefix(u8 opc) > return 0; > } > > -static unsigned long svm_rip2pointer(struct vcpu *v) > +unsigned long svm_rip2pointer(struct vcpu *v) > { > struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; > unsigned long p = vmcb->cs.base + guest_cpu_user_regs()->eip; > diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c > index e3e1565..d7f13d9 100644 > --- a/xen/arch/x86/hvm/svm/svm.c > +++ b/xen/arch/x86/hvm/svm/svm.c > @@ -59,6 +59,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -2111,6 +2112,31 @@ svm_vmexit_do_vmsave(struct vmcb_struct *vmcb, > return; > } > > +static void svm_vmexit_gp_intercept(struct cpu_user_regs *regs, > + struct vcpu *v) > +{ > + struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; > + /* > + * Just use 15 for the instruction length; vmport_gp_check will > + * adjust it. This is because > + * __get_instruction_length_from_list() has issues, and may > + * require a double read of the instruction bytes. At some > + * point a new routine could be added that is based on the code > + * in vmport_gp_check with extensions to make it more general. > + * Since that routine is the only user of this code this can be > + * done later. > + */ > + unsigned long inst_len = 15; > + unsigned long inst_addr = svm_rip2pointer(v); > + int rc = vmport_gp_check(regs, v, &inst_len, inst_addr, > + vmcb->exitinfo1, vmcb->exitinfo2); > + > + if ( !rc ) > + __update_guest_eip(regs, inst_len); > + else > + hvm_inject_hw_exception(TRAP_gp_fault, vmcb->exitinfo1); > +} > + > static void svm_vmexit_ud_intercept(struct cpu_user_regs *regs) > { > struct hvm_emulate_ctxt ctxt; > @@ -2471,6 +2497,10 @@ void svm_vmexit_handler(struct cpu_user_regs *regs) > break; > } > > + case VMEXIT_EXCEPTION_GP: > + svm_vmexit_gp_intercept(regs, v); > + break; > + > case VMEXIT_EXCEPTION_UD: > svm_vmexit_ud_intercept(regs); > break; > diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c > index 21292bb..45ead61 100644 > --- a/xen/arch/x86/hvm/svm/vmcb.c > +++ b/xen/arch/x86/hvm/svm/vmcb.c > @@ -195,6 +195,8 @@ static int construct_vmcb(struct vcpu *v) > HVM_TRAP_MASK > | (1U << TRAP_no_device); > > + if ( v->domain->arch.hvm_domain.is_vmware_port_enabled ) > + vmcb->_exception_intercepts |= 1U << TRAP_gp_fault; > if ( paging_mode_hap(v->domain) ) > { > vmcb->_np_enable = 1; /* enable nested paging */ > diff --git a/xen/arch/x86/hvm/vmware/Makefile b/xen/arch/x86/hvm/vmware/Makefile > index 3fb2e0b..cd8815b 100644 > --- a/xen/arch/x86/hvm/vmware/Makefile > +++ b/xen/arch/x86/hvm/vmware/Makefile > @@ -1 +1,2 @@ > obj-y += cpuid.o > +obj-y += vmport.o > diff --git a/xen/arch/x86/hvm/vmware/vmport.c b/xen/arch/x86/hvm/vmware/vmport.c > new file mode 100644 > index 0000000..e73a5e6 > --- /dev/null > +++ b/xen/arch/x86/hvm/vmware/vmport.c > @@ -0,0 +1,274 @@ > +/* > + * HVM VMPORT emulation > + * > + * Copyright (C) 2012 Verizon Corporation > + * > + * This file is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License Version 2 (GPLv2) > + * as published by the Free Software Foundation. > + * > + * This file 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. . > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +#include "backdoor_def.h" > + > +#define MAX_INST_LEN 15 > + > +#ifndef NDEBUG > +unsigned int opt_vmport_debug __read_mostly; > +integer_param("vmport_debug", opt_vmport_debug); > +#endif > + > +/* More VMware defines */ > + > +#define VMWARE_GUI_AUTO_GRAB 0x001 > +#define VMWARE_GUI_AUTO_UNGRAB 0x002 > +#define VMWARE_GUI_AUTO_SCROLL 0x004 > +#define VMWARE_GUI_AUTO_RAISE 0x008 > +#define VMWARE_GUI_EXCHANGE_SELECTIONS 0x010 > +#define VMWARE_GUI_WARP_CURSOR_ON_UNGRAB 0x020 > +#define VMWARE_GUI_FULL_SCREEN 0x040 > + > +#define VMWARE_GUI_TO_FULL_SCREEN 0x080 > +#define VMWARE_GUI_TO_WINDOW 0x100 > + > +#define VMWARE_GUI_AUTO_RAISE_DISABLED 0x200 > + > +#define VMWARE_GUI_SYNC_TIME 0x400 > + > +/* When set, toolboxes should not show the cursor options page. */ > +#define VMWARE_DISABLE_CURSOR_OPTIONS 0x800 > + > +void vmport_register(struct domain *d) > +{ > + register_portio_handler(d, BDOOR_PORT, 4, vmport_ioport); > +} > + > +int vmport_ioport(int dir, uint32_t port, uint32_t bytes, uint32_t *val) > +{ > + struct cpu_user_regs *regs = guest_cpu_user_regs(); > + uint32_t cmd = regs->rcx & 0xffff; > + uint32_t magic = regs->rax; > + int rc = X86EMUL_OKAY; > + > + if ( magic == BDOOR_MAGIC ) > + { > + uint64_t saved_rax = regs->rax; > + uint64_t value; > + struct vcpu *curr = current; > + struct domain *d = curr->domain; > + struct segment_register sreg; > + > + switch ( cmd ) > + { > + case BDOOR_CMD_GETMHZ: > + regs->rax = (uint32_t)(d->arch.tsc_khz / 1000); > + break; > + case BDOOR_CMD_GETVERSION: > + /* MAGIC */ > + regs->rbx = (regs->rbx & 0xffffffff00000000ull) | BDOOR_MAGIC; > + /* VERSION_MAGIC */ > + regs->rax = 6; > + /* Claim we are an ESX. VMX_TYPE_SCALABLE_SERVER */ > + regs->rcx = (regs->rcx & 0xffffffff00000000ull) | 2; > + break; > + case BDOOR_CMD_GETSCREENSIZE: > + /* We have no screen size */ > + regs->rax = -1; > + break; > + case BDOOR_CMD_GETHWVERSION: > + /* vmware_hw */ > + regs->rax = 0; > + if ( is_hvm_vcpu(curr) ) > + { > + struct hvm_domain *hd = &d->arch.hvm_domain; > + > + regs->rax = (uint32_t)hd->params[HVM_PARAM_VMWARE_HW]; > + } > + if ( !regs->rax ) > + regs->rax = 4; /* Act like version 4 */ > + break; > + case BDOOR_CMD_GETHZ: > + hvm_get_segment_register(curr, x86_seg_ss, &sreg); > + if ( sreg.attr.fields.dpl == 0 ) > + { > + value = d->arch.tsc_khz * 1000; > + /* apic-frequency (bus speed) */ > + regs->rcx = (regs->rcx & 0xffffffff00000000ull) | > + (uint32_t)(1000000000ULL / APIC_BUS_CYCLE_NS); > + /* High part of tsc-frequency */ > + regs->rbx = (regs->rbx & 0xffffffff00000000ull) | > + (uint32_t)(value >> 32); > + /* Low part of tsc-frequency */ > + regs->rax = (uint32_t)value; > + } > + break; > + case BDOOR_CMD_GETTIME: > + value = get_localtime_us(d) - d->time_offset_seconds * 1000000ULL; > + /* hostUsecs */ > + regs->rbx = (regs->rbx & 0xffffffff00000000ull) | > + (uint32_t)(value % 1000000UL); > + /* hostSecs */ > + regs->rax = (uint32_t)(value / 1000000ULL); > + /* maxTimeLag */ > + regs->rcx = (regs->rcx & 0xffffffff00000000ull) | 1000000; > + /* offset to GMT in minutes */ > + regs->rdx = (regs->rdx & 0xffffffff00000000ull) | > + d->time_offset_seconds / 60; > + break; > + case BDOOR_CMD_GETTIMEFULL: > + value = get_localtime_us(d) - d->time_offset_seconds * 1000000ULL; > + /* ... */ > + regs->rax = BDOOR_MAGIC; > + /* hostUsecs */ > + regs->rbx = (regs->rbx & 0xffffffff00000000ull) | > + (uint32_t)(value % 1000000UL); > + /* High part of hostSecs */ > + regs->rsi = (regs->rsi & 0xffffffff00000000ull) | > + (uint32_t)((value / 1000000ULL) >> 32); > + /* Low part of hostSecs */ > + regs->rdx = (regs->rdx & 0xffffffff00000000ull) | > + (uint32_t)(value / 1000000ULL); > + /* maxTimeLag */ > + regs->rcx = (regs->rcx & 0xffffffff00000000ull) | 1000000; > + break; > + case BDOOR_CMD_GETGUIOPTIONS: > + regs->rax = VMWARE_GUI_AUTO_GRAB | VMWARE_GUI_AUTO_UNGRAB | > + VMWARE_GUI_AUTO_RAISE_DISABLED | VMWARE_GUI_SYNC_TIME | > + VMWARE_DISABLE_CURSOR_OPTIONS; > + break; > + case BDOOR_CMD_SETGUIOPTIONS: > + regs->rax = 0x0; > + break; > + default: > + regs->rax = (uint32_t)~0ul; > + break; > + } > + if ( dir == IOREQ_READ ) > + { > + switch ( bytes ) > + { > + case 1: > + regs->rax = (saved_rax & 0xffffff00) | (regs->rax & 0xff); > + break; > + case 2: > + regs->rax = (saved_rax & 0xffff0000) | (regs->rax & 0xffff); > + break; > + case 4: > + regs->rax = (uint32_t)regs->rax; > + break; > + } > + *val = regs->rax; > + } > + else > + regs->rax = saved_rax; > + } > + else > + rc = X86EMUL_UNHANDLEABLE; > + > + return rc; > +} > + > +int vmport_gp_check(struct cpu_user_regs *regs, struct vcpu *v, > + unsigned long *inst_len, unsigned long inst_addr, > + unsigned long ei1, unsigned long ei2) > +{ > + if ( !v->domain->arch.hvm_domain.is_vmware_port_enabled ) > + return X86EMUL_VMPORT_NOT_ENABLED; > + > + if ( *inst_len && *inst_len <= MAX_INST_LEN && > + (regs->rdx & 0xffff) == BDOOR_PORT && ei1 == 0 && ei2 == 0 && > + (uint32_t)regs->rax == BDOOR_MAGIC ) > + { > + int i = 0; > + uint32_t val; > + uint32_t byte_cnt = hvm_guest_x86_mode(v); > + unsigned char bytes[MAX_INST_LEN]; > + unsigned int fetch_len; > + int frc; > + > + /* in or out are limited to 32bits */ > + if ( byte_cnt > 4 ) > + byte_cnt = 4; > + > + /* > + * Fetch up to the next page break; we'll fetch from the > + * next page later if we have to. > + */ > + fetch_len = min_t(unsigned int, *inst_len, > + PAGE_SIZE - (inst_addr & ~PAGE_MASK)); > + frc = hvm_fetch_from_guest_virt_nofault(bytes, inst_addr, fetch_len, > + PFEC_page_present); > + if ( frc != HVMCOPY_okay ) > + { > + gdprintk(XENLOG_WARNING, > + "Bad instruction fetch at %#lx (frc=%d il=%lu fl=%u)\n", > + (unsigned long) inst_addr, frc, *inst_len, fetch_len); > + return X86EMUL_VMPORT_FETCH_ERROR_BYTE1; > + } > + > + /* Check for operand size prefix */ > + while ( (i < MAX_INST_LEN) && (bytes[i] == 0x66) ) > + { > + i++; > + if ( i >= fetch_len ) > + { > + frc = hvm_fetch_from_guest_virt_nofault( > + &bytes[fetch_len], inst_addr + fetch_len, > + MAX_INST_LEN - fetch_len, PFEC_page_present); > + if ( frc != HVMCOPY_okay ) > + { > + gdprintk(XENLOG_WARNING, > + "Bad instruction fetch at %#lx + %#x (frc=%d)\n", > + inst_addr, fetch_len, frc); > + return X86EMUL_VMPORT_FETCH_ERROR_BYTE2; > + } > + fetch_len = MAX_INST_LEN; > + } > + } > + *inst_len = i + 1; > + > + /* Only adjust byte_cnt 1 time */ > + if ( bytes[0] == 0x66 ) /* operand size prefix */ > + { > + if ( byte_cnt == 4 ) > + byte_cnt = 2; > + else > + byte_cnt = 4; > + } > + if ( bytes[i] == 0xed ) /* in (%dx),%eax or in (%dx),%ax */ > + return vmport_ioport(IOREQ_READ, BDOOR_PORT, byte_cnt, &val); > + else if ( bytes[i] == 0xec ) /* in (%dx),%al */ > + return vmport_ioport(IOREQ_READ, BDOOR_PORT, 1, &val); > + else if ( bytes[i] == 0xef ) /* out %eax,(%dx) or out %ax,(%dx) */ > + return vmport_ioport(IOREQ_WRITE, BDOOR_PORT, byte_cnt, &val); > + else if ( bytes[i] == 0xee ) /* out %al,(%dx) */ > + return vmport_ioport(IOREQ_WRITE, BDOOR_PORT, 1, &val); > + else > + { > + *inst_len = 0; /* This is unknown. */ > + return X86EMUL_VMPORT_BAD_OPCODE; > + } > + } > + *inst_len = 0; /* This is unknown. */ > + return X86EMUL_VMPORT_BAD_STATE; > +} > + > +/* > + * Local variables: > + * mode: C > + * c-set-style: "BSD" > + * c-basic-offset: 4 > + * tab-width: 4 > + * indent-tabs-mode: nil > + * End: > + */ > diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c > index 9d8033e..1bab216 100644 > --- a/xen/arch/x86/hvm/vmx/vmcs.c > +++ b/xen/arch/x86/hvm/vmx/vmcs.c > @@ -1102,6 +1102,8 @@ static int construct_vmcs(struct vcpu *v) > > v->arch.hvm_vmx.exception_bitmap = HVM_TRAP_MASK > | (paging_mode_hap(d) ? 0 : (1U << TRAP_page_fault)) > + | (v->domain->arch.hvm_domain.is_vmware_port_enabled ? > + (1U << TRAP_gp_fault) : 0) > | (1U << TRAP_no_device); > vmx_update_exception_bitmap(v); > > diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c > index 304aeea..300d804 100644 > --- a/xen/arch/x86/hvm/vmx/vmx.c > +++ b/xen/arch/x86/hvm/vmx/vmx.c > @@ -44,6 +44,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -1276,9 +1277,11 @@ static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr) > vmx_set_segment_register( > v, s, &v->arch.hvm_vmx.vm86_saved_seg[s]); > v->arch.hvm_vmx.exception_bitmap = HVM_TRAP_MASK > - | (paging_mode_hap(v->domain) ? > - 0 : (1U << TRAP_page_fault)) > - | (1U << TRAP_no_device); > + | (paging_mode_hap(v->domain) ? > + 0 : (1U << TRAP_page_fault)) > + | (v->domain->arch.hvm_domain.is_vmware_port_enabled ? > + (1U << TRAP_gp_fault) : 0) > + | (1U << TRAP_no_device); > vmx_update_exception_bitmap(v); > vmx_update_debug_state(v); > } > @@ -2589,6 +2592,57 @@ static void vmx_idtv_reinject(unsigned long idtv_info) > } > } > > +static unsigned long vmx_rip2pointer(struct cpu_user_regs *regs, > + struct vcpu *v) > +{ > + struct segment_register cs; > + unsigned long p; > + > + vmx_get_segment_register(v, x86_seg_cs, &cs); > + p = cs.base + regs->rip; > + if ( !(cs.attr.fields.l && hvm_long_mode_enabled(v)) ) > + return (uint32_t)p; /* mask to 32 bits */ > + return p; > +} > + > +static void vmx_vmexit_gp_intercept(struct cpu_user_regs *regs, > + struct vcpu *v) > +{ > + unsigned long exit_qualification; > + unsigned long inst_len; > + unsigned long inst_addr = vmx_rip2pointer(regs, v); > + unsigned long ecode; > + int rc; > +#ifndef NDEBUG > + unsigned long orig_inst_len; > + unsigned long vector; > + > + __vmread(VM_EXIT_INTR_INFO, &vector); > + BUG_ON(!(vector & INTR_INFO_VALID_MASK)); > + BUG_ON(!(vector & INTR_INFO_DELIVER_CODE_MASK)); > +#endif > + > + __vmread(EXIT_QUALIFICATION, &exit_qualification); > + __vmread(VM_EXIT_INSTRUCTION_LEN, &inst_len); > + __vmread(VM_EXIT_INTR_ERROR_CODE, &ecode); > + > +#ifndef NDEBUG > + orig_inst_len = inst_len; > +#endif > + rc = vmport_gp_check(regs, v, &inst_len, inst_addr, > + ecode, exit_qualification); > +#ifndef NDEBUG > + if ( inst_len && orig_inst_len != inst_len ) > + gdprintk(XENLOG_WARNING, > + "Unexpected instruction length difference: %lu vs %lu\n", > + orig_inst_len, inst_len); > +#endif > + if ( !rc ) > + update_guest_eip(); > + else > + hvm_inject_hw_exception(TRAP_gp_fault, ecode); > +} > + > static int vmx_handle_apic_write(void) > { > unsigned long exit_qualification; > @@ -2814,6 +2868,9 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs) > HVMTRACE_1D(TRAP, vector); > vmx_fpu_dirty_intercept(); > break; > + case TRAP_gp_fault: > + vmx_vmexit_gp_intercept(regs, v); > + break; > case TRAP_page_fault: > __vmread(EXIT_QUALIFICATION, &exit_qualification); > __vmread(VM_EXIT_INTR_ERROR_CODE, &ecode); > diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c > index 9ccc03f..8e07f92 100644 > --- a/xen/arch/x86/hvm/vmx/vvmx.c > +++ b/xen/arch/x86/hvm/vmx/vvmx.c > @@ -24,6 +24,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -2182,6 +2183,8 @@ int nvmx_n2_vmexit_handler(struct cpu_user_regs *regs, > if ( v->fpu_dirtied ) > nvcpu->nv_vmexit_pending = 1; > } > + else if ( vector == TRAP_gp_fault ) > + nvcpu->nv_vmexit_pending = 1; > else if ( (intr_info & valid_mask) == valid_mask ) > { > exec_bitmap =__get_vvmcs(nvcpu->nv_vvmcx, EXCEPTION_BITMAP); > diff --git a/xen/common/domctl.c b/xen/common/domctl.c > index 30c9e50..fad55a2 100644 > --- a/xen/common/domctl.c > +++ b/xen/common/domctl.c > @@ -543,6 +543,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) > ~(XEN_DOMCTL_CDF_hvm_guest > | XEN_DOMCTL_CDF_pvh_guest > | XEN_DOMCTL_CDF_hap > + | XEN_DOMCTL_CDF_vmware_port > | XEN_DOMCTL_CDF_s3_integrity > | XEN_DOMCTL_CDF_oos_off)) ) > break; > @@ -586,6 +587,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) > domcr_flags |= DOMCRF_s3_integrity; > if ( op->u.createdomain.flags & XEN_DOMCTL_CDF_oos_off ) > domcr_flags |= DOMCRF_oos_off; > + if ( op->u.createdomain.flags & XEN_DOMCTL_CDF_vmware_port ) > + domcr_flags |= DOMCRF_vmware_port; > > d = domain_create(dom, domcr_flags, op->u.createdomain.ssidref); > if ( IS_ERR(d) ) > diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h > index 2757c7f..d4718df 100644 > --- a/xen/include/asm-x86/hvm/domain.h > +++ b/xen/include/asm-x86/hvm/domain.h > @@ -121,6 +121,9 @@ struct hvm_domain { > spinlock_t uc_lock; > bool_t is_in_uc_mode; > > + /* VMware backdoor port available */ > + bool_t is_vmware_port_enabled; > + > /* Pass-through */ > struct hvm_iommu hvm_iommu; > > diff --git a/xen/include/asm-x86/hvm/io.h b/xen/include/asm-x86/hvm/io.h > index 886a9d6..d257161 100644 > --- a/xen/include/asm-x86/hvm/io.h > +++ b/xen/include/asm-x86/hvm/io.h > @@ -25,7 +25,7 @@ > #include > #include > > -#define MAX_IO_HANDLER 16 > +#define MAX_IO_HANDLER 17 > > #define HVM_PORTIO 0 > #define HVM_BUFFERED_IO 2 > diff --git a/xen/include/asm-x86/hvm/svm/emulate.h b/xen/include/asm-x86/hvm/svm/emulate.h > index ccc2d3c..d9a9dc5 100644 > --- a/xen/include/asm-x86/hvm/svm/emulate.h > +++ b/xen/include/asm-x86/hvm/svm/emulate.h > @@ -44,6 +44,7 @@ enum instruction_index { > > struct vcpu; > > +unsigned long svm_rip2pointer(struct vcpu *v); > int __get_instruction_length_from_list( > struct vcpu *, const enum instruction_index *, unsigned int list_count); > > diff --git a/xen/include/asm-x86/hvm/vmport.h b/xen/include/asm-x86/hvm/vmport.h > new file mode 100644 > index 0000000..d037d55 > --- /dev/null > +++ b/xen/include/asm-x86/hvm/vmport.h > @@ -0,0 +1,52 @@ > +/* > + * asm/hvm/vmport.h: HVM VMPORT emulation > + * > + * > + * Copyright (C) 2012 Verizon Corporation > + * > + * This file is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License Version 2 (GPLv2) > + * as published by the Free Software Foundation. > + * > + * This file 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. . > + */ > + > +#ifndef ASM_X86_HVM_VMPORT_H__ > +#define ASM_X86_HVM_VMPORT_H__ > + > +void vmport_register(struct domain *d); > +int vmport_ioport(int dir, uint32_t port, uint32_t bytes, uint32_t *val); > +int vmport_gp_check(struct cpu_user_regs *regs, struct vcpu *v, > + unsigned long *inst_len, unsigned long inst_addr, > + unsigned long ei1, unsigned long ei2); > +/* > + * Additional return values from vmport_gp_check. > + * > + * Note: return values include: > + * X86EMUL_OKAY > + * X86EMUL_UNHANDLEABLE > + * X86EMUL_EXCEPTION > + * X86EMUL_RETRY > + * X86EMUL_CMPXCHG_FAILED > + * > + * The additional do not overlap any of the above. > + */ > +#define X86EMUL_VMPORT_NOT_ENABLED 10 > +#define X86EMUL_VMPORT_FETCH_ERROR_BYTE1 11 > +#define X86EMUL_VMPORT_FETCH_ERROR_BYTE2 12 > +#define X86EMUL_VMPORT_BAD_OPCODE 13 > +#define X86EMUL_VMPORT_BAD_STATE 14 > + > +#endif /* ASM_X86_HVM_VMPORT_H__ */ > + > +/* > + * 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 61f7555..2b38515 100644 > --- a/xen/include/public/domctl.h > +++ b/xen/include/public/domctl.h > @@ -63,6 +63,9 @@ struct xen_domctl_createdomain { > /* Is this a PVH guest (as opposed to an HVM or PV guest)? */ > #define _XEN_DOMCTL_CDF_pvh_guest 4 > #define XEN_DOMCTL_CDF_pvh_guest (1U<<_XEN_DOMCTL_CDF_pvh_guest) > + /* Is VMware backdoor port available? */ > +#define _XEN_DOMCTL_CDF_vmware_port 5 > +#define XEN_DOMCTL_CDF_vmware_port (1U<<_XEN_DOMCTL_CDF_vmware_port) > uint32_t flags; > }; > typedef struct xen_domctl_createdomain xen_domctl_createdomain_t; > diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h > index c5157e6..d741978 100644 > --- a/xen/include/xen/sched.h > +++ b/xen/include/xen/sched.h > @@ -546,6 +546,9 @@ struct domain *domain_create( > /* DOMCRF_pvh: Create PV domain in HVM container. */ > #define _DOMCRF_pvh 5 > #define DOMCRF_pvh (1U<<_DOMCRF_pvh) > + /* DOMCRF_vmware_port: Enable use of vmware backdoor port. */ > +#define _DOMCRF_vmware_port 6 > +#define DOMCRF_vmware_port (1U<<_DOMCRF_vmware_port) > > /* > * rcu_lock_domain_by_id() is more efficient than get_domain_by_id().