Index: /trunk/include/VBox/err.h
===================================================================
--- /trunk/include/VBox/err.h	(revision 67986)
+++ /trunk/include/VBox/err.h	(revision 67987)
@@ -91,4 +91,6 @@
 /** This VirtualBox build does not support raw-mode. */
 #define VERR_RAW_MODE_NOT_SUPPORTED         (-1023)
+/** Essential fields in the shared VM structure doesn't match the global one. */
+#define VERR_INCONSISTENT_VM_HANDLE         (-1024)
 /** @} */
 
@@ -2349,4 +2351,6 @@
 /** Internal processing error \#2 in the GVMM code. */
 #define VERR_GVMM_IPE_2                             (-5204)
+/** Cannot destroy VM because not all other EMTs have deregistered. */
+#define VERR_GVMM_NOT_ALL_EMTS_DEREGISTERED         (-5205)
 /** @} */
 
Index: /trunk/include/VBox/vmm/gvmm.h
===================================================================
--- /trunk/include/VBox/vmm/gvmm.h	(revision 67986)
+++ /trunk/include/VBox/vmm/gvmm.h	(revision 67987)
@@ -165,4 +165,5 @@
 GVMMR0DECL(int)     GVMMR0DestroyVM(PGVM pGVM, PVM pVM);
 GVMMR0DECL(int)     GVMMR0RegisterVCpu(PGVM pGVM, PVM pVM, VMCPUID idCpu);
+GVMMR0DECL(int)     GVMMR0DeregisterVCpu(PGVM pGVM, PVM pVM, VMCPUID idCpu);
 GVMMR0DECL(PGVM)    GVMMR0ByHandle(uint32_t hGVM);
 GVMMR0DECL(int)     GVMMR0ByVM(PVM pVM, PGVM *ppGVM);
Index: /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp	(revision 67986)
+++ /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp	(revision 67987)
@@ -362,4 +362,5 @@
 static int gvmmR0ByVMAndEMT(PVM pVM, VMCPUID idCpu, PGVM *ppGVM, PGVMM *ppGVMM);
 static int gvmmR0ByGVMandVM(PGVM pGVM, PVM pVM, PGVMM *ppGVMM, bool fTakeUsedLock);
+static int gvmmR0ByGVMandVMandEMT(PGVM pGVM, PVM pVM, VMCPUID idCpu, PGVMM *ppGVMM);
 
 #ifdef GVMM_SCHED_WITH_PPT
@@ -1184,6 +1185,6 @@
 
     uint32_t        hGVM = pGVM->hSelf;
-    AssertReturn(hGVM != NIL_GVM_HANDLE, VERR_INVALID_HANDLE);
-    AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_HANDLE);
+    AssertReturn(hGVM != NIL_GVM_HANDLE, VERR_INVALID_VM_HANDLE);
+    AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_VM_HANDLE);
 
     PGVMHANDLE      pHandle = &pGVMM->aHandles[hGVM];
@@ -1203,7 +1204,4 @@
     int rc = gvmmR0CreateDestroyLock(pGVMM);
     AssertRC(rc);
-
-/** @todo Check that all other EMTs have said bye-bye. */
-
 
     /* Be careful here because we might theoretically be racing someone else cleaning up. */
@@ -1217,18 +1215,32 @@
         &&  pHandle->pGVM->u32Magic == GVM_MAGIC)
     {
-        void *pvObj = pHandle->pvObj;
-        pHandle->pvObj = NULL;
-        gvmmR0CreateDestroyUnlock(pGVMM);
-
-        for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
-        {
-            /** @todo Can we busy wait here for all thread-context hooks to be
-             *        deregistered before releasing (destroying) it? Only until we find a
-             *        solution for not deregistering hooks everytime we're leaving HMR0
-             *        context. */
-            VMMR0ThreadCtxHookDestroyForEmt(&pVM->aCpus[idCpu]);
-        }
-
-        SUPR0ObjRelease(pvObj, pHandle->pSession);
+        /* Check that other EMTs have deregistered. */
+        uint32_t cNotDeregistered = 0;
+        for (VMCPUID idCpu = 1; idCpu < pGVM->cCpus; idCpu++)
+            cNotDeregistered += pGVM->aCpus[idCpu].hEMT != ~(RTNATIVETHREAD)1; /* see GVMMR0DeregisterVCpu for the value */
+        if (cNotDeregistered == 0)
+        {
+            /* Grab the object pointer. */
+            void *pvObj = pHandle->pvObj;
+            pHandle->pvObj = NULL;
+            gvmmR0CreateDestroyUnlock(pGVMM);
+
+            /** @todo this _must_ be the wrong place.   */
+            for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
+            {
+                /** @todo Can we busy wait here for all thread-context hooks to be
+                 *        deregistered before releasing (destroying) it? Only until we find a
+                 *        solution for not deregistering hooks everytime we're leaving HMR0
+                 *        context. */
+                VMMR0ThreadCtxHookDestroyForEmt(&pVM->aCpus[idCpu]);
+            }
+
+            SUPR0ObjRelease(pvObj, pHandle->pSession);
+        }
+        else
+        {
+            gvmmR0CreateDestroyUnlock(pGVMM);
+            rc = VERR_GVMM_NOT_ALL_EMTS_DEREGISTERED;
+        }
     }
     else
@@ -1444,5 +1456,5 @@
 GVMMR0DECL(int) GVMMR0RegisterVCpu(PGVM pGVM, PVM pVM, VMCPUID idCpu)
 {
-    AssertReturn(idCpu != 0, VERR_NOT_OWNER);
+    AssertReturn(idCpu != 0, VERR_INVALID_FUNCTION);
 
     /*
@@ -1483,4 +1495,55 @@
         else
             rc = VERR_INVALID_CPU_ID;
+    }
+    return rc;
+}
+
+
+/**
+ * Deregisters the calling thread as the EMT of a Virtual CPU.
+ *
+ * Note that VCPU 0 shall call GVMMR0DestroyVM intead of this API.
+ *
+ * @returns VBox status code
+ * @param   pGVM        The global (ring-0) VM structure.
+ * @param   pVM         The cross context VM structure.
+ * @param   idCpu       VCPU id to register the current thread as.
+ */
+GVMMR0DECL(int) GVMMR0DeregisterVCpu(PGVM pGVM, PVM pVM, VMCPUID idCpu)
+{
+    AssertReturn(idCpu != 0, VERR_INVALID_FUNCTION);
+
+    /*
+     * Validate the VM structure, state and handle.
+     */
+    PGVMM pGVMM;
+    int rc = gvmmR0ByGVMandVMandEMT(pGVM, pVM, idCpu, &pGVMM);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Take the destruction lock and recheck the handle state to
+         * prevent racing GVMMR0DestroyVM.
+         */
+        gvmmR0CreateDestroyLock(pGVMM);
+        uint32_t hSelf = pGVM->hSelf;
+        if (   hSelf < RT_ELEMENTS(pGVMM->aHandles)
+            && pGVMM->aHandles[hSelf].pvObj != NULL
+            && pGVMM->aHandles[hSelf].pGVM  == pGVM)
+        {
+            /*
+             * Do per-EMT cleanups.
+             */
+            VMMR0ThreadCtxHookDestroyForEmt(&pVM->aCpus[idCpu]);
+
+            /*
+             * Invalidate hEMT.  We don't use NIL here as that would allow
+             * GVMMR0RegisterVCpu to be called again, and we don't want that.
+             */
+            AssertCompile(~(RTNATIVETHREAD)1 != NIL_RTNATIVETHREAD);
+            pGVM->aCpus[idCpu].hEMT           = ~(RTNATIVETHREAD)1;
+            pVM->aCpus[idCpu].hNativeThreadR0 = NIL_RTNATIVETHREAD;
+        }
+
+        gvmmR0CreateDestroyUnlock(pGVMM);
     }
     return rc;
@@ -1673,5 +1736,5 @@
                 }
             }
-            rc = VERR_INVALID_HANDLE;
+            rc = VERR_INVALID_VM_HANDLE;
         }
         else
@@ -1714,5 +1777,5 @@
  * @thread  EMT
  *
- * @remark  This will assert in all failure paths.
+ * @remarks This will assert in all failure paths.
  */
 static int gvmmR0ByVMAndEMT(PVM pVM, VMCPUID idCpu, PGVM *ppGVM, PGVMM *ppGVMM)
@@ -1752,4 +1815,69 @@
 }
 
+
+/**
+ * Check that the given GVM and VM structures match up.
+ *
+ * The calling thread must be in the same process as the VM. All current lookups
+ * are by threads inside the same process, so this will not be an issue.
+ *
+ * @returns VBox status code.
+ * @param   pGVM            The global (ring-0) VM structure.
+ * @param   pVM             The cross context VM structure.
+ * @param   idCpu           The (alleged) Virtual CPU ID of the calling EMT.
+ * @param   ppGVMM          Where to store the pointer to the GVMM instance data.
+ * @thread  EMT
+ *
+ * @remarks This will assert in all failure paths.
+ */
+static int gvmmR0ByGVMandVMandEMT(PGVM pGVM, PVM pVM, VMCPUID idCpu, PGVMM *ppGVMM)
+{
+    /*
+     * Check the pointers.
+     */
+    AssertPtrReturn(pGVM, VERR_INVALID_POINTER);
+
+    AssertPtrReturn(pVM,  VERR_INVALID_POINTER);
+    AssertReturn(((uintptr_t)pVM & PAGE_OFFSET_MASK) == 0, VERR_INVALID_POINTER);
+    AssertReturn(pGVM->pVM == pVM, VERR_INVALID_VM_HANDLE);
+
+
+    /*
+     * Get the pGVMM instance and check the VM handle.
+     */
+    PGVMM pGVMM;
+    GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
+
+    uint16_t hGVM = pGVM->hSelf;
+    AssertReturn(   hGVM != NIL_GVM_HANDLE
+                 && hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_VM_HANDLE);
+
+    RTPROCESS const pidSelf = RTProcSelf();
+    PGVMHANDLE      pHandle = &pGVMM->aHandles[hGVM];
+    AssertReturn(   pHandle->pGVM   == pGVM
+                 && pHandle->pVM    == pVM
+                 && pHandle->ProcId == pidSelf
+                 && RT_VALID_PTR(pHandle->pvObj),
+                 VERR_INVALID_HANDLE);
+
+    /*
+     * Check the EMT claim.
+     */
+    RTNATIVETHREAD const hAllegedEMT = RTThreadNativeSelf();
+    AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID);
+    AssertReturn(pGVM->aCpus[idCpu].hEMT == hAllegedEMT, VERR_NOT_OWNER);
+
+    /*
+     * Some more VM data consistency checks.
+     */
+    AssertReturn(pVM->cCpus == pGVM->cCpus, VERR_INCONSISTENT_VM_HANDLE);
+    AssertReturn(pVM->hSelf == hGVM, VERR_INCONSISTENT_VM_HANDLE);
+    AssertReturn(pVM->pVMR0 == pVM, VERR_INCONSISTENT_VM_HANDLE);
+    AssertReturn(   pVM->enmVMState >= VMSTATE_CREATING
+                 && pVM->enmVMState <= VMSTATE_TERMINATED, VERR_INCONSISTENT_VM_HANDLE);
+
+    *ppGVMM = pGVMM;
+    return VINF_SUCCESS;
+}
 
 /**
Index: /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 67986)
+++ /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 67987)
@@ -654,4 +654,5 @@
     int rc = RTThreadCtxHookDestroy(pVCpu->vmm.s.hCtxHook);
     AssertRC(rc);
+    pVCpu->vmm.s.hCtxHook = NIL_RTTHREADCTXHOOK;
 }
 
@@ -1436,6 +1437,14 @@
 
         case VMMR0_DO_GVMM_REGISTER_VMCPU:
-            if (pGVM != NULL && pVM != NULL )
+            if (pGVM != NULL && pVM != NULL)
                 rc = GVMMR0RegisterVCpu(pGVM, pVM, idCpu);
+            else
+                rc = VERR_INVALID_PARAMETER;
+            VMM_CHECK_SMAP_CHECK2(pVM, RT_NOTHING);
+            break;
+
+        case VMMR0_DO_GVMM_DEREGISTER_VMCPU:
+            if (pGVM != NULL && pVM != NULL)
+                rc = GVMMR0DeregisterVCpu(pGVM, pVM, idCpu);
             else
                 rc = VERR_INVALID_PARAMETER;
Index: /trunk/src/VBox/VMM/VMMR3/VMEmt.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/VMEmt.cpp	(revision 67986)
+++ /trunk/src/VBox/VMM/VMMR3/VMEmt.cpp	(revision 67987)
@@ -248,4 +248,5 @@
     /*
      * Cleanup and exit.
+     * EMT0 does the VM destruction after all other EMTs have deregistered and terminated.
      */
     Log(("vmR3EmulationThread: Terminating emulation thread! Thread=%#x pUVM=%p rc=%Rrc enmBefore=%d enmVMState=%d\n",
@@ -275,4 +276,11 @@
 
         int rc2 = SUPR3CallVMMR0Ex(pVM->pVMR0, 0 /*idCpu*/, VMMR0_DO_GVMM_DESTROY_VM, 0, NULL);
+        AssertLogRelRC(rc2);
+    }
+    /* Deregister the EMT with VMMR0. */
+    else if (   idCpu != 0
+             && (pVM = pUVM->pVM) != NULL)
+    {
+        int rc2 = SUPR3CallVMMR0Ex(pVM->pVMR0, idCpu, VMMR0_DO_GVMM_DEREGISTER_VMCPU, 0, NULL);
         AssertLogRelRC(rc2);
     }
