Index: /trunk/include/VBox/vmm/vm.h
===================================================================
--- /trunk/include/VBox/vmm/vm.h	(revision 71128)
+++ /trunk/include/VBox/vmm/vm.h	(revision 71129)
@@ -79,4 +79,6 @@
     /** Executing guest code using NEM. */
     VMCPUSTATE_STARTED_EXEC_NEM,
+    VMCPUSTATE_STARTED_EXEC_NEM_WAIT,
+    VMCPUSTATE_STARTED_EXEC_NEM_CANCELED,
     /** Halted. */
     VMCPUSTATE_STARTED_HALTED,
Index: /trunk/include/VBox/vmm/vmapi.h
===================================================================
--- /trunk/include/VBox/vmm/vmapi.h	(revision 71128)
+++ /trunk/include/VBox/vmm/vmapi.h	(revision 71129)
@@ -485,4 +485,5 @@
 VMMR3_INT_DECL(RTCPUID)     VMR3GetVMCPUId(PVM pVM);
 VMMR3_INT_DECL(bool)        VMR3IsLongModeAllowed(PVM pVM);
+VMMR3_INT_DECL(RTTHREAD)    VMR3GetThreadHandle(PUVMCPU pUVCpu);
 VMMR3DECL(RTTHREAD)         VMR3GetVMCPUThread(PUVM pUVM);
 VMMR3DECL(RTNATIVETHREAD)   VMR3GetVMCPUNativeThread(PVM pVM);
Index: /trunk/include/iprt/nt/hyperv.h
===================================================================
--- /trunk/include/iprt/nt/hyperv.h	(revision 71128)
+++ /trunk/include/iprt/nt/hyperv.h	(revision 71129)
@@ -28,4 +28,5 @@
 #define ___iprt_nt_hyperv_h
 
+
 #ifndef IN_IDA_PRO
 # include <iprt/types.h>
@@ -53,4 +54,16 @@
 /** Hyper-V unsigned 128-bit integer type.   */
 typedef struct { uint64_t Low64, High64; } HV_UINT128;
+/** Hyper-V port ID. */
+typedef union
+{
+    uint32_t        AsUINT32;
+    struct
+    {
+        uint32_t    Id       : 24;
+        uint32_t    Reserved : 8;
+    };
+} HV_PORT_ID;
+/** Pointer to a Hyper-V port ID. */
+typedef HV_PORT_ID *PHV_PORT_ID;
 
 
@@ -917,4 +930,5 @@
     };
 } HV_X64_SEGMENT_REGISTER;
+AssertCompileSize(HV_X64_SEGMENT_REGISTER, 16);
 /** Pointer to a value of HvX64RegisterEs..Tr. */
 typedef HV_X64_SEGMENT_REGISTER *PHV_X64_SEGMENT_REGISTER;
@@ -928,4 +942,5 @@
     uint64_t            Base;
 } HV_X64_TABLE_REGISTER;
+AssertCompileSize(HV_X64_TABLE_REGISTER, 16);
 /** Pointer to a value of HvX64RegisterIdtr/Gdtrr. */
 typedef HV_X64_TABLE_REGISTER *PHV_X64_TABLE_REGISTER;
@@ -1083,4 +1098,361 @@
 
 
+
+/**
+ * Hyper-V SyncIC message types.
+ */
+typedef enum
+{
+    HvMessageTypeNone = 0x00000000,
+
+    HvMessageTypeUnmappedGpa = 0x80000000,
+    HvMessageTypeGpaIntercept,
+
+    HvMessageTimerExpired = 0x80000010,
+
+    HvMessageTypeInvalidVpRegisterValue = 0x80000020,
+    HvMessageTypeUnrecoverableException,
+    HvMessageTypeUnsupportedFeature,
+    HvMessageTypeTlbPageSizeMismatch,                   /**< @since v5 */
+
+    /** @note Same as HvMessageTypeX64ApicEoi? Gone in 5.0.  Missing from 7600 WDK
+     *        headers even if it's in the 2.0 docs.  */
+    HvMessageTypeApicEoi = 0x80000030,
+    /** @note Same as HvMessageTypeX64LegacyFpError? Gone in 5.0, whereas 4.0b
+     *        calls it HvMessageTypeX64LegacyFpError.  Missing from 7600 WDK
+     *        headers even if it's in the 2.0 docs. */
+    HvMessageTypeFerrAsserted,
+
+    HvMessageTypeEventLogBufferComplete = 0x80000040,
+
+    HvMessageTypeX64IoPortIntercept = 0x80010000,
+    HvMessageTypeX64MsrIntercept,
+    HvMessageTypeX64CpuidIntercept,
+    HvMessageTypeX64ExceptionIntercept,
+    /** @note Appeared in 5.0 docs, but were here in 7600 WDK headers already. */
+    HvMessageTypeX64ApicEoi,
+    /** @note Appeared in 5.0 docs, but were here in 7600 WDK headers already. */
+    HvMessageTypeX64LegacyFpError,
+    /** @since v5   */
+    HvMessageTypeX64RegisterIntercept,
+    /** @since WinHvPlatform? */
+    HvMessageTypeX64Halt,
+    /** @since WinHvPlatform? */
+    HvMessageTypeX64InterruptWindow
+
+} HV_MESSAGE_TYPE;
+AssertCompileSize(HV_MESSAGE_TYPE, 4);
+AssertCompile(HvMessageTypeX64RegisterIntercept == 0x80010006);
+AssertCompile(HvMessageTypeX64Halt == 0x80010007);
+AssertCompile(HvMessageTypeX64InterruptWindow == 0x80010008);
+/** Pointer to a Hyper-V SyncIC message type. */
+typedef HV_MESSAGE_TYPE *PHV_MESSAGE_TYPE;
+
+/** Flag set for hypervisor messages, guest cannot send messages with this
+ *  flag set. */
+#define HV_MESSAGE_TYPE_HYPERVISOR_MASK     UINT32_C(0x80000000)
+
+/** Hyper-V SynIC message size (they are fixed sized). */
+#define HV_MESSAGE_SIZE                     256
+/** Maximum Hyper-V SynIC message payload size in bytes. */
+#define HV_MESSAGE_MAX_PAYLOAD_BYTE_COUNT   (HV_MESSAGE_SIZE - 16)
+/** Maximum Hyper-V SynIC message payload size in QWORDs (uint64_t). */
+#define HV_MESSAGE_MAX_PAYLOAD_QWORD_COUNT  (HV_MESSAGE_MAX_PAYLOAD_BYTE_COUNT / 8)
+
+/** SynIC message flags.   */
+typedef union
+{
+    uint8_t             AsUINT8;
+    struct
+    {
+        /** Messages are pending in the queue. */
+        uint8_t         MessagePending : 1;
+        uint8_t         Reserved : 7;
+    };
+} HV_MESSAGE_FLAGS;
+AssertCompileSize(HV_MESSAGE_FLAGS, 1);
+
+/** SynIC message header. */
+typedef struct
+{
+    HV_MESSAGE_TYPE     MessageType;
+    /** The 2.0-5.0b docs all have this incorrectly switched with 'Reserved', WDK 7600 got it right. */
+    uint8_t             PayloadSize;
+    HV_MESSAGE_FLAGS    MessageFlags;
+    uint16_t            Reserved;
+    union
+    {
+        uint64_t        OriginationId;
+        HV_PARTITION_ID Sender;
+        HV_PORT_ID      Port;
+    };
+} HV_MESSAGE_HEADER;
+AssertCompileSize(HV_MESSAGE_HEADER, 16);
+/** Pointer to a Hyper-V message header. */
+typedef HV_MESSAGE_HEADER *PHV_MESSAGE_HEADER;
+/** Pointer to a const Hyper-V message header. */
+typedef HV_MESSAGE_HEADER const *PCHV_MESSAGE_HEADER;
+
+
+
+/** @name Intercept access type.
+ * @{ */
+typedef uint8_t HV_INTERCEPT_ACCESS_TYPE;
+#define HV_INTERCEPT_ACCESS_READ            0
+#define HV_INTERCEPT_ACCESS_WRITE           1
+#define HV_INTERCEPT_ACCESS_EXECUTE         2
+/** @} */
+
+/** @name Intercept access type mask.
+ * @{ */
+typedef uint32_t HV_INTERCEPT_ACCESS_TYPE_MASK;
+#define HV_INTERCEPT_ACCESS_MASK_NONE       0
+#define HV_INTERCEPT_ACCESS_MASK_READ       1
+#define HV_INTERCEPT_ACCESS_MASK_WRITE      2
+#define HV_INTERCEPT_ACCESS_MASK_EXECUTE    4
+/** @} */
+
+/** X64 intercept execution state.
+ * @sa WHV_X64_VP_EXECUTION_STATE */
+typedef union
+{
+    uint16_t            AsUINT16;
+    struct
+    {
+        uint16_t        Cpl                 : 2;
+        uint16_t        Cr0Pe               : 1;
+        uint16_t        Cr0Am               : 1;
+        uint16_t        EferLma             : 1;
+        uint16_t        DebugActive         : 1;
+        uint16_t        InterruptionPending : 1;
+        uint16_t        Reserved0           : 5;
+        uint16_t        InterruptShadow     : 1;
+        uint16_t        Reserved1           : 3;
+    };
+} HV_X64_VP_EXECUTION_STATE;
+AssertCompileSize(HV_X64_VP_EXECUTION_STATE, 2);
+/** Pointer to X86 intercept execution state. */
+typedef HV_X64_VP_EXECUTION_STATE *PHV_X64_VP_EXECUTION_STATE;
+/** Pointer to const X86 intercept execution state. */
+typedef HV_X64_VP_EXECUTION_STATE const *PCHV_X64_VP_EXECUTION_STATE;
+
+/** X64 intercept message header. */
+typedef struct
+{
+    HV_VP_INDEX                     VpIndex;                /**< 0x00 */
+    uint8_t                         InstructionLength;      /**< 0x04: Zero if not available, instruction fetch exit, ... */
+    HV_INTERCEPT_ACCESS_TYPE        InterceptAccessType;    /**< 0x05 */
+    HV_X64_VP_EXECUTION_STATE       ExecutionState;         /**< 0x06 */
+    HV_X64_SEGMENT_REGISTER         CsSegment;              /**< 0x08 */
+    uint64_t                        Rip;                    /**< 0x18 */
+    uint64_t                        Rflags;                 /**< 0x20 */
+} HV_X64_INTERCEPT_MESSAGE_HEADER;
+AssertCompileSize(HV_X64_INTERCEPT_MESSAGE_HEADER, 40);
+/** Pointer to a x86 intercept message header. */
+typedef HV_X64_INTERCEPT_MESSAGE_HEADER *PHV_X64_INTERCEPT_MESSAGE_HEADER;
+
+
+/** X64 memory access flags (HvMessageTypeGpaIntercept, HvMessageTypeUnmappedGpa).
+ * @sa WHV_MEMORY_ACCESS_INFO */
+typedef union
+{
+    uint8_t             AsUINT8;
+    struct
+    {
+        uint8_t         GvaValid : 1;
+        uint8_t         Reserved : 7;
+    };
+} HV_X64_MEMORY_ACCESS_INFO;
+AssertCompileSize(HV_X64_MEMORY_ACCESS_INFO, 1);
+
+/** The payload format for HvMessageTypeGpaIntercept and HvMessageTypeUnmappedGpa.
+ * @sa   WHV_MEMORY_ACCESS_CONTEXT
+ * @note max message size. */
+typedef struct
+{
+    HV_X64_INTERCEPT_MESSAGE_HEADER Header;                 /**< 0x00 */
+    HV_CACHE_TYPE                   CacheType;              /**< 0x28 */
+    uint8_t                         InstructionByteCount;   /**< 0x2c */
+    HV_X64_MEMORY_ACCESS_INFO       MemoryAccessInfo;       /**< 0x2d */
+    uint16_t                        Reserved1;              /**< 0x2e */
+    uint64_t                        GuestVirtualAddress;    /**< 0x30 */
+    uint64_t                        GuestPhysicalAddress;   /**< 0x38 */
+    uint8_t                         InstructionBytes[16];   /**< 0x40 */
+    /* We don't the following (v5 / WinHvPlatform): */
+    HV_X64_SEGMENT_REGISTER         DsSegment;              /**< 0x50 */
+    HV_X64_SEGMENT_REGISTER         SsSegment;              /**< 0x60 */
+    uint64_t                        Rax;                    /**< 0x70 */
+    uint64_t                        Rcx;                    /**< 0x78 */
+    uint64_t                        Rdx;                    /**< 0x80 */
+    uint64_t                        Rbx;                    /**< 0x88 */
+    uint64_t                        Rsp;                    /**< 0x90 */
+    uint64_t                        Rbp;                    /**< 0x98 */
+    uint64_t                        Rsi;                    /**< 0xa0 */
+    uint64_t                        Rdi;                    /**< 0xa8 */
+    uint64_t                        R8;                     /**< 0xb0 */
+    uint64_t                        R9;                     /**< 0xb8 */
+    uint64_t                        R10;                    /**< 0xc0 */
+    uint64_t                        R11;                    /**< 0xc8 */
+    uint64_t                        R12;                    /**< 0xd0 */
+    uint64_t                        R13;                    /**< 0xd8 */
+    uint64_t                        R14;                    /**< 0xe0 */
+    uint64_t                        R15;                    /**< 0xe8 */
+} HV_X64_MEMORY_INTERCEPT_MESSAGE;
+AssertCompileSize(HV_X64_MEMORY_INTERCEPT_MESSAGE, 0xf0);
+AssertCompileMemberOffset(HV_X64_MEMORY_INTERCEPT_MESSAGE, DsSegment, 0x50);
+/** Pointer to a HvMessageTypeGpaIntercept or HvMessageTypeUnmappedGpa payload. */
+typedef HV_X64_MEMORY_INTERCEPT_MESSAGE *PHV_X64_MEMORY_INTERCEPT_MESSAGE;
+/** Pointer to a const HvMessageTypeGpaIntercept or HvMessageTypeUnmappedGpa payload. */
+typedef HV_X64_MEMORY_INTERCEPT_MESSAGE const *PCHV_X64_MEMORY_INTERCEPT_MESSAGE;
+
+
+/** X64 I/O port access information (HvMessageTypeX64IoPortIntercept). */
+typedef union HV_X64_IO_PORT_ACCESS_INFO
+{
+    uint8_t             AsUINT8;
+    struct
+    {
+        uint8_t         AccessSize  : 3;
+        uint8_t         StringOp    : 1;
+        uint8_t         RepPrefix   : 1;
+        uint8_t         Reserved    : 3;
+    };
+} HV_X64_IO_PORT_ACCESS_INFO;
+AssertCompileSize(HV_X64_IO_PORT_ACCESS_INFO, 1);
+
+/** The payload format for HvMessageTypeX64IoPortIntercept.  */
+typedef struct _HV_X64_IO_PORT_INTERCEPT_MESSAGE
+{
+    HV_X64_INTERCEPT_MESSAGE_HEADER     Header;                 /**< 0x00 */
+    uint16_t                            PortNumber;             /**< 0x28 */
+    HV_X64_IO_PORT_ACCESS_INFO          AccessInfo;             /**< 0x2a */
+    uint8_t                             InstructionByteCount;   /**< 0x2b */
+    uint32_t                            Reserved;               /**< 0x2c */
+    uint64_t                            Rax;                    /**< 0x30 */
+    uint8_t                             InstructionBytes[16];   /**< 0x38 */
+    HV_X64_SEGMENT_REGISTER             DsSegment;              /**< 0x48 */
+    HV_X64_SEGMENT_REGISTER             EsSegment;              /**< 0x58 */
+    uint64_t                            Rcx;                    /**< 0x68 */
+    uint64_t                            Rsi;                    /**< 0x70 */
+    uint64_t                            Rdi;                    /**< 0x78 */
+} HV_X64_IO_PORT_INTERCEPT_MESSAGE;
+AssertCompileSize(HV_X64_IO_PORT_INTERCEPT_MESSAGE, 128);
+/** Pointer to a HvMessageTypeX64IoPortIntercept payload. */
+typedef HV_X64_IO_PORT_INTERCEPT_MESSAGE *PHV_X64_IO_PORT_INTERCEPT_MESSAGE;
+/** Pointer to a const HvMessageTypeX64IoPortIntercept payload. */
+typedef HV_X64_IO_PORT_INTERCEPT_MESSAGE const *PCHV_X64_IO_PORT_INTERCEPT_MESSAGE;
+
+/** Full I/O port message. */
+typedef struct
+{
+    HV_MESSAGE_HEADER                   MsgHdr;
+    HV_X64_IO_PORT_INTERCEPT_MESSAGE    Payload;
+} HV_X64_IO_PORT_INTERCEPT_MESSAGE_FULL;
+
+
+/** X64 exception information (HvMessageTypeX64ExceptionIntercept).
+ * @sa WHV_VP_EXCEPTION_INFO */
+typedef union
+{
+    uint8_t             AsUINT8;
+    struct
+    {
+        uint8_t         ErrorCodeValid : 1;
+        /** @todo WHV_VP_EXCEPTION_INFO::SoftwareException   */
+        uint8_t         Reserved       : 7;
+    };
+} HV_X64_EXCEPTION_INFO;
+AssertCompileSize(HV_X64_EXCEPTION_INFO, 1);
+
+/** The payload format for HvMessageTypeX64ExceptionIntercept.
+ * @sa   WHV_VP_EXCEPTION_CONTEXT
+ * @note max message size. */
+typedef struct
+{
+    HV_X64_INTERCEPT_MESSAGE_HEADER     Header;                 /**< 0x00 */
+    uint16_t                            ExceptionVector;        /**< 0x28 */
+    HV_X64_EXCEPTION_INFO               ExceptionInfo;          /**< 0x2a */
+    uint8_t                             InstructionByteCount;   /**< 0x2b */
+    uint32_t                            ErrorCode;              /**< 0x2c */
+    uint64_t                            ExceptionParameter;     /**< 0x30 */
+    uint64_t                            Reserved;               /**< 0x38 */
+    uint8_t                             InstructionBytes[16];   /**< 0x40 */
+    HV_X64_SEGMENT_REGISTER             DsSegment;              /**< 0x50 */
+    HV_X64_SEGMENT_REGISTER             SsSegment;              /**< 0x60 */
+    uint64_t                            Rax;                    /**< 0x70 */
+    uint64_t                            Rcx;                    /**< 0x78 */
+    uint64_t                            Rdx;                    /**< 0x80 */
+    uint64_t                            Rbx;                    /**< 0x88 */
+    uint64_t                            Rsp;                    /**< 0x90 */
+    uint64_t                            Rbp;                    /**< 0x98 */
+    uint64_t                            Rsi;                    /**< 0xa0 */
+    uint64_t                            Rdi;                    /**< 0xa8 */
+    uint64_t                            R8;                     /**< 0xb0 */
+    uint64_t                            R9;                     /**< 0xb8 */
+    uint64_t                            R10;                    /**< 0xc0 */
+    uint64_t                            R11;                    /**< 0xc8 */
+    uint64_t                            R12;                    /**< 0xd0 */
+    uint64_t                            R13;                    /**< 0xd8 */
+    uint64_t                            R14;                    /**< 0xe0 */
+    uint64_t                            R15;                    /**< 0xe8 */
+} HV_X64_EXCEPTION_INTERCEPT_MESSAGE;
+AssertCompileSize(HV_X64_EXCEPTION_INTERCEPT_MESSAGE, 0xf0);
+/** Pointer to a HvMessageTypeX64ExceptionIntercept payload. */
+typedef HV_X64_EXCEPTION_INTERCEPT_MESSAGE *PHV_X64_EXCEPTION_INTERCEPT_MESSAGE;
+/** Pointer to a ocnst HvMessageTypeX64ExceptionIntercept payload. */
+typedef HV_X64_EXCEPTION_INTERCEPT_MESSAGE const *PCHV_X64_EXCEPTION_INTERCEPT_MESSAGE;
+
+
+/**
+ * The payload format for HvMessageTypeX64Halt,
+ *
+ * @note This message does not include HV_X64_INTERCEPT_MESSAGE_HEADER!
+ */
+typedef struct
+{
+    /** Seems to be a zero 64-bit field here.  */
+    uint64_t    u64Reserved;
+} HV_X64_HALT_MESSAGE;
+/** Pointer to a HvMessageTypeX64Halt payload. */
+typedef HV_X64_HALT_MESSAGE *PHV_X64_HALT_MESSAGE;
+/** Pointer to a const HvMessageTypeX64Halt payload. */
+typedef HV_X64_HALT_MESSAGE const *PCHV_X64_HALT_MESSAGE;
+
+/** Full HvMessageTypeX64Halt message. */
+typedef struct
+{
+    HV_MESSAGE_HEADER                   MsgHdr;
+    HV_X64_HALT_MESSAGE                 Payload;
+} HV_X64_HALT_MESSAGE_FULL;
+
+
+
+/** Hyper-V SynIC message. */
+typedef struct
+{
+    HV_MESSAGE_HEADER   Header;
+    /** 0x10 */
+    union
+    {
+        uint64_t                            Payload[HV_MESSAGE_MAX_PAYLOAD_QWORD_COUNT];
+
+        /** Common header for X64 intercept messages. */
+        HV_X64_INTERCEPT_MESSAGE_HEADER     X64InterceptHeader;
+        /** HvMessageTypeGpaIntercept, HvMessageTypeUnmappedGpa. */
+        HV_X64_MEMORY_INTERCEPT_MESSAGE     X86MemoryIntercept;
+        /** HvMessageTypeX64IoPortIntercept */
+        HV_X64_IO_PORT_INTERCEPT_MESSAGE    X64IoPortIntercept;
+        /** HvMessageTypeX64ExceptionIntercept */
+        HV_X64_EXCEPTION_INTERCEPT_MESSAGE  X64ExceptionIntercept;
+        /** HvMessageTypeX64Halt. */
+        HV_X64_HALT_MESSAGE                 X64Halt;
+    };
+} HV_MESSAGE;
+AssertCompileSize(HV_MESSAGE, HV_MESSAGE_SIZE);
+/** Pointer to a Hyper-V SynIC message. */
+typedef HV_MESSAGE *PHV_MESSAGE;
+/** Pointer to const a Hyper-V SynIC message. */
+typedef HV_MESSAGE const *PCHV_MESSAGE;
+
 #endif
 
Index: /trunk/include/iprt/nt/vid.h
===================================================================
--- /trunk/include/iprt/nt/vid.h	(revision 71129)
+++ /trunk/include/iprt/nt/vid.h	(revision 71129)
@@ -0,0 +1,201 @@
+/** @file
+ * Virtualization Infrastructure Driver (VID) API.
+ */
+
+/*
+ * Copyright (C) 2018 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+#ifndef ___iprt_nt_vid_h
+#define ___iprt_nt_vid_h
+
+#include "hyperv.h"
+
+
+/**
+ * Output from VidMessageSlotMap.
+ */
+typedef struct VID_MAPPED_MESSAGE_SLOT
+{
+    /** The message block mapping. */
+    struct _HV_MESSAGE *pMsgBlock;
+    /** Copy of input iCpu. */
+    uint32_t            iCpu;
+    /** Explicit padding.   */
+    uint32_t            uParentAdvisory;
+} VID_MAPPED_MESSAGE_SLOT;
+/** Pointer to VidMessageSlotMap output structure. */
+typedef VID_MAPPED_MESSAGE_SLOT *PVID_MAPPED_MESSAGE_SLOT;
+
+
+/** @name VID_MESSAGE_MAPPING_HEADER::enmVidMsgType values (wild guess).
+ * @{ */
+/** Type mask, strips flags. */
+#define VID_MESSAGE_TYPE_MASK                   UINT32_C(0x00ffffff)
+/** No return message necessary. */
+#define VID_MESSAGE_TYPE_FLAG_NO_RETURN         UINT32_C(0x01000000)
+/** Observed message values. */
+typedef enum
+{
+    /** Invalid zero value. */
+    VidMessageInvalid = 0,
+    /** Guessing this means a message from the hypervisor.  */
+    VidMessageHypervisorMessage   = 0x00000c | VID_MESSAGE_TYPE_FLAG_NO_RETURN,
+    /** Guessing this means stop request completed.  Message length is 1 byte. */
+    VidMessageStopRequestComplete = 0x00000d | VID_MESSAGE_TYPE_FLAG_NO_RETURN,
+} VID_MESSAGE_TYPE;
+AssertCompileSize(VID_MESSAGE_TYPE, 4);
+/** @} */
+
+/**
+ * Header of the message mapping returned by VidMessageSlotMap.
+ */
+typedef struct VID_MESSAGE_MAPPING_HEADER
+{
+    /** Current guess is that this is VID_MESSAGE_TYPE. */
+    VID_MESSAGE_TYPE    enmVidMsgType;
+    /** The message size or so it seems (0x100). */
+    uint32_t            cbMessage;
+    /** So far these have been zero. */
+    uint32_t            aZeroPPadding[2+4];
+} VID_MESSAGE_MAPPING_HEADER;
+AssertCompileSize(VID_MESSAGE_MAPPING_HEADER, 32);
+
+/**
+ * VID processor status (VidGetVirtualProcessorRunningStatus).
+ *
+ * @note This is used internally in VID.SYS, in 17101 it's at offset 8 in their
+ *       'pVCpu' structure.
+ */
+typedef enum
+{
+    VidProcessorStatusStopped = 0,
+    VidProcessorStatusRunning,
+    VidProcessorStatusSuspended,
+    VidProcessorStatusUndefined = 0xffff
+} VID_PROCESSOR_STATUS;
+AssertCompileSize(VID_PROCESSOR_STATUS, 4);
+
+
+RT_C_DECLS_BEGIN
+
+/** Calling convention. */
+#ifndef WINAPI
+# define VIDAPI __stdcall
+#else
+# define VIDAPI WINAPI
+#endif
+
+/** Partition handle. */
+#ifndef WINAPI
+typedef void *VID_PARTITION_HANDLE;
+#else
+typedef HANDLE VID_PARTITION_HANDLE;
+#endif
+
+/**
+ * Gets the partition ID.
+ *
+ * The partition ID is the numeric identifier used when making hypercalls to the
+ * hypervisor.
+ */
+DECLIMPORT(BOOL) VIDAPI VidGetHvPartitionId(VID_PARTITION_HANDLE hPartition, HV_PARTITION_ID *pidPartition);
+
+/**
+ * Starts asynchronous execution of a virtual CPU.
+ */
+DECLIMPORT(BOOL) VIDAPI VidStartVirtualProcessor(VID_PARTITION_HANDLE hPartition, HV_VP_INDEX iCpu);
+
+/**
+ * Stops the asynchronous execution of a virtual CPU.
+ *
+ * @retval ERROR_VID_STOP_PENDING if busy with intercept, check messages.
+ */
+DECLIMPORT(BOOL) VIDAPI VidStopVirtualProcessor(VID_PARTITION_HANDLE hPartition, HV_VP_INDEX iCpu);
+
+/**
+ * WHvCreateVirtualProcessor boils down to a call to VidMessageSlotMap and
+ * some internal WinHvPlatform state fiddling.
+ *
+ * Looks like it maps memory and returns the pointer to it.
+ * VidMessageSlotHandleAndGetNext is later used to wait for the next message and
+ * put (??) it into that memory mapping.
+ *
+ * @returns Success indicator (details in LastStatusValue and LastErrorValue).
+ *
+ * @param   hPartition  The partition handle.
+ * @param   pOutput     Where to return the pointer to the message memory
+ *                      mapping.  The CPU index is also returned here.
+ * @param   iCpu        The CPU to wait-and-get messages for.
+ */
+DECLIMPORT(BOOL) VIDAPI VidMessageSlotMap(VID_PARTITION_HANDLE hPartition, PVID_MAPPED_MESSAGE_SLOT pOutput, HV_VP_INDEX iCpu);
+
+/**
+ * This is used by WHvRunVirtualProcessor to wait for the next exit msg.
+ *
+ * The message appears in the memory mapping returned by VidMessageSlotMap.
+ *
+ * @returns Success indicator (details only in LastErrorValue - LastStatusValue
+ *          is not set).
+ * @retval  STATUS_TIMEOUT for STATUS_TIMEOUT as well as STATUS_USER_APC and
+ *          STATUS_ALERTED.
+ *
+ * @param   hPartition  The partition handle.
+ * @param   iCpu        The CPU to wait-and-get messages for.
+ * @param   fFlags      Flags. At least one of the two flags must be set:
+ *                          - VID_MSHAGN_F_GET_NEXT_MESSAGE (bit 0)
+ *                          - VID_MSHAGN_F_HANDLE_MESSAGE (bit 1)
+ * @param   cMillies    The timeout, presumably in milliseconds.
+ *
+ * @todo    Would be awfully nice if someone at Microsoft could hit at the
+ *          flags here.
+ * @note
+ */
+DECLIMPORT(BOOL) VIDAPI VidMessageSlotHandleAndGetNext(VID_PARTITION_HANDLE hPartition, HV_VP_INDEX iCpu,
+                                                       uint32_t fFlags, uint32_t cMillies);
+/** @name VID_MSHAGN_F_GET_XXX - Flags for VidMessageSlotHandleAndGetNext
+ * @{ */
+/** This will try get the next message, waiting if necessary.
+ * It is subject to NtAlertThread processing when it starts waiting.  */
+#define VID_MSHAGN_F_GET_NEXT_MESSAGE   RT_BIT_32(0)
+/** ACK the message as handled and resume execution/whatever.
+ * This is executed before VID_MSHAGN_F_GET_NEXT_MESSAGE and should not be
+ * subject to NtAlertThread side effects. */
+#define VID_MSHAGN_F_HANDLE_MESSAGE     RT_BIT_32(1)
+/** @} */
+
+/**
+ * Gets the processor running status.
+ *
+ * This is probably only available in special builds, as one of the early I/O
+ * control dispatching routines will not let it thru.  Lower down routines does
+ * implement it, so it's possible to patch it into working.  This works for
+ * build 17101: eb vid+12180 0f 84 98 00 00 00
+ *
+ * @retval STATUS_NOT_IMPLEMENTED
+ */
+DECLIMPORT(BOOL) VIDAPI VidGetVirtualProcessorRunningStatus(VID_PARTITION_HANDLE hPartition, HV_VP_INDEX iCpu,
+                                                            VID_PROCESSOR_STATUS *penmStatus);
+
+RT_C_DECLS_END
+
+#endif
+
Index: /trunk/src/VBox/VMM/Makefile.kmk
===================================================================
--- /trunk/src/VBox/VMM/Makefile.kmk	(revision 71128)
+++ /trunk/src/VBox/VMM/Makefile.kmk	(revision 71129)
@@ -313,4 +313,5 @@
  VBoxVMM_SOURCES.win.amd64 += VMMR3/NEMR3Native-win.cpp
  VBoxVMM_DEFS.win.amd64    += VBOX_WITH_NATIVE_NEM
+ VBoxVMM_SDKS.win          += VBOX_NTDLL
  VMMR3/NEMR3Native-win.cpp_DEFS.amd64 = _AMD64_
  VMMR3/NEMR3Native-win.cpp_INCS = \
Index: /trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 71128)
+++ /trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 71129)
@@ -29,4 +29,5 @@
 #include <iprt/nt/nt-and-windows.h>
 #include <iprt/nt/hyperv.h>
+#include <iprt/nt/vid.h>
 #include <WinHvPlatform.h>
 
@@ -78,26 +79,8 @@
 
 
-#define NEM_WIN_USE_HYPERCALLS_FOR_PAGES
-#define NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS
-
 
 /*********************************************************************************************************************************
 *   Structures and Typedefs                                                                                                      *
 *********************************************************************************************************************************/
-/** WHvRegisterInterruptState layout, reconstructed from the v7.1 DDK. */
-typedef union MISSINGINTERUPTSTATEREG
-{
-    /** 64-bit view. */
-    uint64_t au64[2];
-    struct /* unamed */
-    {
-        uint64_t fInterruptShadow : 1;
-        uint64_t fNmiMasked : 2;
-        uint64_t uReserved0 : 61;
-        uint64_t uReserved1;
-    };
-} MISSINGINTERUPTSTATEREG;
-AssertCompileSize(MISSINGINTERUPTSTATEREG, 16);
-
 
 
@@ -116,4 +99,5 @@
 static decltype(WHvUnmapGpaRange) *                 g_pfnWHvUnmapGpaRange;
 static decltype(WHvTranslateGva) *                  g_pfnWHvTranslateGva;
+#ifndef NEM_WIN_USE_OUR_OWN_RUN_API
 static decltype(WHvCreateVirtualProcessor) *        g_pfnWHvCreateVirtualProcessor;
 static decltype(WHvDeleteVirtualProcessor) *        g_pfnWHvDeleteVirtualProcessor;
@@ -123,9 +107,17 @@
 static decltype(WHvGetVirtualProcessorRegisters) *  g_pfnWHvGetVirtualProcessorRegisters;
 static decltype(WHvSetVirtualProcessorRegisters) *  g_pfnWHvSetVirtualProcessorRegisters;
+#endif
 /** @} */
 
 /** @name APIs imported from Vid.dll
  * @{ */
-static BOOL (WINAPI *g_pfnVidGetHvPartitionId)(HANDLE hPartition, HV_PARTITION_ID *pidPartition);
+static decltype(VidGetHvPartitionId)               *g_pfnVidGetHvPartitionId;
+static decltype(VidStartVirtualProcessor)          *g_pfnVidStartVirtualProcessor;
+static decltype(VidStopVirtualProcessor)           *g_pfnVidStopVirtualProcessor;
+static decltype(VidMessageSlotMap)                 *g_pfnVidMessageSlotMap;
+static decltype(VidMessageSlotHandleAndGetNext)    *g_pfnVidMessageSlotHandleAndGetNext;
+#ifdef LOG_ENABLED
+static decltype(VidGetVirtualProcessorRunningStatus) *g_pfnVidGetVirtualProcessorRunningStatus;
+#endif
 /** @} */
 
@@ -152,12 +144,21 @@
     NEM_WIN_IMPORT(0, false, WHvUnmapGpaRange),
     NEM_WIN_IMPORT(0, false, WHvTranslateGva),
+#ifndef NEM_WIN_USE_OUR_OWN_RUN_API
     NEM_WIN_IMPORT(0, false, WHvCreateVirtualProcessor),
     NEM_WIN_IMPORT(0, false, WHvDeleteVirtualProcessor),
     NEM_WIN_IMPORT(0, false, WHvRunVirtualProcessor),
+    NEM_WIN_IMPORT(0, false, WHvCancelRunVirtualProcessor),
     NEM_WIN_IMPORT(0, false, WHvGetRunExitContextSize),
-    NEM_WIN_IMPORT(0, false, WHvCancelRunVirtualProcessor),
     NEM_WIN_IMPORT(0, false, WHvGetVirtualProcessorRegisters),
     NEM_WIN_IMPORT(0, false, WHvSetVirtualProcessorRegisters),
+#endif
     NEM_WIN_IMPORT(1, false, VidGetHvPartitionId),
+    NEM_WIN_IMPORT(1, false, VidMessageSlotMap),
+    NEM_WIN_IMPORT(1, false, VidMessageSlotHandleAndGetNext),
+    NEM_WIN_IMPORT(1, false, VidStartVirtualProcessor),
+    NEM_WIN_IMPORT(1, false, VidStopVirtualProcessor),
+#ifdef LOG_ENABLED
+    NEM_WIN_IMPORT(1, false, VidGetVirtualProcessorRunningStatus),
+#endif
 #undef NEM_WIN_IMPORT
 };
@@ -202,5 +203,12 @@
 
 /** The real NtDeviceIoControlFile API in NTDLL.   */
-static decltype(NtDeviceIoControlFile) *g_pfnNtDeviceIoControlFile;
+static decltype(NtDeviceIoControlFile)       *g_pfnNtDeviceIoControlFile;
+/** Mapping slot for CPU #0.
+ * @{  */
+static VID_MESSAGE_MAPPING_HEADER            *g_pMsgSlotMapping = NULL;
+static const HV_MESSAGE_HEADER               *g_pHvMsgHdr;
+static const HV_X64_INTERCEPT_MESSAGE_HEADER *g_pX64MsgHdr;
+/** @} */
+
 
 /**
@@ -214,13 +222,48 @@
                                          PVOID pvOutput, ULONG cbOutput)
 {
+    char szFunction[32];
+    const char *pszFunction;
+    switch (uFunction)
+    {
+        case 0x2210cb: pszFunction = "VidMessageSlotHandleAndGetNext"; break;
+        case 0x2210cc: pszFunction = "VidMessageSlotMap"; break;
+        case 0x221164: pszFunction = "VidStopVirtualProcessor"; break;
+        case 0x221158: pszFunction = "VidStartVirtualProcessor"; break;
+        case 0x2210a7: pszFunction = "VidGetVirtualProcessorState"; break;
+        case 0x221153: pszFunction = "VidSetVirtualProcessorState"; break;
+        default:
+            RTStrPrintf(szFunction, sizeof(szFunction), "%#x", uFunction);
+            pszFunction = szFunction;
+            break;
+    }
+
+    if (cbInput > 0 && pvInput)
+        Log12(("VID!NtDeviceIoControlFile: %s/input: %.*Rhxs\n", pszFunction, RT_MIN(cbInput, 32), pvInput));
     NTSTATUS rcNt = g_pfnNtDeviceIoControlFile(hFile, hEvt, pfnApcCallback, pvApcCtx, pIos, uFunction,
                                                pvInput, cbInput, pvOutput, cbOutput);
     if (!hEvt && !pfnApcCallback && !pvApcCtx)
-        Log12(("VID!NtDeviceIoControlFile: hFile=%#zx pIos=%p->{s:%#x, i:%#zx} uFunction=%#x Input=%p LB %#x Output=%p LB %#x) -> %#x; Caller=%p\n",
-               hFile, pIos, pIos->Status, pIos->Information, uFunction, pvInput, cbInput, pvOutput, cbOutput, rcNt, ASMReturnAddress()));
+        Log12(("VID!NtDeviceIoControlFile: hFile=%#zx pIos=%p->{s:%#x, i:%#zx} uFunction=%s Input=%p LB %#x Output=%p LB %#x) -> %#x; Caller=%p\n",
+               hFile, pIos, pIos->Status, pIos->Information, pszFunction, pvInput, cbInput, pvOutput, cbOutput, rcNt, ASMReturnAddress()));
     else
-        Log12(("VID!NtDeviceIoControlFile: hFile=%#zx hEvt=%#zx Apc=%p/%p pIos=%p->{s:%#x, i:%#zx} uFunction=%#x Input=%p LB %#x Output=%p LB %#x) -> %#x; Caller=%p\n",
-               hFile, hEvt, pfnApcCallback, pvApcCtx, pIos, pIos->Status, pIos->Information, uFunction,
+        Log12(("VID!NtDeviceIoControlFile: hFile=%#zx hEvt=%#zx Apc=%p/%p pIos=%p->{s:%#x, i:%#zx} uFunction=%s Input=%p LB %#x Output=%p LB %#x) -> %#x; Caller=%p\n",
+               hFile, hEvt, pfnApcCallback, pvApcCtx, pIos, pIos->Status, pIos->Information, pszFunction,
                pvInput, cbInput, pvOutput, cbOutput, rcNt, ASMReturnAddress()));
+    if (cbOutput > 0 && pvOutput)
+    {
+        Log12(("VID!NtDeviceIoControlFile: %s/output: %.*Rhxs\n", pszFunction, RT_MIN(cbOutput, 32), pvOutput));
+        if (uFunction == 0x2210cc && g_pMsgSlotMapping == NULL && cbOutput >= sizeof(void *))
+        {
+            g_pMsgSlotMapping = *(VID_MESSAGE_MAPPING_HEADER **)pvOutput;
+            g_pHvMsgHdr       = (const HV_MESSAGE_HEADER               *)(g_pMsgSlotMapping + 1);
+            g_pX64MsgHdr      = (const HV_X64_INTERCEPT_MESSAGE_HEADER *)(g_pHvMsgHdr + 1);
+            Log12(("VID!NtDeviceIoControlFile: Message slot mapping: %p\n", g_pMsgSlotMapping));
+        }
+    }
+    if (g_pMsgSlotMapping && (uFunction == 0x2210cb || uFunction == 0x2210cc || uFunction == 0x221164))
+        Log12(("VID!NtDeviceIoControlFile: enmVidMsgType=%#x cb=%#x msg=%#x payload=%u cs:rip=%04x:%08RX64 (%s)\n",
+               g_pMsgSlotMapping->enmVidMsgType, g_pMsgSlotMapping->cbMessage,
+               g_pHvMsgHdr->MessageType, g_pHvMsgHdr->PayloadSize,
+               g_pX64MsgHdr->CsSegment.Selector, g_pX64MsgHdr->Rip, pszFunction));
+
     return rcNt;
 }
@@ -304,6 +347,6 @@
     Assert(fSuccess);
 }
-#endif
-
+
+#endif /* NEM_WIN_INTERCEPT_NT_IO_CTLS */
 
 
@@ -456,5 +499,5 @@
         return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
                              "WHvGetCapability/WHvCapabilityCodeHypervisorPresent failed: %Rhrc (Last=%#x/%u)",
