[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH for-4.5 v5 04/16] xen: Add 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 a debug=y build there is a new command line option vmport_debug=. It enabled output to the console of various stages of handling the "in (%dx)" instruction. Signed-off-by: Don Slutz <dslutz@xxxxxxxxxxx> --- 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 | 26 ++- xen/arch/x86/hvm/svm/svm.c | 39 ++++- xen/arch/x86/hvm/svm/vmcb.c | 2 + xen/arch/x86/hvm/vmware/Makefile | 1 + xen/arch/x86/hvm/vmware/vmport.c | 305 ++++++++++++++++++++++++++++++++++ xen/arch/x86/hvm/vmx/vmcs.c | 2 + xen/arch/x86/hvm/vmx/vmx.c | 73 +++++++- xen/arch/x86/hvm/vmx/vvmx.c | 14 ++ 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 | 13 +- xen/include/asm-x86/hvm/vmport.h | 77 +++++++++ xen/include/public/domctl.h | 3 + xen/include/xen/sched.h | 3 + 17 files changed, 558 insertions(+), 14 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 f7e0e78..a6b82de 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -507,6 +507,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 03a1a19..9c59c85 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -58,6 +58,7 @@ #include <asm/hvm/trace.h> #include <asm/hvm/nestedhvm.h> #include <asm/hvm/vmware.h> +#include <asm/hvm/vmport.h> #include <asm/mtrr.h> #include <asm/apic.h> #include <public/sched.h> @@ -1498,6 +1499,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..2446eb7 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; @@ -78,6 +78,7 @@ static unsigned long svm_nextrip_insn_length(struct vcpu *v) /* ...and the rest of the #VMEXITs */ case VMEXIT_CR0_SEL_WRITE: case VMEXIT_EXCEPTION_BP: + case VMEXIT_EXCEPTION_GP: break; default: BUG(); @@ -95,6 +96,10 @@ MAKE_INSTR(CPUID, 2, 0x0f, 0xa2); MAKE_INSTR(RDMSR, 2, 0x0f, 0x32); MAKE_INSTR(WRMSR, 2, 0x0f, 0x30); MAKE_INSTR(VMCALL, 3, 0x0f, 0x01, 0xd9); +MAKE_INSTR(INB_DX, 1, 0xec); +MAKE_INSTR(INL_DX, 1, 0xed); +MAKE_INSTR(OUTB_DX,1, 0xee); +MAKE_INSTR(OUTL_DX,1, 0xef); MAKE_INSTR(HLT, 1, 0xf4); MAKE_INSTR(INT3, 1, 0xcc); MAKE_INSTR(RDTSC, 2, 0x0f, 0x31); @@ -115,6 +120,10 @@ static const u8 *const opc_bytes[INSTR_MAX_COUNT] = [INSTR_RDMSR] = OPCODE_RDMSR, [INSTR_WRMSR] = OPCODE_WRMSR, [INSTR_VMCALL] = OPCODE_VMCALL, + [INSTR_INB_DX] = OPCODE_INB_DX, + [INSTR_INL_DX] = OPCODE_INL_DX, + [INSTR_OUTB_DX] = OPCODE_OUTB_DX, + [INSTR_OUTL_DX] = OPCODE_OUTL_DX, [INSTR_HLT] = OPCODE_HLT, [INSTR_INT3] = OPCODE_INT3, [INSTR_RDTSC] = OPCODE_RDTSC, @@ -152,7 +161,9 @@ static int fetch(struct vcpu *v, u8 *buf, unsigned long addr, int len) } int __get_instruction_length_from_list(struct vcpu *v, - const enum instruction_index *list, unsigned int list_count) + const enum instruction_index *list, + unsigned int list_count, + bool_t err_rpt) { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; unsigned int i, j, inst_len = 0; @@ -211,10 +222,13 @@ int __get_instruction_length_from_list(struct vcpu *v, mismatch: ; } - gdprintk(XENLOG_WARNING, - "%s: Mismatch between expected and actual instruction bytes: " - "eip = %lx\n", __func__, (unsigned long)vmcb->rip); - hvm_inject_hw_exception(TRAP_gp_fault, 0); + if ( err_rpt ) + { + gdprintk(XENLOG_WARNING, + "%s: Mismatch between expected and actual instruction bytes: " + "eip = %lx\n", __func__, (unsigned long)vmcb->rip); + hvm_inject_hw_exception(TRAP_gp_fault, 0); + } return 0; done: diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index b5188e6..ca96ffe 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -59,6 +59,7 @@ #include <public/sched.h> #include <asm/hvm/vpt.h> #include <asm/hvm/trace.h> +#include <asm/hvm/vmport.h> #include <asm/hap.h> #include <asm/apic.h> #include <asm/debugger.h> @@ -2065,6 +2066,38 @@ 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; + unsigned long inst_len; + unsigned long inst_addr = svm_rip2pointer(v); + int rc; + static const enum instruction_index list[] = { + INSTR_INL_DX, INSTR_INB_DX, INSTR_OUTL_DX, INSTR_OUTB_DX + }; + + inst_len = __get_instruction_length_from_list( + v, list, ARRAY_SIZE(list), 0); + + rc = vmport_gp_check(regs, v, inst_len, inst_addr, + vmcb->exitinfo1, vmcb->exitinfo2); + if ( !rc ) + __update_guest_eip(regs, inst_len); + else + { + VMPORT_DBG_LOG(VMPORT_LOG_GP_UNKNOWN, + "gp: rc=%d ei1=0x%lx ei2=0x%lx ec=0x%x ip=%"PRIx64 + " (0x%lx,%ld) ax=%"PRIx64" bx=%"PRIx64" cx=%"PRIx64 + " dx=%"PRIx64" si=%"PRIx64" di=%"PRIx64, rc, + (unsigned long)vmcb->exitinfo1, + (unsigned long)vmcb->exitinfo2, regs->error_code, + regs->rip, inst_addr, inst_len, regs->rax, regs->rbx, + regs->rcx, regs->rdx, regs->rsi, regs->rdi); + 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; @@ -2140,7 +2173,7 @@ static void svm_vmexit_do_invalidate_cache(struct cpu_user_regs *regs) int inst_len; inst_len = __get_instruction_length_from_list( - current, list, ARRAY_SIZE(list)); + current, list, ARRAY_SIZE(list), 1); if ( inst_len == 0 ) return; @@ -2412,6 +2445,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..caa1748 --- /dev/null +++ b/xen/arch/x86/hvm/vmware/vmport.c @@ -0,0 +1,305 @@ +/* + * 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. <http://www.gnu.org/licenses/>. + */ + +#include <xen/config.h> +#include <xen/lib.h> +#include <asm/hvm/hvm.h> +#include <asm/hvm/support.h> +#include <asm/hvm/vmport.h> + +#include "backdoor_def.h" +#include "guest_msg_def.h" + +#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; + + VMPORT_DBG_LOG(VMPORT_LOG_TRACE, + "VMware trace dir=%d bytes=%u ip=%"PRIx64" cmd=%d ax=%" + PRIx64" bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64" si=%" + PRIx64" di=%"PRIx64"\n", dir, bytes, + regs->rip, cmd, regs->rax, regs->rbx, regs->rcx, + regs->rdx, regs->rsi, regs->rdi); + switch ( cmd ) + { + case BDOOR_CMD_GETMHZ: + /* ... */ + regs->rbx = BDOOR_MAGIC; + regs->rax = current->domain->arch.tsc_khz / 1000; + break; + case BDOOR_CMD_GETVERSION: + /* ... */ + regs->rbx = BDOOR_MAGIC; + /* VERSION_MAGIC */ + regs->rax = 6; + /* Claim we are an ESX. VMX_TYPE_SCALABLE_SERVER */ + regs->rcx = 2; + break; + case BDOOR_CMD_GETSCREENSIZE: + /* We have no screen size */ + regs->rax = 0; + break; + case BDOOR_CMD_GETHWVERSION: + /* vmware_hw */ + regs->rax = 0; + if ( is_hvm_vcpu(current) ) + { + struct hvm_domain *hd = ¤t->domain->arch.hvm_domain; + + regs->rax = hd->params[HVM_PARAM_VMWARE_HW]; + } + if ( !regs->rax ) + regs->rax = 4; /* Act like version 4 */ + break; + case BDOOR_CMD_GETHZ: + value = current->domain->arch.tsc_khz * 1000; + /* apic-frequency (bus speed) */ + regs->rcx = (uint32_t)(1000000000ULL / APIC_BUS_CYCLE_NS); + /* High part of tsc-frequency */ + regs->rbx = (uint32_t)(value >> 32); + /* Low part of tsc-frequency */ + regs->rax = value; + break; + case BDOOR_CMD_GETTIME: + value = get_localtime_us(current->domain); + /* hostUsecs */ + regs->rbx = (uint32_t)(value % 1000000UL); + /* hostSecs */ + regs->rax = value / 1000000ULL; + /* maxTimeLag */ + regs->rcx = 0; + break; + case BDOOR_CMD_GETTIMEFULL: + value = get_localtime_us(current->domain); + /* ... */ + regs->rax = BDOOR_MAGIC; + /* hostUsecs */ + regs->rbx = (uint32_t)(value % 1000000UL); + /* High part of hostSecs */ + regs->rsi = (uint32_t)((value / 1000000ULL) >> 32); + /* Low part of hostSecs */ + regs->rdx = (uint32_t)(value / 1000000ULL); + /* maxTimeLag */ + regs->rcx = 0; + 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: + VMPORT_DBG_LOG(VMPORT_LOG_ERROR, + "VMware bytes=%d dir=%d cmd=%d", + bytes, dir, cmd); + break; + } + VMPORT_DBG_LOG(VMPORT_LOG_VMWARE_AFTER, + "VMware after ip=%"PRIx64" cmd=%d ax=%"PRIx64" bx=%" + PRIx64" cx=%"PRIx64" dx=%"PRIx64" si=%"PRIx64" di=%" + PRIx64"\n", + regs->rip, cmd, regs->rax, regs->rbx, regs->rcx, + regs->rdx, regs->rsi, regs->rdi); + 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; + VMPORT_DBG_LOG(VMPORT_LOG_ERROR, + "Not VMware %x vs %x; ip=%"PRIx64" ax=%"PRIx64 + " bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64" si=%"PRIx64 + " di=%"PRIx64"", + magic, BDOOR_MAGIC, regs->rip, regs->rax, regs->rbx, + regs->rcx, regs->rdx, regs->rsi, regs->rdi); + } + + 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) +{ + ASSERT(v->domain->arch.hvm_domain.is_vmware_port_enabled); + + if ( inst_len && inst_len <= 2 && (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 = 4; + unsigned char bytes[2]; + unsigned int fetch_len; + int frc; + int rc; + + /* + * 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; + } + if ( bytes[0] == 0x66 ) /* operand size prefix */ + { + byte_cnt = 2; + i = 1; + if ( fetch_len != inst_len ) + { + frc = hvm_fetch_from_guest_virt_nofault(&bytes[1], + inst_addr + 1, 1, + PFEC_page_present); + if ( frc != HVMCOPY_okay ) + { + gdprintk(XENLOG_WARNING, + "Bad instruction fetch at %#lx + 1 (frc=%d)\n", + (unsigned long) inst_addr, frc); + return X86EMUL_VMPORT_FETCH_ERROR_BYTE2; + } + } + } + if ( bytes[i] == 0xed ) /* in (%dx),%eax or in (%dx),%ax */ + { + rc = vmport_ioport(IOREQ_READ, BDOOR_PORT, byte_cnt, &val); + VMPORT_DBG_LOG(VMPORT_LOG_GP_VMWARE_AFTER, + "gp: VMwareIn rc=%d ip=%"PRIx64" byte_cnt=%d ax=%" + PRIx64" bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64 + " si=%"PRIx64" di=%"PRIx64, rc, + inst_addr, byte_cnt, regs->rax, regs->rbx, + regs->rcx, regs->rdx, regs->rsi, regs->rdi); + return rc; + } + else if ( bytes[i] == 0xec ) /* in (%dx),%al */ + { + rc = vmport_ioport(IOREQ_READ, BDOOR_PORT, 1, &val); + VMPORT_DBG_LOG(VMPORT_LOG_GP_VMWARE_AFTER, + "gp: VMwareIn rc=%d ip=%"PRIx64" byte_cnt=1 ax=%" + PRIx64" bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64 + " si=%"PRIx64" di=%"PRIx64, rc, + inst_addr, regs->rax, regs->rbx, regs->rcx, + regs->rdx, regs->rsi, regs->rdi); + return rc; + } + else if ( bytes[i] == 0xef ) /* out %eax,(%dx) or out %ax,(%dx) */ + { + rc = vmport_ioport(IOREQ_WRITE, BDOOR_PORT, byte_cnt, &val); + VMPORT_DBG_LOG(VMPORT_LOG_GP_VMWARE_AFTER, + "gp: VMwareOut rc=%d ip=%"PRIx64" byte_cnt=%d ax=%" + PRIx64" bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64 + " si=%"PRIx64" di=%"PRIx64, rc, + inst_addr, byte_cnt, regs->rax, regs->rbx, + regs->rcx, regs->rdx, regs->rsi, regs->rdi); + return rc; + } + else if ( bytes[i] == 0xee ) /* out %al,(%dx) */ + { + rc = vmport_ioport(IOREQ_WRITE, BDOOR_PORT, 1, &val); + VMPORT_DBG_LOG(VMPORT_LOG_GP_VMWARE_AFTER, + "gp: VMwareOut rc=%d ip=%"PRIx64" byte_cnt=1 ax=%" + PRIx64" bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64 + " si=%"PRIx64" di=%"PRIx64, rc, + inst_addr, regs->rax, regs->rbx, regs->rcx, + regs->rdx, regs->rsi, regs->rdi); + return rc; + } + else + { + VMPORT_DBG_LOG(VMPORT_LOG_GP_FAIL_RD_INST, + "gp: VMware? lip=%"PRIx64"[%d]=>0x%x(%ld) ax=%" + PRIx64" bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64 + " si=%"PRIx64" di=%"PRIx64, + inst_addr, i, bytes[i], inst_len, regs->rax, + regs->rbx, regs->rcx, regs->rdx, regs->rsi, + regs->rdi); + return X86EMUL_VMPORT_BAD_OPCODE; + } + } + 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 4a4f4e1..bbe3e96 100644 --- a/xen/arch/x86/hvm/vmx/vmcs.c +++ b/xen/arch/x86/hvm/vmx/vmcs.c @@ -1077,6 +1077,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 d3e7ac9..9ad1a79 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -44,6 +44,7 @@ #include <asm/hvm/support.h> #include <asm/hvm/vmx/vmx.h> #include <asm/hvm/vmx/vmcs.h> +#include <asm/hvm/vmport.h> #include <public/sched.h> #include <public/hvm/ioreq.h> #include <asm/hvm/vpic.h> @@ -1277,9 +1278,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); } @@ -2564,6 +2567,56 @@ 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 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); + + rc = vmport_gp_check(regs, v, inst_len, inst_addr, + ecode, exit_qualification); + if ( !rc ) + update_guest_eip(); + else + { + VMPORT_DBG_LOG(VMPORT_LOG_GP_UNKNOWN, + "gp: rc=%d ecode=0x%lx eq=0x%lx ec=0x%x ip=%"PRIx64 + " (0x%lx,%ld) ax=%"PRIx64" bx=%"PRIx64" cx=%"PRIx64 + " dx=%"PRIx64" si=%"PRIx64" di=%"PRIx64, rc, ecode, + exit_qualification, regs->error_code, regs->rip, + inst_addr, inst_len, regs->rax, regs->rbx, regs->rcx, + regs->rdx, regs->rsi, regs->rdi); + hvm_inject_hw_exception(TRAP_gp_fault, ecode); + } +} + static int vmx_handle_apic_write(void) { unsigned long exit_qualification; @@ -2674,6 +2727,17 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs) && vector != TRAP_nmi && vector != TRAP_machine_check ) { +#ifndef NDEBUG + if ( vector == TRAP_gp_fault ) + { + VMPORT_DBG_LOG(VMPORT_LOG_REALMODE_GP, + "realmode gp: ip=%"PRIx64" ax=%"PRIx64 + " bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64 + " si=%"PRIx64" di=%"PRIx64, + regs->rip, regs->rax, regs->rbx, regs->rcx, + regs->rdx, regs->rsi, regs->rdi); + } +#endif perfc_incr(realmode_exits); v->arch.hvm_vmx.vmx_emulate = 1; HVMTRACE_0D(REALMODE_EMULATE); @@ -2789,6 +2853,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..51d2336 100644 --- a/xen/arch/x86/hvm/vmx/vvmx.c +++ b/xen/arch/x86/hvm/vmx/vvmx.c @@ -24,6 +24,7 @@ #include <asm/types.h> #include <asm/mtrr.h> #include <asm/p2m.h> +#include <asm/hvm/vmport.h> #include <asm/hvm/vmx/vmx.h> #include <asm/hvm/vmx/vvmx.h> #include <asm/hvm/nestedhvm.h> @@ -2182,6 +2183,19 @@ 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 ) + { +#ifndef NDEBUG + struct cpu_user_regs *ur = guest_cpu_user_regs(); + VMPORT_DBG_LOG(VMPORT_LOG_VGP_UNKNOWN, + "Unexpected gp: ip=%"PRIx64" ax=%"PRIx64 + " bx=%"PRIx64" cx=%"PRIx64" dx=%"PRIx64 + " si=%"PRIx64" di=%"PRIx64, + ur->rip, ur->rax, ur->rbx, ur->rcx, ur->rdx, + ur->rsi, ur->rdi); +#endif + 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 8907aac..1307be0 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -541,6 +541,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; @@ -584,6 +585,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 291a2e0..93b081b 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 <public/hvm/ioreq.h> #include <public/event_channel.h> -#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..542b6b8 100644 --- a/xen/include/asm-x86/hvm/svm/emulate.h +++ b/xen/include/asm-x86/hvm/svm/emulate.h @@ -39,18 +39,25 @@ enum instruction_index { INSTR_STGI, INSTR_CLGI, INSTR_INVLPGA, + INSTR_INL_DX, + INSTR_INB_DX, + INSTR_OUTL_DX, + INSTR_OUTB_DX, INSTR_MAX_COUNT /* Must be last - Number of instructions supported */ }; struct vcpu; -int __get_instruction_length_from_list( - struct vcpu *, const enum instruction_index *, unsigned int list_count); +unsigned long svm_rip2pointer(struct vcpu *v); +int __get_instruction_length_from_list(struct vcpu *, + const enum instruction_index *, + unsigned int list_count, + bool_t err_rpt); static inline int __get_instruction_length( struct vcpu *v, enum instruction_index instr) { - return __get_instruction_length_from_list(v, &instr, 1); + return __get_instruction_length_from_list(v, &instr, 1, 1); } #endif /* __ASM_X86_HVM_SVM_EMULATE_H__ */ diff --git a/xen/include/asm-x86/hvm/vmport.h b/xen/include/asm-x86/hvm/vmport.h new file mode 100644 index 0000000..a8a3dea --- /dev/null +++ b/xen/include/asm-x86/hvm/vmport.h @@ -0,0 +1,77 @@ +/* + * 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. <http://www.gnu.org/licenses/>. + */ + +#ifndef ASM_X86_HVM_VMPORT_H__ +#define ASM_X86_HVM_VMPORT_H__ + +#ifndef NDEBUG + +#define VMPORT_LOG_GP_UNKNOWN (1 << 0) +#define VMPORT_LOG_GP_VMWARE_AFTER (1 << 1) +#define VMPORT_LOG_GP_FAIL_RD_INST (1 << 2) +#define VMPORT_LOG_VGP_UNKNOWN (1 << 3) +#define VMPORT_LOG_REALMODE_GP (1 << 4) + +#define VMPORT_LOG_GP_NOT_VMWARE (1 << 9) + +#define VMPORT_LOG_TRACE (1 << 16) +#define VMPORT_LOG_ERROR (1 << 17) +#define VMPORT_LOG_VMWARE_AFTER (1 << 18) + +extern unsigned int opt_vmport_debug; +#define VMPORT_DBG_LOG(level, _f, _a...) \ + do { \ + if ( unlikely((level) & opt_vmport_debug) ) \ + printk("[HVM:%d.%d] <%s> " _f "\n", \ + current->domain->domain_id, current->vcpu_id, __func__, \ + ## _a); \ + } while ( 0 ) +#else +#define VMPORT_DBG_LOG(level, _f, _a...) do {} while ( 0 ) +#endif + +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_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 69a8b44..7a0f691 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(). -- 1.8.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |