Index: trunk/src/VBox/VMM/VMMAll/IEMAll.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/IEMAll.cpp	(revision 74660)
+++ trunk/src/VBox/VMM/VMMAll/IEMAll.cpp	(revision 74661)
@@ -14968,9 +14968,11 @@
  * @param   cbInstr     The instruction length in bytes.
  * @param   u16Port     The port to read.
+ * @param   fImm        Whether the port is specified using an immediate operand or
+ *                      using the implicit DX register.
  * @param   cbReg       The register size.
  *
  * @remarks In ring-0 not all of the state needs to be synced in.
  */
-VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedOut(PVMCPU pVCpu, uint8_t cbInstr, uint16_t u16Port, uint8_t cbReg)
+VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedOut(PVMCPU pVCpu, uint8_t cbInstr, uint16_t u16Port, bool fImm, uint8_t cbReg)
 {
     IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 1);
@@ -14978,5 +14980,5 @@
 
     iemInitExec(pVCpu, false /*fBypassHandlers*/);
-    VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_2(iemCImpl_out, u16Port, cbReg);
+    VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_3(iemCImpl_out, u16Port, fImm, cbReg);
     Assert(!pVCpu->iem.s.cActiveMappings);
     return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
@@ -14991,7 +14993,9 @@
  * @param   cbInstr     The instruction length in bytes.
  * @param   u16Port     The port to read.
+ * @param   fImm        Whether the port is specified using an immediate operand or
+ *                      using the implicit DX.
  * @param   cbReg       The register size.
  */
-VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedIn(PVMCPU pVCpu, uint8_t cbInstr, uint16_t u16Port, uint8_t cbReg)
+VMM_INT_DECL(VBOXSTRICTRC) IEMExecDecodedIn(PVMCPU pVCpu, uint8_t cbInstr, uint16_t u16Port, bool fImm, uint8_t cbReg)
 {
     IEMEXEC_ASSERT_INSTR_LEN_RETURN(cbInstr, 1);
@@ -14999,5 +15003,5 @@
 
     iemInitExec(pVCpu, false /*fBypassHandlers*/);
-    VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_2(iemCImpl_in, u16Port, cbReg);
+    VBOXSTRICTRC rcStrict = IEM_CIMPL_CALL_3(iemCImpl_in, u16Port, fImm, cbReg);
     Assert(!pVCpu->iem.s.cActiveMappings);
     return iemUninitExecAndFiddleStatusAndMaybeReenter(pVCpu, rcStrict);
Index: trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h
===================================================================
--- trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h	(revision 74660)
+++ trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h	(revision 74661)
@@ -6677,7 +6677,9 @@
  *
  * @param   u16Port     The source port.
+ * @param   fImm        Whether the port was specified through an immediate operand
+ *                      or the implicit DX register.
  * @param   cbReg       The register size.
  */
-IEM_CIMPL_DEF_2(iemCImpl_in, uint16_t, u16Port, uint8_t, cbReg)
+IEM_CIMPL_DEF_3(iemCImpl_in, uint16_t, u16Port, bool, fImm, uint8_t, cbReg)
 {
     /*
@@ -6687,4 +6689,18 @@
     if (rcStrict != VINF_SUCCESS)
         return rcStrict;
+
+    /*
+     * Check VMX nested-guest IO intercept.
+     */
+#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
+    if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
+    {
+        rcStrict = iemVmxVmexitInstrIo(pVCpu, VMXINSTRID_IO_IN, u16Port, fImm, cbReg, cbInstr);
+        if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
+            return rcStrict;
+    }
+#else
+    RT_NOREF(fImm);
+#endif
 
     /*
@@ -6762,5 +6778,5 @@
 IEM_CIMPL_DEF_1(iemCImpl_in_eAX_DX, uint8_t, cbReg)
 {
-    return IEM_CIMPL_CALL_2(iemCImpl_in, pVCpu->cpum.GstCtx.dx, cbReg);
+    return IEM_CIMPL_CALL_3(iemCImpl_in, pVCpu->cpum.GstCtx.dx, false /* fImm */, cbReg);
 }
 
@@ -6770,7 +6786,9 @@
  *
  * @param   u16Port     The destination port.
+ * @param   fImm        Whether the port was specified through an immediate operand
+ *                      or the implicit DX register.
  * @param   cbReg       The register size.
  */
-IEM_CIMPL_DEF_2(iemCImpl_out, uint16_t, u16Port, uint8_t, cbReg)
+IEM_CIMPL_DEF_3(iemCImpl_out, uint16_t, u16Port, bool, fImm, uint8_t, cbReg)
 {
     /*
@@ -6782,5 +6800,19 @@
 
     /*
-     * Check SVM nested-guest IO intercept.
+     * Check VMX nested-guest I/O intercept.
+     */
+#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
+    if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
+    {
+        rcStrict = iemVmxVmexitInstrIo(pVCpu, VMXINSTRID_IO_OUT, u16Port, fImm, cbReg, cbInstr);
+        if (rcStrict != VINF_VMX_INTERCEPT_NOT_ACTIVE)
+            return rcStrict;
+    }
+#else
+    RT_NOREF(fImm);
+#endif
+
+    /*
+     * Check SVM nested-guest I/O intercept.
      */
 #ifdef VBOX_WITH_NESTED_HWVIRT_SVM
@@ -6854,5 +6886,5 @@
 IEM_CIMPL_DEF_1(iemCImpl_out_DX_eAX, uint8_t, cbReg)
 {
-    return IEM_CIMPL_CALL_2(iemCImpl_out, pVCpu->cpum.GstCtx.dx, cbReg);
+    return IEM_CIMPL_CALL_3(iemCImpl_out, pVCpu->cpum.GstCtx.dx, false /* fImm */, cbReg);
 }
 
Index: trunk/src/VBox/VMM/VMMAll/IEMAllCImplVmxInstr.cpp.h
===================================================================
--- trunk/src/VBox/VMM/VMMAll/IEMAllCImplVmxInstr.cpp.h	(revision 74660)
+++ trunk/src/VBox/VMM/VMMAll/IEMAllCImplVmxInstr.cpp.h	(revision 74661)
@@ -2866,7 +2866,7 @@
  * @returns @c true if the instruction is intercepted, @c false otherwise.
  * @param   pVCpu           The cross context virtual CPU structure.
- * @param   uPort           The I/O port being accessed by the instruction.
- */
-IEM_STATIC bool iemVmxIsIoInterceptSet(PVMCPU pVCpu, uint16_t uPort)
+ * @param   u16Port         The I/O port being accessed by the instruction.
+ */
+IEM_STATIC bool iemVmxIsIoInterceptSet(PVMCPU pVCpu, uint16_t u16Port)
 {
     PCVMXVVMCS pVmcs = pVCpu->cpum.GstCtx.hwvirt.vmx.CTX_SUFF(pVmcs);
@@ -2874,5 +2874,5 @@
 
     /*
-     * Check whether the IO instruction must cause a VM-exit or not.
+     * Check whether the I/O instruction must cause a VM-exit or not.
      * See Intel spec. 25.1.3 "Instructions That Cause VM Exits Conditionally".
      */
@@ -2886,5 +2886,5 @@
         Assert(pbIoBitmapA);
         Assert(pbIoBitmapB);
-        return HMVmxGetIoBitmapPermission(pbIoBitmapA, pbIoBitmapB, uPort);
+        return HMVmxGetIoBitmapPermission(pbIoBitmapA, pbIoBitmapB, u16Port);
     }
 
@@ -3285,4 +3285,46 @@
                          | RT_BF_MAKE(VMX_BF_EXIT_QUAL_CRX_ACCESS,   VMX_EXIT_QUAL_CRX_ACCESS_WRITE)
                          | RT_BF_MAKE(VMX_BF_EXIT_QUAL_CRX_GENREG,   iGReg);
+        return iemVmxVmexitInstrWithInfo(pVCpu, &ExitInfo);
+    }
+
+    return VINF_VMX_INTERCEPT_NOT_ACTIVE;
+}
+
+
+/**
+ * VMX VM-exit handler for VM-exits due to I/O instructions (IN and OUT).
+ *
+ * @returns VBox strict status code.
+ * @param   pVCpu       The cross context virtual CPU structure.
+ * @param   uInstrId    The VM-exit instruction identity (VMXINSTRID_IO_IN or
+ *                      VMXINSTRID_IO_OUT).
+ * @param   u16Port     The I/O port being accessed.
+ * @param   fImm        Whether the I/O port was encoded using an immediate operand
+ *                      or the implicit DX register.
+ * @param   cbAccess    The size of the I/O access in bytes (1, 2 or 4 bytes).
+ * @param   cbInstr     The instruction length in bytes.
+ */
+IEM_STATIC VBOXSTRICTRC iemVmxVmexitInstrIo(PVMCPU pVCpu, VMXINSTRID uInstrId, uint16_t u16Port, bool fImm, uint8_t cbAccess,
+                                            uint8_t cbInstr)
+{
+    Assert(uInstrId == VMXINSTRID_IO_IN || uInstrId == VMXINSTRID_IO_OUT);
+    Assert(cbAccess == 1 || cbAccess == 2 || cbAccess == 4);
+
+    PCVMXVVMCS pVmcs = pVCpu->cpum.GstCtx.hwvirt.vmx.CTX_SUFF(pVmcs);
+    Assert(pVmcs);
+
+    bool const fIntercept = iemVmxIsIoInterceptSet(pVCpu, u16Port);
+    if (fIntercept)
+    {
+        uint32_t const uDirection = uInstrId == VMXINSTRID_IO_IN ? VMX_EXIT_QUAL_IO_DIRECTION_IN
+                                                                 : VMX_EXIT_QUAL_IO_DIRECTION_OUT;
+        VMXVEXITINFO ExitInfo;
+        RT_ZERO(ExitInfo);
+        ExitInfo.uReason = VMX_EXIT_MOV_CRX;
+        ExitInfo.cbInstr = cbInstr;
+        ExitInfo.u64Qual = RT_BF_MAKE(VMX_BF_EXIT_QUAL_IO_WIDTH,     cbAccess - 1)
+                         | RT_BF_MAKE(VMX_BF_EXIT_QUAL_IO_DIRECTION, uDirection)
+                         | RT_BF_MAKE(VMX_BF_EXIT_QUAL_IO_ENCODING,  fImm)
+                         | RT_BF_MAKE(VMX_BF_EXIT_QUAL_IO_PORT,      u16Port);
         return iemVmxVmexitInstrWithInfo(pVCpu, &ExitInfo);
     }
@@ -4727,5 +4769,5 @@
         IEM_VMX_VMENTRY_FAILED_RET(pVCpu, pszInstr, pszFailure, kVmxVDiag_Vmentry_Cr3TargetCount);
 
-    /* IO bitmaps physical addresses. */
+    /* I/O bitmaps physical addresses. */
     if (pVmcs->u32ProcCtls & VMX_PROC_CTLS_USE_IO_BITMAPS)
     {
Index: trunk/src/VBox/VMM/VMMAll/IEMAllInstructionsOneByte.cpp.h
===================================================================
--- trunk/src/VBox/VMM/VMMAll/IEMAllInstructionsOneByte.cpp.h	(revision 74660)
+++ trunk/src/VBox/VMM/VMMAll/IEMAllInstructionsOneByte.cpp.h	(revision 74661)
@@ -10399,5 +10399,5 @@
     uint8_t u8Imm; IEM_OPCODE_GET_NEXT_U8(&u8Imm);
     IEMOP_HLP_DONE_DECODING_NO_LOCK_PREFIX();
-    return IEM_MC_DEFER_TO_CIMPL_2(iemCImpl_in, u8Imm, 1);
+    return IEM_MC_DEFER_TO_CIMPL_3(iemCImpl_in, u8Imm, true /* fImm */, 1);
 }
 
@@ -10409,5 +10409,5 @@
     uint8_t u8Imm; IEM_OPCODE_GET_NEXT_U8(&u8Imm);
     IEMOP_HLP_DONE_DECODING_NO_LOCK_PREFIX();
-    return IEM_MC_DEFER_TO_CIMPL_2(iemCImpl_in, u8Imm, pVCpu->iem.s.enmEffOpSize == IEMMODE_16BIT ? 2 : 4);
+    return IEM_MC_DEFER_TO_CIMPL_3(iemCImpl_in, u8Imm, true /* fImm */, pVCpu->iem.s.enmEffOpSize == IEMMODE_16BIT ? 2 : 4);
 }
 
