Index: /trunk/include/VBox/err.h
===================================================================
--- /trunk/include/VBox/err.h	(revision 55036)
+++ /trunk/include/VBox/err.h	(revision 55037)
@@ -2622,6 +2622,4 @@
 /** The GIM device is not registered with GIM when it ought to be. */
 #define VERR_GIM_DEVICE_NOT_REGISTERED              (-6310)
-/** An invalid Guest OS identifier was specified for the GIM provider. */
-#define VERR_GIM_INVALID_GUESTOS_ID                 (-6311)
 /** @} */
 
Index: /trunk/include/VBox/vmm/gim.h
===================================================================
--- /trunk/include/VBox/vmm/gim.h	(revision 55036)
+++ /trunk/include/VBox/vmm/gim.h	(revision 55037)
@@ -98,5 +98,4 @@
 AssertCompileMemberAlignment(GIMMMIO2REGION, pvPageR0, 8);
 
-
 #if 0
 /**
@@ -175,7 +174,8 @@
 VMM_INT_DECL(bool)          GIMAreHypercallsEnabled(PVMCPU pVCpu);
 VMM_INT_DECL(int)           GIMHypercall(PVMCPU pVCpu, PCPUMCTX pCtx);
+VMM_INT_DECL(int)           GIMXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx);
+VMM_INT_DECL(bool)          GIMShouldTrapXcptUD(PVM pVM);
 VMM_INT_DECL(VBOXSTRICTRC)  GIMReadMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue);
 VMM_INT_DECL(VBOXSTRICTRC)  GIMWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uValue, uint64_t uRawValue);
-
 /** @} */
 
Index: /trunk/src/VBox/VMM/VMMAll/GIMAll.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/GIMAll.cpp	(revision 55036)
+++ /trunk/src/VBox/VMM/VMMAll/GIMAll.cpp	(revision 55037)
@@ -73,4 +73,7 @@
             return gimHvAreHypercallsEnabled(pVCpu);
 
+        case GIMPROVIDERID_KVM:
+            return gimKvmAreHypercallsEnabled(pVCpu);
+
         default:
             return false;
@@ -99,7 +102,10 @@
             return gimHvHypercall(pVCpu, pCtx);
 
-        default:
-            AssertMsgFailed(("GIMHypercall: for unknown provider %u\n", pVM->gim.s.enmProviderId));
-            return VERR_GIM_IPE_3;
+        case GIMPROVIDERID_KVM:
+            return gimKvmHypercall(pVCpu, pCtx);
+
+        default:
+            AssertMsgFailed(("GIMHypercall: for provider %u not available/implemented\n", pVM->gim.s.enmProviderId));
+            return VERR_GIM_HYPERCALLS_NOT_AVAILABLE;
     }
 }
@@ -127,4 +133,54 @@
     }
     return false;
+}
+
+
+/**
+ * Whether #UD exceptions in the guest needs to be intercepted by the GIM
+ * provider.
+ *
+ * At the moment, the reason why this isn't a more generic interface wrt to
+ * exceptions is because of performance (each VM-exit would have to manually
+ * check whether or not GIM needs to be notified). Left as a todo for later if
+ * really required.
+ *
+ * @returns true if needed, false otherwise.
+ * @param   pVM         Pointer to the VM.
+ */
+VMM_INT_DECL(bool) GIMShouldTrapXcptUD(PVM pVM)
+{
+    if (!GIMIsEnabled(pVM))
+        return 0;
+
+    switch (pVM->gim.s.enmProviderId)
+    {
+        case GIMPROVIDERID_KVM:
+            return gimKvmShouldTrapXcptUD(pVM);
+
+        default:
+            return 0;
+    }
+}
+
+
+/**
+ * Exception handler for #UD when requested by the GIM provider.
+ *
+ * @param   pVCpu       Pointer to the VMCPU.
+ * @param   pCtx        Pointer to the guest-CPU context.
+ */
+VMM_INT_DECL(int) GIMXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx)
+{
+    PVM pVM = pVCpu->CTX_SUFF(pVM);
+    Assert(GIMIsEnabled(pVM));
+
+    switch (pVM->gim.s.enmProviderId)
+    {
+        case GIMPROVIDERID_KVM:
+            return gimKvmXcptUD(pVCpu, pCtx);
+
+        default:
+            return VERR_GIM_OPERATION_FAILED;
+    }
 }
 