-                             hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                             hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
     if (!Caps.HypervisorPresent)
     {
@@ -475,5 +518,5 @@
         return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
                              "WHvGetCapability/WHvCapabilityCodeExtendedVmExits failed: %Rhrc (Last=%#x/%u)",
-                             hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                             hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
     NEM_LOG_REL_CAP_EX("WHvCapabilityCodeExtendedVmExits", "%'#018RX64", Caps.ExtendedVmExits.AsUINT64);
     pVM->nem.s.fExtendedMsrExit   = RT_BOOL(Caps.ExtendedVmExits.X64MsrExit);
@@ -495,5 +538,5 @@
         return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
                              "WHvGetCapability/WHvCapabilityCodeFeatures failed: %Rhrc (Last=%#x/%u)",
-                             hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                             hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
     if (Caps.Features.AsUINT64 & ~(uint64_t)0)
         LogRel(("NEM: Warning! Unknown feature definitions: %#RX64\n", Caps.Features.AsUINT64));
@@ -508,5 +551,5 @@
         return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
                              "WHvGetCapability/WHvCapabilityCodeProcessorVendor failed: %Rhrc (Last=%#x/%u)",
-                             hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                             hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
     switch (Caps.ProcessorVendor)
     {
@@ -533,5 +576,5 @@
         return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
                              "WHvGetCapability/WHvCapabilityCodeProcessorFeatures failed: %Rhrc (Last=%#x/%u)",
-                             hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                             hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
     NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorFeatures", "%'#018RX64", Caps.ProcessorFeatures.AsUINT64);
 #define NEM_LOG_REL_CPU_FEATURE(a_Field)    NEM_LOG_REL_CAP_SUB(#a_Field, Caps.ProcessorFeatures.a_Field)
@@ -592,5 +635,5 @@
         return RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
                              "WHvGetCapability/WHvCapabilityCodeProcessorClFlushSize failed: %Rhrc (Last=%#x/%u)",
-                             hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                             hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
     NEM_LOG_REL_CAP_EX("WHvCapabilityCodeProcessorClFlushSize", "2^%u", Caps.ProcessorClFlushSize);
     if (Caps.ProcessorClFlushSize < 8 && Caps.ProcessorClFlushSize > 9)
@@ -650,5 +693,5 @@
     if (FAILED(hrc))
         return RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED, "WHvCreatePartition failed with %Rhrc (Last=%#x/%u)",
-                             hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                             hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
 
     int rc;
@@ -696,5 +739,5 @@
         rc = RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED,
                            "Failed setting WHvPartitionPropertyCodeProcessorCount to %u: %Rhrc (Last=%#x/%u)",
-                           pVM->cCpus, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                           pVM->cCpus, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
     WHvDeletePartition(hPartition);
 
@@ -799,5 +842,5 @@
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
                           "Failed to set WHvPartitionPropertyCodeProcessorVendor to %u: %Rhrc (Last=%#x/%u)",
-                          Property.ProcessorVendor, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                          Property.ProcessorVendor, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
 
     /* Not sure if we really need to set the cache line flush size. */
@@ -809,5 +852,5 @@
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
                           "Failed to set WHvPartitionPropertyCodeProcessorClFlushSize to %u: %Rhrc (Last=%#x/%u)",
-                          pVM->nem.s.cCacheLineFlushShift, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                          pVM->nem.s.cCacheLineFlushShift, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
 
     /*
@@ -824,5 +867,5 @@
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
                           "Failed to set WHvPartitionPropertyCodeProcessorFeatures to %'#RX64: %Rhrc (Last=%#x/%u)",
-                          pVM->nem.s.uCpuFeatures.u64, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                          pVM->nem.s.uCpuFeatures.u64, hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
 
     /*
@@ -836,5 +879,5 @@
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
                           "Call to WHvSetupPartition failed: %Rhrc (Last=%#x/%u)",
-                          hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                          hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
 
     /* Get the handle. */
@@ -858,29 +901,51 @@
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
                           "Failed to get device handle and/or partition ID for %p (hPartitionDevice=%p, Last=%#x/%u)",
-                          hPartition, hPartitionDevice, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue);
+                          hPartition, hPartitionDevice, RTNtLastStatusValue(), RTNtLastErrorValue());
     pVM->nem.s.hPartitionDevice = hPartitionDevice;
     pVM->nem.s.idHvPartition    = idHvPartition;
 
     /*
-     * Create EMTs.
+     * Setup the EMTs.
      */
     VMCPUID iCpu;
     for (iCpu = 0; iCpu < pVM->cCpus; iCpu++)
     {
+        PVMCPU pVCpu = &pVM->aCpus[iCpu];
+
+        pVCpu->nem.s.hNativeThreadHandle = (RTR3PTR)RTThreadGetNativeHandle(VMR3GetThreadHandle(pVCpu->pUVCpu));
+        Assert((HANDLE)pVCpu->nem.s.hNativeThreadHandle != INVALID_HANDLE_VALUE);
+
+#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
+        VID_MAPPED_MESSAGE_SLOT MappedMsgSlot = { NULL, UINT32_MAX, UINT32_MAX };
+        if (g_pfnVidMessageSlotMap(hPartitionDevice, &MappedMsgSlot, iCpu))
+        {
+            AssertLogRelMsg(MappedMsgSlot.iCpu == iCpu && MappedMsgSlot.uParentAdvisory == UINT32_MAX,
+                            ("%#x %#x (iCpu=%#x)\n", MappedMsgSlot.iCpu, MappedMsgSlot.uParentAdvisory, iCpu));
+            pVCpu->nem.s.pvMsgSlotMapping = MappedMsgSlot.pMsgBlock;
+        }
+        else
+        {
+            NTSTATUS const rcNtLast  = RTNtLastStatusValue();
+            DWORD const    dwErrLast = RTNtLastErrorValue();
+            return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
+                              "Call to WHvSetupPartition failed: %Rhrc (Last=%#x/%u)", hrc, rcNtLast, dwErrLast);
+        }
+#else
         hrc = WHvCreateVirtualProcessor(hPartition, iCpu, 0 /*fFlags*/);
         if (FAILED(hrc))
         {
-            NTSTATUS const rcNtLast  = RTNtCurrentTeb()->LastStatusValue;
-            DWORD const    dwErrLast = RTNtCurrentTeb()->LastErrorValue;
+            NTSTATUS const rcNtLast  = RTNtLastStatusValue();
+            DWORD const    dwErrLast = RTNtLastErrorValue();
             while (iCpu-- > 0)
             {
                 HRESULT hrc2 = WHvDeleteVirtualProcessor(hPartition, iCpu);
                 AssertLogRelMsg(SUCCEEDED(hrc2), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc (Last=%#x/%u)\n",
-                                                  hPartition, iCpu, hrc2, RTNtCurrentTeb()->LastStatusValue,
-                                                  RTNtCurrentTeb()->LastErrorValue));
+                                                  hPartition, iCpu, hrc2, RTNtLastStatusValue(),
+                                                  RTNtLastErrorValue()));
             }
             return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
                               "Call to WHvSetupPartition failed: %Rhrc (Last=%#x/%u)", hrc, rcNtLast, dwErrLast);
         }
+#endif /* !NEM_WIN_USE_OUR_OWN_RUN_API */
     }
     pVM->nem.s.fCreatedEmts = true;
@@ -912,8 +977,12 @@
         while (iCpu-- > 0)
         {
+#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
+            pVM->aCpus[iCpu].nem.s.pvMsgSlotMapping = NULL;
+#else
             HRESULT hrc = WHvDeleteVirtualProcessor(hPartition, iCpu);
             AssertLogRelMsg(SUCCEEDED(hrc), ("WHvDeleteVirtualProcessor(%p, %u) -> %Rhrc (Last=%#x/%u)\n",
-                                             hPartition, iCpu, hrc, RTNtCurrentTeb()->LastStatusValue,
-                                             RTNtCurrentTeb()->LastErrorValue));
+                                             hPartition, iCpu, hrc, RTNtLastStatusValue(),
+                                             RTNtLastErrorValue()));
+#endif
         }
         WHvDeletePartition(hPartition);