@@ -10419,5 +10419,5 @@
     uint8_t u8Imm; IEM_OPCODE_GET_NEXT_U8(&u8Imm);
     IEMOP_HLP_DONE_DECODING_NO_LOCK_PREFIX();
-    return IEM_MC_DEFER_TO_CIMPL_2(iemCImpl_out, u8Imm, 1);
+    return IEM_MC_DEFER_TO_CIMPL_3(iemCImpl_out, u8Imm, true /* fImm */, 1);
 }
 
@@ -10429,5 +10429,5 @@
     uint8_t u8Imm; IEM_OPCODE_GET_NEXT_U8(&u8Imm);
     IEMOP_HLP_DONE_DECODING_NO_LOCK_PREFIX();
-    return IEM_MC_DEFER_TO_CIMPL_2(iemCImpl_out, u8Imm, pVCpu->iem.s.enmEffOpSize == IEMMODE_16BIT ? 2 : 4);
+    return IEM_MC_DEFER_TO_CIMPL_3(iemCImpl_out, u8Imm, true /* fImm */, pVCpu->iem.s.enmEffOpSize == IEMMODE_16BIT ? 2 : 4);
 }
 
Index: trunk/src/VBox/VMM/VMMRC/IOMRC.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMRC/IOMRC.cpp	(revision 74660)
+++ trunk/src/VBox/VMM/VMMRC/IOMRC.cpp	(revision 74661)
@@ -84,10 +84,11 @@
     STAM_COUNTER_INC(&pVM->iom.s.StatInstIn); RT_NOREF_PV(pVM);
     Assert(pCpu->Param2.fUse & (DISUSE_IMMEDIATE8 | DISUSE_REG_GEN16));