Index: /trunk/src/VBox/VMM/VMMAll/GIMAllKvm.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/GIMAllKvm.cpp	(revision 55036)
+++ /trunk/src/VBox/VMM/VMMAll/GIMAllKvm.cpp	(revision 55037)
@@ -24,5 +24,7 @@
 
 #include <VBox/err.h>
+#include <VBox/dis.h>
 #include <VBox/vmm/hm.h>
+#include <VBox/vmm/em.h>
 #include <VBox/vmm/tm.h>
 #include <VBox/vmm/vm.h>
@@ -30,4 +32,5 @@
 #include <VBox/vmm/pdmdev.h>
 #include <VBox/vmm/pdmapi.h>
+#include <VBox/sup.h>
 
 #include <iprt/asm-amd64-x86.h>
@@ -43,7 +46,75 @@
 VMM_INT_DECL(int) gimKvmHypercall(PVMCPU pVCpu, PCPUMCTX pCtx)
 {
-    PVM pVM = pVCpu->CTX_SUFF(pVM);
-    /** @todo Handle hypercalls. Fail for now */
-    return VERR_GIM_IPE_3;
+    /*
+     * Get the hypercall operation and arguments.
+     */
+    bool const fIs64BitMode = CPUMIsGuestIn64BitCodeEx(pCtx);
+    uint64_t uHyperOp       = pCtx->rax;
+    uint64_t uHyperArg0     = pCtx->rbx;
+    uint64_t uHyperArg1     = pCtx->rcx;
+    uint64_t uHyperArg2     = pCtx->rdi;
+    uint64_t uHyperArg3     = pCtx->rsi;
+    uint64_t uHyperRet      = KVM_HYPERCALL_RET_ENOSYS;
+    uint64_t uAndMask       = UINT64_C(0xffffffffffffffff);
+    if (!fIs64BitMode)
+    {
+        uAndMask    = UINT64_C(0xffffffff);
+        uHyperOp   &= UINT64_C(0xffffffff);
+        uHyperArg0 &= UINT64_C(0xffffffff);
+        uHyperArg1 &= UINT64_C(0xffffffff);
+        uHyperArg2 &= UINT64_C(0xffffffff);
+        uHyperArg3 &= UINT64_C(0xffffffff);
+        uHyperRet  &= UINT64_C(0xffffffff);
+    }
+
+    /*
+     * Verify that guest ring-0 is the one making the hypercall.
+     */
+    uint32_t uCpl = CPUMGetGuestCPL(pVCpu);
+    if (uCpl)
+    {
+        pCtx->rax = KVM_HYPERCALL_RET_EPERM & uAndMask;
+        return VINF_SUCCESS;
+    }
+
+    /*
+     * Do the work.
+     */
+    switch (uHyperOp)
+    {
+        case KVM_HYPERCALL_OP_KICK_CPU:
+        {
+            PVM pVM = pVCpu->CTX_SUFF(pVM);
+            if (uHyperArg1 < pVM->cCpus)
+            {
+                PVMCPU pVCpuTarget = &pVM->aCpus[uHyperArg1];   /** ASSUMES pVCpu index == ApicId of the VCPU. */
+                VMCPU_FF_SET(pVCpuTarget, VMCPU_FF_UNHALT);
+#ifdef IN_RING0
+                GVMMR0SchedWakeUp(pVM, pVCpuTarget->idCpu);
+#elif defined(IN_RING3)
+                int rc2 = SUPR3CallVMMR0(pVM->pVMR0, pVCpuTarget->idCpu, VMMR0_DO_GVMM_SCHED_WAKE_UP, NULL);
+                AssertRC(rc2);
+#endif
+                uHyperRet = KVM_HYPERCALL_RET_SUCCESS;
+            }
+            break;
+        }
+
+        case KVM_HYPERCALL_OP_VAPIC_POLL_IRQ:
+            uHyperRet = KVM_HYPERCALL_RET_SUCCESS;
+            break;
+
+        default:
+            break;
+    }
+
+    /*
+     * Place the result in rax/eax.
+     */
+    if (fIs64BitMode)
+        pCtx->rax = uHyperRet;
+    else
+        pCtx->eax = uHyperRet & uAndMask;
+    return VINF_SUCCESS;
 }
 
@@ -231,2 +302,72 @@
 }
 