@@ -1293,5 +1362,5 @@
     AssertLogRelMsgFailed(("WHvSetVirtualProcessorRegisters(%p, %u,,%u,) -> %Rhrc (Last=%#x/%u)\n",
                            pVM->nem.s.hPartition, pVCpu->idCpu, iReg,
-                           hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+                           hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
     return VERR_INTERNAL_ERROR;
 #endif /* !NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS */
@@ -1666,5 +1735,5 @@
     AssertLogRelMsgFailed(("WHvGetVirtualProcessorRegisters(%p, %u,,%u,) -> %Rhrc (Last=%#x/%u)\n",
                            pVM->nem.s.hPartition, pVCpu->idCpu, cRegs,
-                           hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+                           hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
     return VERR_INTERNAL_ERROR;
 #endif /* !NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS */
@@ -1673,4 +1742,407 @@
 
 #ifdef LOG_ENABLED
+/**
+ * Get the virtual processor running status.
+ */
+DECLINLINE(VID_PROCESSOR_STATUS) nemR3WinCpuGetRunningStatus(PVMCPU pVCpu)
+{
+    RTERRVARS Saved;
+    RTErrVarsSave(&Saved);
+
+    /*
+     * This API is disabled in release builds, it seems.  On build 17101 it requires
+     * the following patch to be enabled (windbg): eb vid+12180 0f 84 98 00 00 00
+     */
+    VID_PROCESSOR_STATUS enmCpuStatus = VidProcessorStatusUndefined;
+    NTSTATUS rcNt = g_pfnVidGetVirtualProcessorRunningStatus(pVCpu->pVMR3->nem.s.hPartitionDevice, pVCpu->idCpu, &enmCpuStatus);
+    AssertRC(rcNt);
+
+    RTErrVarsRestore(&Saved);
+    return enmCpuStatus;
+}
+#endif
+
+
+#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
+
+/**
+ * Our own WHvCancelRunVirtualProcessor that can later be moved to ring-0.
+ *
+ * This is an experiment only.
+ *
+ * @returns VBox status code.
+ * @param   pVM             The cross context VM structure.
+ * @param   pVCpu           The cross context virtual CPU structure of the
+ *                          calling EMT.
+ */
+static int nemR3WinCancelRunVirtualProcessor(PVM pVM, PVMCPU pVCpu)
+{
+    /*
+     * Work the state.
+     *
+     * From the looks of things, we should let the EMT call VidStopVirtualProcessor.
+     * So, we just need to modify the state and kick the EMT if it's waiting on
+     * messages.  For the latter we use QueueUserAPC / KeAlterThread.
+     */
+    for (;;)
+    {
+        VMCPUSTATE enmState = VMCPU_GET_STATE(pVCpu);
+        switch (enmState)
+        {
+            case VMCPUSTATE_STARTED_EXEC_NEM:
+                if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED, VMCPUSTATE_STARTED_EXEC_NEM))
+                {
+                    Log8(("nemR3WinCancelRunVirtualProcessor: Switched %u to canceled state\n", pVCpu->idCpu));
+                    return VINF_SUCCESS;
+                }
+                break;
+
+            case VMCPUSTATE_STARTED_EXEC_NEM_WAIT:
+            {
+                if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED, VMCPUSTATE_STARTED_EXEC_NEM_WAIT))
+                {
+                    NTSTATUS rcNt = NtAlertThread(pVCpu->nem.s.hNativeThreadHandle);
+                    Log8(("nemR3WinCancelRunVirtualProcessor: Alerted %u: %#x\n", pVCpu->idCpu, rcNt));
+                    Assert(rcNt == STATUS_SUCCESS);
+                    if (NT_SUCCESS(rcNt))
+                        return VINF_SUCCESS;
+                    AssertLogRelMsgFailedReturn(("NtAlertThread failed: %#x\n", rcNt), RTErrConvertFromNtStatus(rcNt));
+                }
+                break;
+            }
+
+            default:
+                return VINF_SUCCESS;
+        }
+
+        ASMNopPause();
+        RT_NOREF(pVM);
+    }
+}
+
+
+/** 
+ * Fills in WHV_VP_EXIT_CONTEXT from HV_X64_INTERCEPT_MESSAGE_HEADER.
+ */
+DECLINLINE(void) nemR3WinConvertX64MsgHdrToVpExitCtx(HV_X64_INTERCEPT_MESSAGE_HEADER const *pHdr, WHV_VP_EXIT_CONTEXT *pCtx)
+{
+    pCtx->ExecutionState.AsUINT16   = pHdr->ExecutionState.AsUINT16;
+    pCtx->InstructionLength         = pHdr->InstructionLength;
+    pCtx->Cs.Base                   = pHdr->CsSegment.Base;
+    pCtx->Cs.Limit                  = pHdr->CsSegment.Limit;
+    pCtx->Cs.Selector               = pHdr->CsSegment.Selector;
+    pCtx->Cs.Attributes             = pHdr->CsSegment.Attributes;
+    pCtx->Rip                       = pHdr->Rip;
+    pCtx->Rflags                    = pHdr->Rflags;
+}
+
+
+/**
+ * Convert hyper-V exit message to the WinHvPlatform structures.
+ *
+ * @returns VBox status code
+ * @param   pMsgHdr         The message to convert.
+ * @param   pExitCtx        The output structure. Assumes zeroed.
+ */
+static int nemR3WinRunVirtualProcessorConvertPending(HV_MESSAGE_HEADER const *pMsgHdr, WHV_RUN_VP_EXIT_CONTEXT *pExitCtx)
+{
+    switch (pMsgHdr->MessageType)
+    {
+        case HvMessageTypeUnmappedGpa:
+        case HvMessageTypeGpaIntercept:
+        {
+            PCHV_X64_MEMORY_INTERCEPT_MESSAGE pMemMsg = (PCHV_X64_MEMORY_INTERCEPT_MESSAGE)(pMsgHdr + 1);
+            Assert(pMsgHdr->PayloadSize == RT_UOFFSETOF(HV_X64_MEMORY_INTERCEPT_MESSAGE, DsSegment));
+
+            pExitCtx->ExitReason                            = WHvRunVpExitReasonMemoryAccess;
+            nemR3WinConvertX64MsgHdrToVpExitCtx(&pMemMsg->Header, &pExitCtx->MemoryAccess.VpContext);
+            pExitCtx->MemoryAccess.InstructionByteCount     = pMemMsg->InstructionByteCount;
+            ((uint64_t *)pExitCtx->MemoryAccess.InstructionBytes)[0] = ((uint64_t const *)pMemMsg->InstructionBytes)[0];
+            ((uint64_t *)pExitCtx->MemoryAccess.InstructionBytes)[1] = ((uint64_t const *)pMemMsg->InstructionBytes)[1];
+
+            pExitCtx->MemoryAccess.AccessInfo.AccessType    = pMemMsg->Header.InterceptAccessType;
+            pExitCtx->MemoryAccess.AccessInfo.GpaUnmapped   = pMsgHdr->MessageType == HvMessageTypeUnmappedGpa;
+            pExitCtx->MemoryAccess.AccessInfo.GvaValid      = pMemMsg->MemoryAccessInfo.GvaValid;
+            pExitCtx->MemoryAccess.AccessInfo.Reserved      = pMemMsg->MemoryAccessInfo.Reserved;
+            pExitCtx->MemoryAccess.Gpa                      = pMemMsg->GuestPhysicalAddress;
+            pExitCtx->MemoryAccess.Gva                      = pMemMsg->GuestVirtualAddress;
+            return VINF_SUCCESS;
+        }
+
+        case HvMessageTypeX64IoPortIntercept:
+        {
+            PCHV_X64_IO_PORT_INTERCEPT_MESSAGE pPioMsg= (PCHV_X64_IO_PORT_INTERCEPT_MESSAGE)(pMsgHdr + 1);
+            Assert(pMsgHdr->PayloadSize == sizeof(*pPioMsg));
+
+            pExitCtx->ExitReason                            = WHvRunVpExitReasonX64IoPortAccess;
+            nemR3WinConvertX64MsgHdrToVpExitCtx(&pPioMsg->Header, &pExitCtx->IoPortAccess.VpContext);
+            pExitCtx->IoPortAccess.InstructionByteCount     = pPioMsg->InstructionByteCount;
+            ((uint64_t *)pExitCtx->IoPortAccess.InstructionBytes)[0] = ((uint64_t const *)pPioMsg->InstructionBytes)[0];
+            ((uint64_t *)pExitCtx->IoPortAccess.InstructionBytes)[1] = ((uint64_t const *)pPioMsg->InstructionBytes)[1];
+
+            pExitCtx->IoPortAccess.AccessInfo.IsWrite       = pPioMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE;
+            pExitCtx->IoPortAccess.AccessInfo.AccessSize    = pPioMsg->AccessInfo.AccessSize;
+            pExitCtx->IoPortAccess.AccessInfo.StringOp      = pPioMsg->AccessInfo.StringOp;
+            pExitCtx->IoPortAccess.AccessInfo.RepPrefix     = pPioMsg->AccessInfo.RepPrefix;
+            pExitCtx->IoPortAccess.AccessInfo.Reserved      = pPioMsg->AccessInfo.Reserved;
+            pExitCtx->IoPortAccess.PortNumber               = pPioMsg->PortNumber;
+            pExitCtx->IoPortAccess.Rax                      = pPioMsg->Rax;
+            pExitCtx->IoPortAccess.Rcx                      = pPioMsg->Rcx;
+            pExitCtx->IoPortAccess.Rsi                      = pPioMsg->Rsi;
+            pExitCtx->IoPortAccess.Rdi                      = pPioMsg->Rdi;
+            pExitCtx->IoPortAccess.Ds.Base                  = pPioMsg->DsSegment.Base;
+            pExitCtx->IoPortAccess.Ds.Limit                 = pPioMsg->DsSegment.Limit;
+            pExitCtx->IoPortAccess.Ds.Selector              = pPioMsg->DsSegment.Selector;
+            pExitCtx->IoPortAccess.Ds.Attributes            = pPioMsg->DsSegment.Attributes;
+            pExitCtx->IoPortAccess.Es.Base                  = pPioMsg->EsSegment.Base;
+            pExitCtx->IoPortAccess.Es.Limit                 = pPioMsg->EsSegment.Limit;
+            pExitCtx->IoPortAccess.Es.Selector              = pPioMsg->EsSegment.Selector;
+            pExitCtx->IoPortAccess.Es.Attributes            = pPioMsg->EsSegment.Attributes;
+            return VINF_SUCCESS;
+        }
+
+        case HvMessageTypeX64Halt:
+        {
+            PCHV_X64_HALT_MESSAGE pHaltMsg = (PCHV_X64_HALT_MESSAGE)(pMsgHdr + 1);
+            AssertMsg(pHaltMsg->u64Reserved == 0, ("HALT reserved: %#RX64\n", pHaltMsg->u64Reserved));
+            pExitCtx->ExitReason = WHvRunVpExitReasonX64Halt;
+            return VINF_SUCCESS;
+        }
+
+        case HvMessageTypeX64InterruptWindow:
+            AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);
+
+        case HvMessageTypeInvalidVpRegisterValue:
+        case HvMessageTypeUnrecoverableException:
+        case HvMessageTypeUnsupportedFeature:
+        case HvMessageTypeTlbPageSizeMismatch:
+            AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);
+
+        case HvMessageTypeX64MsrIntercept:
+        case HvMessageTypeX64CpuidIntercept:
+        case HvMessageTypeX64ExceptionIntercept:
+        case HvMessageTypeX64ApicEoi:
+        case HvMessageTypeX64LegacyFpError:
+        case HvMessageTypeX64RegisterIntercept:
+        case HvMessageTypeApicEoi:
+        case HvMessageTypeFerrAsserted:
+        case HvMessageTypeEventLogBufferComplete:
+        case HvMessageTimerExpired:
+            AssertLogRelMsgFailedReturn(("Unexpected message type #x!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);
+
+        default:
+            AssertLogRelMsgFailedReturn(("Unknown message type #x!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);
+    }
+}
+
+
+/**
+ * Our own WHvRunVirtualProcessor that can later be moved to ring-0.
+ *
+ * This is an experiment only.
+ *
+ * @returns VBox status code.
+ * @param   pVM             The cross context VM structure.
+ * @param   pVCpu           The cross context virtual CPU structure of the
+ *                          calling EMT.
+ * @param   pExitCtx        Where to return exit information.
+ * @param   cbExitCtx       Size of the exit information area.
+ */
+static int nemR3WinRunVirtualProcessor(PVM pVM, PVMCPU pVCpu, WHV_RUN_VP_EXIT_CONTEXT *pExitCtx, size_t cbExitCtx)
+{
+    RT_BZERO(pExitCtx, cbExitCtx);
+
+    /*
+     * Tell the CPU to execute stuff if we haven't got a pending message.
+     */
+    VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader = (VID_MESSAGE_MAPPING_HEADER volatile *)pVCpu->nem.s.pvMsgSlotMapping;
+    uint32_t                             fHandleAndGetFlags;
+    if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED))
+    {
+        uint8_t const bMsgState = pVCpu->nem.s.bMsgState;
+        if (bMsgState == NEM_WIN_MSG_STATE_PENDING_MSG)
+        {
+            Assert(pMappingHeader->enmVidMsgType == VidMessageHypervisorMessage);
+            fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE | VID_MSHAGN_F_HANDLE_MESSAGE;
+            Log8(("nemR3WinRunVirtualProcessor: #1: msg pending, no need to start CPU (cpu state %u)\n", nemR3WinCpuGetRunningStatus(pVCpu) ));
+        }
+        else if (bMsgState != NEM_WIN_MSG_STATE_STARTED)
+        {
+            if (bMsgState == NEM_WIN_MSG_STATE_PENDING_STOP_AND_MSG)
+            {
+                Log8(("nemR3WinRunVirtualProcessor: #0: pending stop+message (cpu status %u)\n", nemR3WinCpuGetRunningStatus(pVCpu) ));
+                /* ACK the pending message and get the stop message. */
+                BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
+                                                                 VID_MSHAGN_F_HANDLE_MESSAGE | VID_MSHAGN_F_GET_NEXT_MESSAGE, 5000);
+                AssertLogRelMsg(fWait, ("dwErr=%u (%#x) rcNt=%#x\n", RTNtLastErrorValue(), RTNtLastErrorValue(), RTNtLastStatusValue()));
+
+                /* ACK the stop message. */
+                fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
+                                                                 VID_MSHAGN_F_HANDLE_MESSAGE, 5000);
+                AssertLogRelMsg(fWait, ("dwErr=%u (%#x) rcNt=%#x\n", RTNtLastErrorValue(), RTNtLastErrorValue(), RTNtLastStatusValue()));
+
+                pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STOPPED;
+            }
+
+            Log8(("nemR3WinRunVirtualProcessor: #1: starting CPU (cpu status %u)\n", nemR3WinCpuGetRunningStatus(pVCpu) ));
+            if (g_pfnVidStartVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu))
+                pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STARTED;
+            else
+            {
+                VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM);
+                AssertLogRelMsgFailedReturn(("VidStartVirtualProcessor failed for CPU #%u: rcNt=%#x dwErr=%u\n",
+                                             pVCpu->idCpu, RTNtLastStatusValue(), RTNtLastErrorValue()),
+                                            VERR_INTERNAL_ERROR_3);
+            }
+            fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE;
+        }
+        else
+        {
+            /* This shouldn't happen. */
+            fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE;
+            Log8(("nemR3WinRunVirtualProcessor: #1: NO MSG PENDING! No need to start CPU (cpu state %u)\n", nemR3WinCpuGetRunningStatus(pVCpu) ));
+        }
+    }
+    else
+    {
+        Log8(("nemR3WinRunVirtualProcessor: #1: state=%u -> canceled (cpu status %u)\n",
+              VMCPU_GET_STATE(pVCpu), nemR3WinCpuGetRunningStatus(pVCpu)));
+        pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;
+        return VINF_SUCCESS;
+    }
+
+    /*
+     * Wait for it to stop and give us a reason to work with.
+     */
+    uint32_t cMillies = 5000; // Starting low so we can experiment without getting stuck.
+    for (;;)
+    {
+        if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_WAIT, VMCPUSTATE_STARTED_EXEC_NEM))
+        {
+            Log8(("nemR3WinRunVirtualProcessor: #2: Waiting %#x (cpu status %u)...\n",
+                  fHandleAndGetFlags, nemR3WinCpuGetRunningStatus(pVCpu)));
+            BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
+                                                             fHandleAndGetFlags, cMillies);
+            if (fWait)
+            {
+                /* Not sure yet, but we have to check whether there is anything pending
+                   and retry if there isn't. */
+                VID_MESSAGE_TYPE const enmVidMsgType = pMappingHeader->enmVidMsgType;
+                if (enmVidMsgType == VidMessageHypervisorMessage)
+                {
+                    if (!VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_WAIT))
+                        VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);
+                    Log8(("nemR3WinRunVirtualProcessor: #3: wait succeeded: %#x / %#x (cpu status %u)\n",
+                          enmVidMsgType, ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType,
+                          nemR3WinCpuGetRunningStatus(pVCpu) ));
+                    pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_PENDING_MSG;
+                    return nemR3WinRunVirtualProcessorConvertPending((HV_MESSAGE_HEADER const *)(pMappingHeader + 1), pExitCtx);
+                }
+
+                /* This shouldn't happen, and I think its wrong. */
+                VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);
+#ifdef DEBUG_bird
+                __debugbreak();
+#endif
+                Log8(("nemR3WinRunVirtualProcessor: #3: wait succeeded, but nothing pending: %#x / %#x (cpu status %u)\n",
+                      enmVidMsgType, ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType, nemR3WinCpuGetRunningStatus(pVCpu) ));
+                pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STARTED;
+                AssertLogRelMsgReturnStmt(enmVidMsgType == VidMessageStopRequestComplete,
+                                          ("enmVidMsgType=%#x\n", enmVidMsgType),
+                                          g_pfnVidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu),
+                                          VERR_INTERNAL_ERROR_3);
+                fHandleAndGetFlags &= ~VID_MSHAGN_F_HANDLE_MESSAGE;
+            }
+            else
+            {
+                VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);
+
+                /* Note! VID.SYS merges STATUS_ALERTED and STATUS_USER_APC into STATUS_TIMEOUT. */
+                DWORD const dwErr = RTNtLastErrorValue();
+                AssertLogRelMsgReturnStmt(   dwErr == STATUS_TIMEOUT
+                                          || dwErr == STATUS_ALERTED || dwErr == STATUS_USER_APC, /* just in case */
+                                          ("dwErr=%u (%#x) (cpu status %u)\n", dwErr, dwErr, nemR3WinCpuGetRunningStatus(pVCpu)),
+                                          g_pfnVidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu),
+                                          VERR_INTERNAL_ERROR_3);
+                Log8(("nemR3WinRunVirtualProcessor: #3: wait timed out (cpu status %u)\n", nemR3WinCpuGetRunningStatus(pVCpu) ));
+                pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STARTED;
+                fHandleAndGetFlags &= ~VID_MSHAGN_F_HANDLE_MESSAGE;
+            }
+        }
+        else
+        {
+            /*
+             * State changed and we need to return.
+             *
+             * We must ensure that the processor is not running while we
+             * return, and that can be a bit complicated.
+             */
+            Log8(("nemR3WinRunVirtualProcessor: #4: state changed to %u (cpu status %u)\n",
+                  VMCPU_GET_STATE(pVCpu), nemR3WinCpuGetRunningStatus(pVCpu) ));
+            VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);
+
+            /* If we haven't marked the pervious message as handled, simply return
+               without doing anything special. */
+            if (fHandleAndGetFlags & VID_MSHAGN_F_HANDLE_MESSAGE)
+            {
+                Log8(("nemR3WinRunVirtualProcessor: #5: Didn't resume previous message.\n"));
+                pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_PENDING_MSG;
+                pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;
+                return VINF_SUCCESS;
+            }
+
+            /* The processor is running, so try stop it. */
+            BOOL fStop = g_pfnVidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu);
+            if (fStop)
+            {
+                Log8(("nemR3WinRunVirtualProcessor: #5: Stopping CPU succeeded (cpu status %u)\n", nemR3WinCpuGetRunningStatus(pVCpu) ));
+                pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STOPPED;
+                pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;
+                return VINF_SUCCESS;
+            }
+
+            /* Dang, the CPU stopped by itself with a message pending. */
+            DWORD dwErr = RTNtLastErrorValue();
+            Log8(("nemR3WinRunVirtualProcessor: #5: Stopping CPU failed (%u/%#x) - cpu status %u\n",
+                  dwErr, dwErr, nemR3WinCpuGetRunningStatus(pVCpu) ));
+            pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;
+            AssertLogRelMsgReturn(dwErr == ERROR_VID_STOP_PENDING, ("dwErr=%#u\n", dwErr), VERR_INTERNAL_ERROR_3);
+
+            /* Get the pending message. */
+            BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
+                                                             VID_MSHAGN_F_GET_NEXT_MESSAGE, 5000);
+            AssertLogRelMsgReturn(fWait, ("error=%#u\n", RTNtLastErrorValue()), VERR_INTERNAL_ERROR_3);
+
+            VID_MESSAGE_TYPE const enmVidMsgType = pMappingHeader->enmVidMsgType;
+            if (enmVidMsgType == VidMessageHypervisorMessage)
+            {
+                Log8(("nemR3WinRunVirtualProcessor: #6: wait succeeded: %#x / %#x (cpu status %u)\n", enmVidMsgType,
+                      ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType, nemR3WinCpuGetRunningStatus(pVCpu) ));
+                pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_PENDING_STOP_AND_MSG;
+                return nemR3WinRunVirtualProcessorConvertPending((HV_MESSAGE_HEADER const *)(pMappingHeader + 1), pExitCtx);
+            }
+
+            /* ACK the stop message, if that's what it is.  Don't think we'll ever get here. */
+            Log8(("nemR3WinRunVirtualProcessor: #6b: wait succeeded: %#x / %#x (cpu status %u)\n", enmVidMsgType,
+                  ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType, nemR3WinCpuGetRunningStatus(pVCpu) ));
+            AssertLogRelMsgReturn(enmVidMsgType == VidMessageStopRequestComplete, ("enmVidMsgType=%#x\n", enmVidMsgType),
+                                  VERR_INTERNAL_ERROR_3);
+            fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,
+                                                        VID_MSHAGN_F_HANDLE_MESSAGE, 5000);
+            AssertLogRelMsgReturn(fWait, ("dwErr=%#u\n", RTNtLastErrorValue()), VERR_INTERNAL_ERROR_3);
+
+            pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STOPPED;
+            pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;
+            return VINF_SUCCESS;
+        }
+
+        /** @todo check flags and stuff? */
+    }
+}
+
+#endif /* NEM_WIN_USE_OUR_OWN_RUN_API */
+
+#ifdef LOG_ENABLED
+
 /**
  * Log the full details of an exit reason.
@@ -1890,6 +2362,6 @@
 #else
         LogRel(("nemR3WinUnmapOnePageCallback: GCPhys=%RGp %s hrc=%Rhrc (%#x) Last=%#x/%u (cMappedPages=%u)\n",
-                GCPhys, g_apszPageStates[*pu2NemState], hrc, hrc, RTNtCurrentTeb()->LastStatusValue,
-                RTNtCurrentTeb()->LastErrorValue, pVM->nem.s.cMappedPages));
+                GCPhys, g_apszPageStates[*pu2NemState], hrc, hrc, RTNtLastStatusValue(),
+                RTNtLastErrorValue(), pVM->nem.s.cMappedPages));
 #endif
         *pu2NemState = NEM_WIN_PAGE_STATE_NOT_SET;
@@ -2072,5 +2544,5 @@
 #else
     LogRel(("nemR3WinHandleMemoryAccessPageCheckerCallback/unmap: GCPhysDst=%RGp %s hrc=%Rhrc (%#x) Last=%#x/%u (cMappedPages=%u)\n",
-            GCPhys, g_apszPageStates[u2State], hrc, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue,
+            GCPhys, g_apszPageStates[u2State], hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue(),
             pVM->nem.s.cMappedPages));
 
@@ -2315,4 +2787,9 @@
             && !VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_HM_TO_R3_MASK))
         {
+#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
+            int rc2 = nemR3WinRunVirtualProcessor(pVM, pVCpu, &ExitReason, sizeof(ExitReason));
+            AssertRCBreakStmt(rc2, rcStrict = rc2);
+#else
+            Log8(("Calling WHvRunVirtualProcessor\n"));
             VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED);
             HRESULT hrc = WHvRunVirtualProcessor(pVM->nem.s.hPartition, pVCpu->idCpu, &ExitReason, sizeof(ExitReason));
@@ -2320,7 +2797,9 @@
             AssertLogRelMsgBreakStmt(SUCCEEDED(hrc),
                                      ("WHvRunVirtualProcessor(%p, %u,,) -> %Rhrc (Last=%#x/%u)\n", pVM->nem.s.hPartition, pVCpu->idCpu,
-                                      hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue),
+                                      hrc, RTNtLastStatusValue(), RTNtLastErrorValue()),
                                      rcStrict = VERR_INTERNAL_ERROR);
-            Log2(("WHvRunVirtualProcessor -> %#x; exit code %#x (%d)\n", hrc, ExitReason.ExitReason, ExitReason.ExitReason));
+            Log2(("WHvRunVirtualProcessor -> %#x; exit code %#x (%d) (cpu status %u)\n",
+                  hrc, ExitReason.ExitReason, ExitReason.ExitReason, nemR3WinCpuGetRunningStatus(pVCpu) ));
+#endif
         }
         else
@@ -2494,8 +2973,12 @@
 void nemR3NativeNotifyFF(PVM pVM, PVMCPU pVCpu, uint32_t fFlags)
 {
+#ifdef NEM_WIN_USE_OUR_OWN_RUN_API
+    nemR3WinCancelRunVirtualProcessor(pVM, pVCpu);
+#else
+    Log8(("nemR3NativeNotifyFF: canceling %u\n", pVCpu->idCpu));
     HRESULT hrc = WHvCancelRunVirtualProcessor(pVM->nem.s.hPartition, pVCpu->idCpu, 0);
     AssertMsg(SUCCEEDED(hrc), ("WHvCancelRunVirtualProcessor -> hrc=%Rhrc\n", hrc));
-
     RT_NOREF_PV(hrc);
+#endif
     RT_NOREF_PV(fFlags);
 }
@@ -2577,5 +3060,5 @@
             {
                 LogRel(("nemR3NativeNotifyPhysRomRegisterEarly: GCPhys=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
-                        GCPhys, hrc, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+                        GCPhys, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
                 return VERR_NEM_INIT_FAILED;
             }
@@ -2644,5 +3127,5 @@
 #else
             LogRel(("nemR3WinUnsetForA20CheckerCallback/unmap: GCPhys=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
-                    GCPhys, hrc, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+                    GCPhys, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
             return VERR_INTERNAL_ERROR_2;
 #endif
@@ -2858,5 +3341,5 @@
             {
                 LogRel(("nemR3NativeSetPhysPage/unmap: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
-                        GCPhysDst, hrc, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+                        GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
                 return VERR_NEM_INIT_FAILED;
             }
@@ -2901,5 +3384,5 @@
             }
             LogRel(("nemR3NativeSetPhysPage/writable: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
-                    GCPhysDst, hrc, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+                    GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
             return VERR_NEM_INIT_FAILED;
         }
@@ -2941,5 +3424,5 @@
             }
             LogRel(("nemR3NativeSetPhysPage/readonly: GCPhysDst=%RGp hrc=%Rhrc (%#x) Last=%#x/%u\n",
-                    GCPhysDst, hrc, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+                    GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
             return VERR_NEM_INIT_FAILED;
         }
@@ -2988,5 +3471,5 @@
     }
     LogRel(("nemR3JustUnmapPageFromHyperV(%RGp): failed! hrc=%Rhrc (%#x) Last=%#x/%u\n",
-            GCPhysDst, hrc, hrc, RTNtCurrentTeb()->LastStatusValue, RTNtCurrentTeb()->LastErrorValue));
+            GCPhysDst, hrc, hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
     return VERR_INTERNAL_ERROR_3;
 #endif
Index: /trunk/src/VBox/VMM/VMMR3/VM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/VM.cpp	(revision 71128)
+++ /trunk/src/VBox/VMM/VMMR3/VM.cpp	(revision 71129)
@@ -4533,5 +4533,5 @@
 
 /**
- * Returns the native handle of the current EMT VMCPU thread.
+ * Returns the native ID of the current EMT VMCPU thread.
  *
  * @returns Handle if this is an EMT thread; NIL_RTNATIVETHREAD otherwise
@@ -4551,5 +4551,5 @@
 
 /**
- * Returns the native handle of the current EMT VMCPU thread.
+ * Returns the native ID of the current EMT VMCPU thread.
  *
  * @returns Handle if this is an EMT thread; NIL_RTNATIVETHREAD otherwise
@@ -4582,4 +4582,17 @@
         return NIL_RTTHREAD;
 
+    return pUVCpu->vm.s.ThreadEMT;
+}
+
+
+/**
+ * Returns the handle of the current EMT VMCPU thread.
+ *
+ * @returns The IPRT thread handle.
+ * @param   pUVCpu          The user mode CPU handle.
+ * @thread  EMT
+ */
+VMMR3_INT_DECL(RTTHREAD) VMR3GetThreadHandle(PUVMCPU pUVCpu)
+{
     return pUVCpu->vm.s.ThreadEMT;
 }