-    uint16_t u16Port = pCpu->Param2.fUse & DISUSE_REG_GEN16 ? pRegFrame->dx : (uint16_t)pCpu->Param2.uValue;
+    bool const     fUseReg = RT_BOOL(pCpu->Param2.fUse & DISUSE_REG_GEN16);
+    uint16_t const u16Port = fUseReg ? pRegFrame->dx : (uint16_t)pCpu->Param2.uValue;
 
     Assert(pCpu->Param1.fUse & (DISUSE_REG_GEN32 | DISUSE_REG_GEN16 | DISUSE_REG_GEN8));
     uint8_t cbValue = pCpu->Param1.fUse & DISUSE_REG_GEN32 ? 4 : pCpu->Param1.fUse & DISUSE_REG_GEN16 ? 2 : 1;
 
-    return IEMExecDecodedIn(pVCpu, pCpu->cbInstr, u16Port, cbValue);
+    return IEMExecDecodedIn(pVCpu, pCpu->cbInstr, u16Port, !fUseReg, cbValue);
 }
 
@@ -116,10 +117,11 @@
     STAM_COUNTER_INC(&pVM->iom.s.StatInstOut); RT_NOREF_PV(pVM);
     Assert(pCpu->Param1.fUse & (DISUSE_IMMEDIATE8 | DISUSE_REG_GEN16));
-    uint16_t const u16Port = pCpu->Param1.fUse & DISUSE_REG_GEN16 ? pRegFrame->dx : (uint16_t)pCpu->Param1.uValue;
+    bool const     fUseReg = RT_BOOL(pCpu->Param1.fUse & DISUSE_REG_GEN16);
+    uint16_t const u16Port = fUseReg ? pRegFrame->dx : (uint16_t)pCpu->Param1.uValue;
 
     Assert(pCpu->Param2.fUse & (DISUSE_REG_GEN32 | DISUSE_REG_GEN16 | DISUSE_REG_GEN8));
     uint8_t const cbValue = pCpu->Param2.fUse & DISUSE_REG_GEN32 ? 4 : pCpu->Param2.fUse & DISUSE_REG_GEN16 ? 2 : 1;
 
-    return IEMExecDecodedOut(pVCpu, pCpu->cbInstr, u16Port, cbValue);
+    return IEMExecDecodedOut(pVCpu, pCpu->cbInstr, u16Port, !fUseReg, cbValue);
 }
 