+
+/**
+ * Whether we need to trap #UD exceptions in the guest.
+ *
+ * On AMD-V we need to trap them because paravirtualized Linux/KVM guests use
+ * the Intel VMCALL instruction to make hypercalls and we need to trap and
+ * optionally patch them to the AMD-V VMMCALL instruction and handle the
+ * hypercall.
+ *
+ * I guess this was done so that guest teleporation between an AMD and an Intel
+ * machine would working without any changes at the time of teleporation.
+ * However, this also means we -always- need to intercept #UD exceptions on one
+ * of the two CPU models (Intel or AMD). Hyper-V solves this problem more
+ * elegantly by letting the hypervisor supply an opaque hypercall page.
+ *
+ * @param   pVM         Pointer to the VM.
+ */
+VMM_INT_DECL(bool) gimKvmShouldTrapXcptUD(PVM pVM)
+{
+    pVM->gim.s.u.Kvm.fTrapXcptUD = ASMIsAmdCpu();
+    return pVM->gim.s.u.Kvm.fTrapXcptUD;
+}
+
+
+/**
+ * Exception handler for #UD.
+ *
+ * @param   pVCpu       Pointer to the VMCPU.
+ * @param   pCtx        Pointer to the guest-CPU context.
+ */
+VMM_INT_DECL(int) gimKvmXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx)
+{
+    /*
+     * If we didn't ask for #UD to be trapped, bail.
+     */
+    PVM pVM = pVCpu->CTX_SUFF(pVM);
+    if (RT_UNLIKELY(!pVM->gim.s.u.Kvm.fTrapXcptUD))
+        return VERR_GIM_OPERATION_FAILED;
+
+    /*
+     * Disassemble the instruction at RIP to figure out if it's the Intel
+     * VMCALL instruction and if so, handle it as a hypercall.
+     */
+    DISCPUSTATE Dis;
+    unsigned    cbInstr;
+    int rc = EMInterpretDisasCurrent(pVM, pVCpu, &Dis, &cbInstr);
+    if (RT_SUCCESS(rc))
+    {
+        if (Dis.pCurInstr->uOpcode == OP_VMCALL)
+        {
+            Assert(cbInstr == 3);        /* paranoia. */
+
+            /*
+             * Patch the instruction to so we don't have to spend time disassembling it each time.
+             */
+            static uint8_t s_abHypercall[3];
+            size_t cbWritten;
+            rc = HMPatchHypercall(pVM, &s_abHypercall, sizeof(s_abHypercall), &cbWritten);
+            AssertRC(rc);
+
+            if (RT_LIKELY(cbWritten == cbInstr))
+                rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->rip, &s_abHypercall, sizeof(s_abHypercall));
+            if (RT_SUCCESS(rc))
+                return gimKvmHypercall(pVCpu, pCtx);
+        }
+    }
+
+    return VERR_GIM_OPERATION_FAILED;
+}
+
Index: /trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp	(revision 55036)
+++ /trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp	(revision 55037)
@@ -296,4 +296,5 @@
 static FNSVMEXITHANDLER hmR0SvmExitXcptPF;
 static FNSVMEXITHANDLER hmR0SvmExitXcptNM;
+static FNSVMEXITHANDLER hmR0SvmExitXcptUD;
 static FNSVMEXITHANDLER hmR0SvmExitXcptMF;
 static FNSVMEXITHANDLER hmR0SvmExitXcptDB;
@@ -669,4 +670,6 @@
     Assert(pVM->hm.s.svm.fSupported);
 
