[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v4 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>
---
 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 = &current->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


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.