From: Don Slutz <dslutz@verizon.com>
To: xen-devel@lists.xen.org
Cc: Kevin Tian <kevin.tian@intel.com>, Keir Fraser <keir@xen.org>,
Ian Campbell <ian.campbell@citrix.com>,
Stefano Stabellini <stefano.stabellini@eu.citrix.com>,
Jun Nakajima <jun.nakajima@intel.com>,
Eddie Dong <eddie.dong@intel.com>,
Ian Jackson <ian.jackson@eu.citrix.com>,
Don Slutz <dslutz@verizon.com>, Tim Deegan <tim@xen.org>,
George Dunlap <George.Dunlap@eu.citrix.com>,
Aravind Gopalakrishnan <Aravind.Gopalakrishnan@amd.com>,
Jan Beulich <jbeulich@suse.com>,
Andrew Cooper <andrew.cooper3@citrix.com>,
Boris Ostrovsky <boris.ostrovsky@oracle.com>,
Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Subject: [PATCH v4 04/16] xen: Add is_vmware_port_enabled
Date: Thu, 11 Sep 2014 14:36:38 -0400 [thread overview]
Message-ID: <1410460610-14759-5-git-send-email-dslutz@verizon.com> (raw)
In-Reply-To: <1410460610-14759-1-git-send-email-dslutz@verizon.com>
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@verizon.com>
---
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 | 310 ++++++++++++++++++++++++++++++++++
xen/arch/x86/hvm/vmx/vmcs.c | 1 +
xen/arch/x86/hvm/vmx/vmx.c | 60 +++++++
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 | 61 +++++++
xen/include/public/domctl.h | 3 +
xen/include/xen/sched.h | 3 +
17 files changed, 536 insertions(+), 11 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..9e14d2a 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 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->rip, inst_addr,
+ inst_len, regs->rax, regs->rbx, regs->rcx, regs->rdx,
+ regs->rsi, regs->rdi);
+ hvm_inject_hw_exception(TRAP_gp_fault, regs->error_code);
+ }
+}
+
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..914d1ad 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 |= 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..bfc01dc
--- /dev/null
+++ b/xen/arch/x86/hvm/vmware/vmport.c
@@ -0,0 +1,310 @@
+/*
+ * 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
+
+inline uint16_t get_low_bits(uint32_t bits)
+{
+ return bits & 0xffff;
+}
+
+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 = get_low_bits(regs->rcx);
+ 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_GETHWVERSION:
+ /* ... */
+ regs->rbx = BDOOR_MAGIC;
+ /* 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) | get_low_bits(regs->rax);
+ 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)
+{
+ if ( !v->domain->arch.hvm_domain.is_vmware_port_enabled )
+ return 10;
+
+ if ( inst_len && inst_len <= 2 && get_low_bits(regs->rdx) == BDOOR_PORT &&
+ ei1 == 0 && ei2 == 0 && regs->error_code == 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 11;
+ }
+ 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 12;
+ }
+ }
+ }
+ 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 13;
+ }
+ }
+ return 14;
+}
+
+/*
+ * 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..fa1f69a 100644
--- a/xen/arch/x86/hvm/vmx/vmcs.c
+++ b/xen/arch/x86/hvm/vmx/vmcs.c
@@ -1077,6 +1077,7 @@ 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))
+ | (1U << TRAP_gp_fault)
| (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 26b1ad5..6cd1730 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>
@@ -1279,6 +1280,8 @@ static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr)
v->arch.hvm_vmx.exception_bitmap = HVM_TRAP_MASK
| (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);
@@ -2565,6 +2568,49 @@ 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;
+
+ __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 ip=%"PRIx64
+ " (0x%lx,%ld) ax=%"PRIx64" bx=%"PRIx64" cx=%"PRIx64
+ " dx=%"PRIx64" si=%"PRIx64" di=%"PRIx64, rc, ecode,
+ exit_qualification, 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, regs->error_code);
+ }
+}
+
static int vmx_handle_apic_write(void)
{
unsigned long exit_qualification;
@@ -2675,6 +2721,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);
@@ -2790,6 +2847,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..ea2c5a9
--- /dev/null
+++ b/xen/include/asm-x86/hvm/vmport.h
@@ -0,0 +1,61 @@
+/*
+ * 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);
+
+#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
next prev parent reply other threads:[~2014-09-11 18:36 UTC|newest]
Thread overview: 51+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-09-11 18:36 [PATCH v4 00/16] Xen VMware tools support Don Slutz
2014-09-11 18:36 ` [PATCH v4 01/16] xen: Add support for VMware cpuid leaves Don Slutz
2014-09-11 19:49 ` Andrew Cooper
2014-09-12 9:49 ` Jan Beulich
2014-09-12 17:46 ` Don Slutz
2014-09-15 7:42 ` Jan Beulich
2014-09-17 15:41 ` Don Slutz
2014-09-12 21:26 ` Don Slutz
2014-09-12 12:37 ` Boris Ostrovsky
2014-09-12 17:50 ` Don Slutz
2014-09-11 18:36 ` [PATCH v4 02/16] tools: Add vmware_hw support Don Slutz
2014-09-11 21:09 ` Andrew Cooper
2014-09-16 16:20 ` Don Slutz
2014-09-11 18:36 ` [PATCH v4 03/16] vmware: Add VMware provided include files Don Slutz
2014-09-11 18:36 ` Don Slutz [this message]
2014-09-11 21:22 ` [PATCH v4 04/16] xen: Add is_vmware_port_enabled Andrew Cooper
2014-09-16 16:20 ` Don Slutz
2014-09-12 13:08 ` Boris Ostrovsky
2014-09-16 12:08 ` Slutz, Donald Christopher
2014-09-17 15:56 ` Boris Ostrovsky
2014-09-17 18:23 ` Slutz, Donald Christopher
2014-09-18 9:14 ` Jan Beulich
2014-09-19 12:48 ` Slutz, Donald Christopher
2014-09-18 22:53 ` Boris Ostrovsky
2014-09-19 13:24 ` Slutz, Donald Christopher
2014-09-19 15:50 ` Boris Ostrovsky
2014-09-19 17:00 ` Slutz, Donald Christopher
2014-09-11 18:36 ` [PATCH v4 05/16] tools: Add vmware_port support Don Slutz
2014-09-11 18:36 ` [PATCH v4 06/16] xen: Convert vmware_port to xentrace usage Don Slutz
2014-09-12 13:10 ` Boris Ostrovsky
2014-09-12 23:57 ` Don Slutz
2014-09-11 18:36 ` [PATCH v4 07/16] tools: " Don Slutz
2014-09-12 13:15 ` Boris Ostrovsky
2014-09-13 0:01 ` Don Slutz
2014-09-11 18:36 ` [PATCH v4 08/16] xen: Add limited support of VMware's hyper-call rpc Don Slutz
2014-09-12 13:37 ` Boris Ostrovsky
2014-09-12 14:27 ` Jan Beulich
2014-09-16 12:38 ` Slutz, Donald Christopher
2014-09-16 12:46 ` Jan Beulich
2014-09-16 13:47 ` Slutz, Donald Christopher
2014-09-16 13:17 ` Slutz, Donald Christopher
2014-09-11 18:36 ` [PATCH v4 09/16] tools: " Don Slutz
2014-09-11 18:36 ` [PATCH v4 10/16] Add VMware tool's triggers Don Slutz
2014-09-11 18:36 ` [PATCH v4 11/16] Add live migration of VMware's hyper-call RPC Don Slutz
2014-09-12 13:54 ` Boris Ostrovsky
2014-09-16 15:48 ` Don Slutz
2014-09-11 18:36 ` [PATCH v4 12/16] Add dump of HVM_SAVE_CODE(VMPORT) to xen-hvmctx Don Slutz
2014-09-11 18:36 ` [PATCH v4 13/16] Add xen-hvm-param Don Slutz
2014-09-11 18:36 ` [PATCH v4 14/16] Add xen-vmware-guestinfo Don Slutz
2014-09-11 18:36 ` [PATCH v4 15/16] Add xen-list-vmware-guestinfo Don Slutz
2014-09-11 18:36 ` [PATCH v4 16/16] Add xen-hvm-send-trigger Don Slutz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1410460610-14759-5-git-send-email-dslutz@verizon.com \
--to=dslutz@verizon.com \
--cc=Aravind.Gopalakrishnan@amd.com \
--cc=George.Dunlap@eu.citrix.com \
--cc=andrew.cooper3@citrix.com \
--cc=boris.ostrovsky@oracle.com \
--cc=eddie.dong@intel.com \
--cc=ian.campbell@citrix.com \
--cc=ian.jackson@eu.citrix.com \
--cc=jbeulich@suse.com \
--cc=jun.nakajima@intel.com \
--cc=keir@xen.org \
--cc=kevin.tian@intel.com \
--cc=stefano.stabellini@eu.citrix.com \
--cc=suravee.suthikulpanit@amd.com \
--cc=tim@xen.org \
--cc=xen-devel@lists.xen.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).