+    pVM->hm.s.fTrapXcptUD             = GIMShouldTrapXcptUD(pVM);
+    uint32_t const fGimXcptIntercepts = pVM->hm.s.fTrapXcptUD ? RT_BIT(X86_XCPT_UD) : 0;
     for (VMCPUID i = 0; i < pVM->cCpus; i++)
     {
@@ -783,4 +786,7 @@
         pVmcb->ctrl.u32InterceptCtrl1 |= SVM_CTRL1_INTERCEPT_TASK_SWITCH;
 #endif
+
+        /* Apply the exceptions intercepts needed by the GIM provider. */
+        pVmcb->ctrl.u32InterceptException |= fGimXcptIntercepts;
 
         /*
@@ -3476,4 +3482,7 @@
             return hmR0SvmExitXcptNM(pVCpu, pCtx, pSvmTransient);
 
+        case SVM_EXIT_EXCEPTION_6:  /* X86_XCPT_UD */
+            return hmR0SvmExitXcptUD(pVCpu, pCtx, pSvmTransient);
+
         case SVM_EXIT_EXCEPTION_10:  /* X86_XCPT_MF */
             return hmR0SvmExitXcptMF(pVCpu, pCtx, pSvmTransient);
@@ -3582,5 +3591,5 @@
                 case SVM_EXIT_EXCEPTION_4:             /* X86_XCPT_OF */
                 case SVM_EXIT_EXCEPTION_5:             /* X86_XCPT_BR */
-                case SVM_EXIT_EXCEPTION_6:             /* X86_XCPT_UD */
+                /* case SVM_EXIT_EXCEPTION_6: */       /* X86_XCPT_UD - Handled above. */
                 /*   SVM_EXIT_EXCEPTION_7: */          /* X86_XCPT_NM - Handled above. */
                 case SVM_EXIT_EXCEPTION_8:             /* X86_XCPT_DF */
@@ -3619,8 +3628,4 @@
                             /** @todo Investigate this later. */
                             STAM_COUNTER_INC(&pVCpu->hm.s.StatExitGuestBP);
-                            break;
-
-                        case X86_XCPT_UD:
-                            STAM_COUNTER_INC(&pVCpu->hm.s.StatExitGuestUD);
                             break;
 
@@ -4152,4 +4157,5 @@
     {
         PSVMVMCB pVmcb = (PSVMVMCB)pVCpu->hm.s.svm.pvVmcb;
+        Assert(pVmcb->ctrl.u64NextRIP - pCtx->rip == cb);
         pCtx->rip = pVmcb->ctrl.u64NextRIP;
     }
@@ -5003,7 +5009,12 @@
         if (GIMAreHypercallsEnabled(pVCpu))
             rc = GIMHypercall(pVCpu, pCtx);
-    }
-
-    if (rc != VINF_SUCCESS)
+
+        /* If the hypercall changes anything other than guest general-purpose registers,
+           we would need to reload the guest changed bits on VM-reentry. */
+    }
+
+    if (RT_SUCCESS(rc))
+        hmR0SvmUpdateRip(pVCpu, pCtx, 3);
+    else
         hmR0SvmSetPendingXcptUD(pVCpu);
     return VINF_SUCCESS;