Index: /trunk/src/VBox/VMM/VMMR3/VMEmt.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/VMEmt.cpp	(revision 71128)
+++ /trunk/src/VBox/VMM/VMMR3/VMEmt.cpp	(revision 71129)
@@ -859,5 +859,6 @@
                 }
             }
-            else if (enmState == VMCPUSTATE_STARTED_EXEC_NEM)
+            else if (   enmState == VMCPUSTATE_STARTED_EXEC_NEM
+                     || enmState == VMCPUSTATE_STARTED_EXEC_NEM_WAIT)
                 NEMR3NotifyFF(pUVCpu->pVM, pVCpu, fFlags);
 #ifdef VBOX_WITH_REM
@@ -1001,5 +1002,6 @@
         {
             VMCPUSTATE enmState = pVCpu->enmState;
-            if (enmState == VMCPUSTATE_STARTED_EXEC_NEM)
+            if (   enmState == VMCPUSTATE_STARTED_EXEC_NEM
+                || enmState == VMCPUSTATE_STARTED_EXEC_NEM_WAIT)
                 NEMR3NotifyFF(pUVCpu->pVM, pVCpu, fFlags);
 #ifdef VBOX_WITH_REM
Index: /trunk/src/VBox/VMM/include/NEMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 71128)
+++ /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 71129)
@@ -37,4 +37,17 @@
  * @{
  */
+
+
+#ifdef RT_OS_WINDOWS
+/*
+ * Windows: Code configuration.
+ */
+# define NEM_WIN_USE_HYPERCALLS_FOR_PAGES
+# define NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS
+# define NEM_WIN_USE_OUR_OWN_RUN_API
+# if defined(NEM_WIN_USE_OUR_OWN_RUN_API) && !defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS)
+#  error "NEM_WIN_USE_OUR_OWN_RUN_API requires NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS"
+# endif
+#endif
 
 /**
@@ -102,4 +115,19 @@
 #define NEM_MAGIC_DEAD          UINT32_C(0xdead1111)
 
+#if defined(RT_OS_WINDOWS) && defined(NEM_WIN_USE_OUR_OWN_RUN_API)
+/** @name NEM_WIN_MSG_STATE_XXX - Windows message handling state.
+ * @{ */
+/** The CPU has not been started. */
+# define NEM_WIN_MSG_STATE_STOPPED              UINT8_C(0x00)
+/** The CPU has been started, no messages are pending. */
+# define NEM_WIN_MSG_STATE_STARTED              UINT8_C(0x01)
+/** Message is pending and needs to be ACKed. */
+# define NEM_WIN_MSG_STATE_PENDING_MSG          UINT8_C(0x02)
+/** Both a message and execution stopping is pending.  We need to ACK the
+ * current message and get the stop message, then ACK the stop message before
+ *  the CPU can be started again.  */
+# define NEM_WIN_MSG_STATE_PENDING_STOP_AND_MSG UINT8_C(0x03)
+/** @} */
+#endif
 
 /**
@@ -111,4 +139,12 @@
     uint32_t                    u32Magic;
 #ifdef RT_OS_WINDOWS
+# ifdef NEM_WIN_USE_OUR_OWN_RUN_API
+    /** We've got a message pending (NEM_WIN_MSG_STATE_XXX). */
+    uint8_t                     bMsgState;
+    /** What VidMessageSlotMap returns and is used for passing exit info. */
+    RTR3PTR                     pvMsgSlotMapping;
+# endif
+    /** The windows thread handle. */
+    RTR3PTR                     hNativeThreadHandle;
     /** Parameters for making Hyper-V hypercalls. */
     union