@@ -5202,4 +5213,35 @@
 
 /**
+ * #VMEXIT handler for undefined opcode (SVM_EXIT_EXCEPTION_6).
+ * Conditional #VMEXIT.
+ */
+HMSVM_EXIT_DECL hmR0SvmExitXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx, PSVMTRANSIENT pSvmTransient)
+{
+    HMSVM_VALIDATE_EXIT_HANDLER_PARAMS();
+
+    HMSVM_CHECK_EXIT_DUE_TO_EVENT_DELIVERY();
+
+    STAM_COUNTER_INC(&pVCpu->hm.s.StatExitGuestUD);
+
+    PVM pVM = pVCpu->CTX_SUFF(pVM);
+    if (   pVM->hm.s.fTrapXcptUD
+        && GIMAreHypercallsEnabled(pVCpu))
+    {
+        int rc = GIMXcptUD(pVCpu, pCtx);
+        if (RT_SUCCESS(rc))
+        {
+            /* If the exception handler changes anything other than guest general-purpose registers,
+               we would need to reload the guest changed bits on VM-reentry. */
+            hmR0SvmUpdateRip(pVCpu, pCtx, 3);
+            return VINF_SUCCESS;
+        }
+    }
+
+    hmR0SvmSetPendingXcptUD(pVCpu);
+    return VINF_SUCCESS;
+}
+
+
+/**
  * #VMEXIT handler for math-fault exceptions (SVM_EXIT_EXCEPTION_10).
  * Conditional #VMEXIT.
Index: /trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 55036)
+++ /trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 55037)
@@ -7532,5 +7532,5 @@
         else if (uIntType == VMX_EXIT_INTERRUPTION_INFO_TYPE_NMI)
         {
-            bool fBlockNmi = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI);
+            bool const fBlockNmi = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI);
             Assert(!fBlockSti);
             Assert(!fBlockMovSS);
@@ -10250,5 +10250,14 @@
         rc = GIMHypercall(pVCpu, pMixedCtx);
     }
-    if (rc != VINF_SUCCESS)
+
+    if (RT_SUCCESS(rc))
+    {
+        rc = hmR0VmxAdvanceGuestRip(pVCpu, pMixedCtx, pVmxTransient);
+        Assert(pVmxTransient->cbInstr == 3);
+
+        /* If the hypercall changes anything other than guest general-purpose registers,
+           we would need to reload the guest changed bits on VM-reentry. */
+    }
+    else
     {
         hmR0VmxSetPendingXcptUD(pVCpu, pMixedCtx);
Index: /trunk/src/VBox/VMM/VMMR3/GIMKvm.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/GIMKvm.cpp	(revision 55036)
+++ /trunk/src/VBox/VMM/VMMR3/GIMKvm.cpp	(revision 55037)
@@ -98,5 +98,5 @@
                         //| GIM_KVM_BASE_FEAT_STEAL_TIME
                         //| GIM_KVM_BASE_FEAT_PV_EOI
-                        //| GIM_KVM_BASE_FEAT_UNHALT
+                        | GIM_KVM_BASE_FEAT_PV_UNHALT
                         ;
         /* Rest of the features are determined in gimR3KvmInitCompleted(). */
Index: /trunk/src/VBox/VMM/include/GIMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/GIMInternal.h	(revision 55036)
+++ /trunk/src/VBox/VMM/include/GIMInternal.h	(revision 55037)
@@ -73,5 +73,5 @@
     union
     {
-        GIMHV Hv;
+        GIMHV  Hv;
         GIMKVM Kvm;
     } u;
Index: /trunk/src/VBox/VMM/include/GIMKvmInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/GIMKvmInternal.h	(revision 55036)
+++ /trunk/src/VBox/VMM/include/GIMKvmInternal.h	(revision 55037)
@@ -117,7 +117,25 @@
  */
 /** Guest-physical address of the wall-clock struct. */
-#define MSR_GIM_KVM_WALL_CLOCK_GUEST_GPA(a)      (a)
-/** @} */
-
+#define MSR_GIM_KVM_WALL_CLOCK_GUEST_GPA(a)        (a)
+/** @} */
+
+
+/** @name KVM Hypercall operations.
+ *  @{ */
+#define KVM_HYPERCALL_OP_VAPIC_POLL_IRQ            1
+#define KVM_HYPERCALL_OP_MMU                       2
+#define KVM_HYPERCALL_OP_FEATURES                  3
+#define KVM_HYPERCALL_OP_KICK_CPU                  5
+/** @} */
+
+/** @name KVM Hypercall return values.
+ *  @{ */
+/* Return values for hypercalls */
+#define KVM_HYPERCALL_RET_SUCCESS                  0
+#define KVM_HYPERCALL_RET_ENOSYS                   (uint64_t)(-1000)
+#define KVM_HYPERCALL_RET_EFAULT                   (uint64_t)(-14)
+#define KVM_HYPERCALL_RET_E2BIG                    (uint64_t)(-7)
+#define KVM_HYPERCALL_RET_EPERM                    (uint64_t)(-1)
+/** @} */
 
 /**
@@ -184,4 +202,7 @@
     uint32_t                    uBaseFeat;
     /** @} */
+
+    /** Whether we need to trap #UD exceptions. */
+    bool                        fTrapXcptUD;
 } GIMKVM;
 /** Pointer to per-VM GIM KVM instance data. */
@@ -242,4 +263,7 @@
 VMM_INT_DECL(VBOXSTRICTRC)      gimKvmReadMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue);
 VMM_INT_DECL(VBOXSTRICTRC)      gimKvmWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uRawValue);
+VMM_INT_DECL(bool)              gimKvmShouldTrapXcptUD(PVM pVM);
+VMM_INT_DECL(int)               gimKvmXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx);
+
 
 RT_C_DECLS_END
Index: /trunk/src/VBox/VMM/include/HMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/HMInternal.h	(revision 55036)
+++ /trunk/src/VBox/VMM/include/HMInternal.h	(revision 55037)
@@ -343,5 +343,7 @@
     /** Set when TPR patching is active. */
     bool                        fTPRPatchingActive;
-    bool                        u8Alignment[3];
+    /** Whether #UD needs to be intercepted (required by certain GIM providers). */
+    bool                        fTrapXcptUD;
+    bool                        u8Alignment[2];
 
     /** Host kernel flags that HM might need to know (SUPKERNELFEATURES_XXX). */
