Index: trunk/src/VBox/VMM/VMMAll/GIMAllHv.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/GIMAllHv.cpp	(revision 58247)
+++ trunk/src/VBox/VMM/VMMAll/GIMAllHv.cpp	(revision 58248)
@@ -172,5 +172,5 @@
                         && rcHv == GIM_HV_STATUS_SUCCESS)
                     {
-                        LogRelMax(1, ("GIM: HyperV: Guest initiated debug data reception\n"));
+                        LogRelMax(1, ("GIM: HyperV: Guest initiated debug data reception via hypercall\n"));
                         rc = gimR3HvHypercallRetrieveDebugData(pVM, GCPhysOut, &rcHv);
                         if (RT_FAILURE(rc))
@@ -192,5 +192,5 @@
                         && rcHv == GIM_HV_STATUS_SUCCESS)
                     {
-                        LogRelMax(1, ("GIM: HyperV: Guest initiated debug data transmission\n"));
+                        LogRelMax(1, ("GIM: HyperV: Guest initiated debug data transmission via hypercall\n"));
                         rc = gimR3HvHypercallPostDebugData(pVM, GCPhysOut, &rcHv);
                         if (RT_FAILURE(rc))
@@ -234,5 +234,5 @@
                             rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
                         else
-                            LogRelMax(1, ("GIM: HyperV: Guest resetting debug session\n"));
+                            LogRelMax(1, ("GIM: HyperV: Guest resetting debug session via hypercall\n"));
                     }
                 }
@@ -396,4 +396,30 @@
                 return VERR_CPUM_RAISE_GP_0;
             return VINF_SUCCESS;
+        }
+
+        case MSR_GIM_HV_SYNTH_DEBUG_STATUS:
+            *puValue = pHv->uDebugStatusMsr;
+            return VINF_SUCCESS;
+
+        case MSR_GIM_HV_SINT2:
+        {
+#ifndef IN_RING3
+            return VINF_CPUM_R3_MSR_READ;
+#else
+            LogRelMax(10, ("GIM: HyperV: reading MSR_GIM_HV_SINT2 CS:RIP=%04x:%RX64\n", CPUMGetGuestCS(pVCpu), CPUMGetGuestRIP(pVCpu)));
+            *puValue = RT_BIT_64(16);
+            return VERR_CPUM_RAISE_GP_0;
+#endif
+        }
+
+        case MSR_GIM_HV_SIMP:
+        {
+#ifndef IN_RING3
+            return VINF_CPUM_R3_MSR_READ;
+#else
+            LogRelMax(10, ("GIM: HyperV: reading MSR_GIM_HV_SIMP CS:RIP=%04x:%RX64\n", CPUMGetGuestCS(pVCpu), CPUMGetGuestRIP(pVCpu)));
+            *puValue = 0;
+            return VINF_SUCCESS;
+#endif
         }
 
@@ -640,4 +666,156 @@
         }
 
+        case MSR_GIM_HV_SYNTH_DEBUG_SEND_BUFFER:
+        {
+#ifndef IN_RING3
+            return VINF_CPUM_R3_MSR_WRITE;
+#else
+            RTGCPHYS GCPhysBuffer    = (RTGCPHYS)uRawValue;
+            pHv->uDebugSendBufferMsr = GCPhysBuffer;
+            if (PGMPhysIsGCPhysNormal(pVM, GCPhysBuffer))
+                LogRel(("GIM: HyperV: Guest set up debug send buffer at %#RGp\n", GCPhysBuffer));
+            else
+                LogRel(("GIM: HyperV: Guest destroyed debug send buffer\n"));
+            pHv->uDebugSendBufferMsr = uRawValue;
+            return VINF_SUCCESS;
+#endif
+        }
+
+        case MSR_GIM_HV_SYNTH_DEBUG_RECEIVE_BUFFER:
+        {
+#ifndef IN_RING3
+            return VINF_CPUM_R3_MSR_WRITE;
+#else
+            RTGCPHYS GCPhysBuffer    = (RTGCPHYS)uRawValue;
+            pHv->uDebugRecvBufferMsr = GCPhysBuffer;
+            if (PGMPhysIsGCPhysNormal(pVM, GCPhysBuffer))
+                LogRel(("GIM: HyperV: Guest set up debug receive buffer at %#RGp\n", GCPhysBuffer));
+            else
+                LogRel(("GIM: HyperV: Guest destroyed debug receive buffer\n"));
+            return VINF_SUCCESS;
+#endif
+        }
+
+        case MSR_GIM_HV_SYNTH_DEBUG_PENDING_BUFFER:
+        {
+#ifndef IN_RING3
+            return VINF_CPUM_R3_MSR_WRITE;
+#else
+            RTGCPHYS GCPhysBuffer        = (RTGCPHYS)uRawValue;
+            pHv->uDebugPendingBufferMsr  = GCPhysBuffer;
+            if (PGMPhysIsGCPhysNormal(pVM, GCPhysBuffer))
+            {
+                LogRel(("GIM: HyperV: Guest set up debug pending buffer at %#RGp\n", uRawValue));
+
+                /* Indicate that there is always debug data to be read (guest will poll). */
+                uint8_t uPendingData = 1;
+                int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysBuffer, (void *)&uPendingData, sizeof(uPendingData));
+                if (RT_FAILURE(rc))
+                    LogRelMax(5, ("GIM: HyperV: Failed to update pending buffer at %#RGp, rc=%Rrc\n", GCPhysBuffer, rc));
+            }
+            else
+                LogRel(("GIM: HyperV: Guest destroyed debug pending buffer\n"));
+            return VINF_SUCCESS;
+#endif
+        }
+
+        case MSR_GIM_HV_SYNTH_DEBUG_CONTROL:
+        {
+#ifndef IN_RING3
+            return VINF_CPUM_R3_MSR_WRITE;
+#else
+            if (MSR_GIM_HV_SYNTH_DEBUG_CONTROL_IS_WRITE(uRawValue))
+            {
+                LogRelMax(1, ("GIM: HyperV: Guest initiated debug data transmission via MSR\n",
+                              MSR_GIM_HV_SYNTH_DEBUG_CONTROL_W_LEN(uRawValue)));
+                size_t cbWrite = MSR_GIM_HV_SYNTH_DEBUG_CONTROL_W_LEN(uRawValue);
+                if (   cbWrite > 0
+                    && cbWrite < GIM_HV_PAGE_SIZE)
+                {
+                    void *pvBuf = RTMemAlloc(cbWrite);  /** @todo perhaps we can do this alloc once during VM init. */
+                    if (RT_LIKELY(pvBuf))
+                    {
+                        if (PGMPhysIsGCPhysNormal(pVM, (RTGCPHYS)pHv->uDebugSendBufferMsr))
+                        {
+                            int rc = PGMPhysSimpleReadGCPhys(pVM, pvBuf, (RTGCPHYS)pHv->uDebugSendBufferMsr, cbWrite);
+                            if (RT_SUCCESS(rc))
+                            {
+                                uint32_t cbWritten = 0;
+                                rc = gimR3HvDebugWrite(pVM, pvBuf, cbWrite, &cbWritten, false /*fUdpPkt*/);
+                                if (   RT_SUCCESS(rc)
+                                    && cbWrite == cbWritten)
+                                    pHv->uDebugStatusMsr = MSR_GIM_HV_SYNTH_DEBUG_STATUS_W_SUCCESS_BIT;
+                                else
+                                    pHv->uDebugStatusMsr = 0;
+                            }
+                            else
+                            {
+                                LogRelMax(5, ("GIM: HyperV: Failed to read debug send buffer at %#RGp, rc=%Rrc\n",
+                                              (RTGCPHYS)pHv->uDebugSendBufferMsr, rc));
+                            }
+                        }
+                        else
+                            LogRelMax(5, ("GIM: HyperV: Debug send buffer address %#RGp invalid! Ignoring debug write\n",
+                                          (RTGCPHYS)pHv->uDebugSendBufferMsr));
+                        RTMemFree(pvBuf);
+                    }
+                    else
+                    {
+                        LogRel(("GIM: HyperV: Failed to alloc %u bytes for copying debug send buffer\n", cbWrite));
+                        return VERR_NO_MEMORY;
+                    }
+                }
+            }
+            else if (MSR_GIM_HV_SYNTH_DEBUG_CONTROL_IS_READ(uRawValue))
+            {
+                LogRelMax(1, ("GIM: HyperV: Guest initiated debug data reception via MSR\n"));
+                if (PGMPhysIsGCPhysNormal(pVM, (RTGCPHYS)pHv->uDebugRecvBufferMsr))
+                {
+                    void *pvBuf = RTMemAlloc(PAGE_SIZE);        /** @todo perhaps we can do this alloc once during VM init. */
+                    if (RT_LIKELY(pvBuf))
+                    {
+                        uint32_t cbReallyRead;
+                        int rc = gimR3HvDebugRead(pVM, pvBuf, PAGE_SIZE, PAGE_SIZE, &cbReallyRead, 0, false /*fUdpPkt*/);
+                        if (   RT_SUCCESS(rc)
+                            && cbReallyRead > 0)
+                        {
+                            rc = PGMPhysSimpleWriteGCPhys(pVM, (RTGCPHYS)pHv->uDebugRecvBufferMsr, pvBuf, cbReallyRead);
+                            if (RT_SUCCESS(rc))
+                            {
+                                pHv->uDebugStatusMsr  = ((uint16_t)cbReallyRead) << 16;
+                                pHv->uDebugStatusMsr |= MSR_GIM_HV_SYNTH_DEBUG_STATUS_R_SUCCESS_BIT;
+                            }
+                            else
+                            {
+                                pHv->uDebugStatusMsr = 0;
+                                LogRelMax(5, ("GIM: HyperV: PGMPhysSimpleWriteGCPhys failed. rc=%Rrc\n", rc));
+                            }
+                        }
+                        else
+                            pHv->uDebugStatusMsr = 0;
+                        RTMemFree(pvBuf);
+                    }
+                    else
+                    {
+                        LogRel(("GIM: HyperV: Failed to alloc %u bytes for copying debug receive buffer\n", PAGE_SIZE));
+                        return VERR_NO_MEMORY;
+                    }
+                }
+                else
+                    LogRelMax(5, ("GIM: HyperV: Debug receive buffer address %#RGp invalid! Ignoring debug data reception\n"));
+            }
+            return VINF_SUCCESS;
+#endif
+        }
+
+        case MSR_GIM_HV_SINT2:
+#ifndef IN_RING3
+            return VINF_CPUM_R3_MSR_WRITE;
+#else
+            LogRelMax(5, ("GIM: HyperV: Guest writing MSR_GIM_HV_SINT2 with %#RX64, ignoring CS:RIP=%04x:%RX64\n", uRawValue,
+                           CPUMGetGuestCS(pVCpu), CPUMGetGuestRIP(pVCpu)));
+            return VERR_CPUM_RAISE_GP_0;
+#endif
+
         case MSR_GIM_HV_CRASH_P0:  pHv->uCrashP0 = uRawValue;  return VINF_SUCCESS;
         case MSR_GIM_HV_CRASH_P1:  pHv->uCrashP1 = uRawValue;  return VINF_SUCCESS;
@@ -660,5 +838,5 @@
                 return VINF_CPUM_R3_MSR_WRITE;
 #else
-                LogRelMax(1, ("GIM: HyperV: Guest setting debug options MSR to %#RX64, ignoring\n", uRawValue));
+                LogRelMax(5, ("GIM: HyperV: Guest setting debug options MSR to %#RX64, ignoring\n", uRawValue));
                 return VINF_SUCCESS;
 #endif
Index: trunk/src/VBox/VMM/VMMR3/GIM.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/GIM.cpp	(revision 58247)
+++ trunk/src/VBox/VMM/VMMR3/GIM.cpp	(revision 58248)
@@ -110,4 +110,16 @@
     PCFGMNODE pCfgNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "GIM/");
 
+    /*
+     * Validate the GIM settings.
+     */
+    rc = CFGMR3ValidateConfig(pCfgNode, "/GIM/",
+                              "Provider"   /* pszValidValues */
+                              "|Version",
+                              "HyperV"     /* pszValidNodes */,
+                              "GIM"        /* pszWho */,
+                              0            /* uInstance */);
+    if (RT_FAILURE(rc))
+        return rc;
+
     /** @cfgm{/GIM/Provider, string}
      * The name of the GIM provider. The default is "none". */
@@ -143,5 +155,5 @@
         {
             pVM->gim.s.enmProviderId = GIMPROVIDERID_HYPERV;
-            rc = gimR3HvInit(pVM);
+            rc = gimR3HvInit(pVM, pCfgNode);
         }
         else if (!RTStrCmp(szProvider, "KVM"))
Index: trunk/src/VBox/VMM/VMMR3/GIMHv.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/GIMHv.cpp	(revision 58247)
+++ trunk/src/VBox/VMM/VMMR3/GIMHv.cpp	(revision 58248)
@@ -84,6 +84,6 @@
 *   Internal Functions                                                                                                           *
 *********************************************************************************************************************************/
-static int    gimR3HvInitDebugSupport(PVM pVM);
-static void   gimR3HvTermDebugSupport(PVM pVM);
+static int    gimR3HvInitHypercallSupport(PVM pVM);
+static void   gimR3HvTermHypercallSupport(PVM pVM);
 
 
@@ -94,5 +94,5 @@
  * @param   pVM         The cross context VM structure.
  */
-VMMR3_INT_DECL(int) gimR3HvInit(PVM pVM)
+VMMR3_INT_DECL(int) gimR3HvInit(PVM pVM, PCFGMNODE pGimCfg)
 {
     AssertReturn(pVM, VERR_INVALID_PARAMETER);
@@ -105,10 +105,21 @@
      * Read configuration.
      */
-    PCFGMNODE pCfgNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "GIM/HyperV");
+    PCFGMNODE pCfgHv = CFGMR3GetChild(pGimCfg, "HyperV");
+    if (pCfgHv)
+    {
+        /*
+         * Validate the Hyper-V settings.
+         */
+        rc = CFGMR3ValidateConfig(pCfgHv, "/HyperV/",
+                                  "VendorID",
+                                  "" /* pszValidNodes */, "GIM/HyperV" /* pszWho */, 0 /* uInstance */);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
 
     /** @cfgm{/GIM/HyperV/VendorID, string, 'VBoxVBoxVBox'}
      * The Hyper-V vendor signature, must be 12 characters. */
     char szVendor[13];
-    rc = CFGMR3QueryStringDef(pCfgNode, "VendorID", szVendor, sizeof(szVendor), "VBoxVBoxVBox");
+    rc = CFGMR3QueryStringDef(pCfgHv, "VendorID", szVendor, sizeof(szVendor), "VBoxVBoxVBox");
     AssertLogRelRCReturn(rc, rc);
 
@@ -119,4 +130,6 @@
         pHv->fIsVendorMsHv = true;
     }
+
+    pHv->fIsInterfaceVs = true;
 
     /*
@@ -156,5 +169,6 @@
 
         /* Expose more if we're posing as Microsoft. */
-        if (pHv->fIsVendorMsHv)
+        if (   pHv->fIsVendorMsHv
+            /*&& !pHv->fIsInterfaceVs*/)
         {
             pHv->uMiscFeat  |= GIM_HV_MISC_FEAT_GUEST_DEBUGGING
@@ -274,4 +288,32 @@
     AssertLogRelRCReturn(rc, rc);
 
+    if (   pHv->fIsVendorMsHv
+        && pHv->fIsInterfaceVs)
+    {
+        HyperLeaf.uLeaf        = UINT32_C(0x40000080);
+        HyperLeaf.uEax         = 0;
+        HyperLeaf.uEbx         = 0x7263694d;        /* 'rciM' */
+        HyperLeaf.uEcx         = 0x666f736f;        /* 'foso'*/
+        HyperLeaf.uEdx         = 0x53562074;        /* 'SV t' */
+        rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
+        AssertLogRelRCReturn(rc, rc);
+
+        HyperLeaf.uLeaf        = UINT32_C(0x40000081);
+        HyperLeaf.uEax         = 0x31235356;        /* '1#SV' */
+        HyperLeaf.uEbx         = 0;
+        HyperLeaf.uEcx         = 0;
+        HyperLeaf.uEdx         = 0;
+        rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
+        AssertLogRelRCReturn(rc, rc);
+
+        HyperLeaf.uLeaf        = UINT32_C(0x40000082);
+        HyperLeaf.uEax         = RT_BIT_32(1);
+        HyperLeaf.uEbx         = 0;
+        HyperLeaf.uEcx         = 0;
+        HyperLeaf.uEdx         = 0;
+        rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
+        AssertLogRelRCReturn(rc, rc);
+    }
+
     /*
      * Insert all MSR ranges of Hyper-V.
@@ -290,9 +332,9 @@
 
     /*
-     * Setup guest-host debugging connection.
+     * Setup guest-host hypercall based debugging support.
      */
     if (pHv->uMiscFeat & GIM_HV_MISC_FEAT_GUEST_DEBUGGING)
     {
-        rc = gimR3HvInitDebugSupport(pVM);
+        rc = gimR3HvInitHypercallSupport(pVM);
         AssertLogRelRCReturn(rc, rc);
     }
@@ -366,5 +408,5 @@
     PGIMHV pHv = &pVM->gim.s.u.Hv;
     if (pHv->uMiscFeat & GIM_HV_MISC_FEAT_GUEST_DEBUGGING)
-        gimR3HvTermDebugSupport(pVM);
+        gimR3HvTermHypercallSupport(pVM);
     return VINF_SUCCESS;
 }
@@ -422,12 +464,16 @@
      * Reset MSRs (Careful! Don't reset non-zero MSRs).
      */
-    pHv->u64GuestOsIdMsr = 0;
-    pHv->u64HypercallMsr = 0;
-    pHv->u64TscPageMsr   = 0;
-    pHv->uCrashP0        = 0;
-    pHv->uCrashP1        = 0;
-    pHv->uCrashP2        = 0;
-    pHv->uCrashP3        = 0;
-    pHv->uCrashP4        = 0;
+    pHv->u64GuestOsIdMsr        = 0;
+    pHv->u64HypercallMsr        = 0;
+    pHv->u64TscPageMsr          = 0;
+    pHv->uCrashP0               = 0;
+    pHv->uCrashP1               = 0;
+    pHv->uCrashP2               = 0;
+    pHv->uCrashP3               = 0;
+    pHv->uCrashP4               = 0;
+    pHv->uDebugStatusMsr        = 0;
+    pHv->uDebugPendingBufferMsr = 0;
+    pHv->uDebugSendBufferMsr    = 0;
+    pHv->uDebugRecvBufferMsr    = 0;
 }
 
@@ -909,10 +955,10 @@
 
 /**
- * Initializes Hyper-V guest debugging support.
+ * Initializes Hyper-V guest hypercall support.
  *
  * @returns VBox status code.
  * @param   pVM     The cross context VM structure.
  */
-static int gimR3HvInitDebugSupport(PVM pVM)
+static int gimR3HvInitHypercallSupport(PVM pVM)
 {
     int rc = VINF_SUCCESS;
@@ -931,9 +977,9 @@
 
 /**
- * Terminates Hyper-V guest debugging support.
+ * Terminates Hyper-V guest hypercall support.
  *
  * @param   pVM     The cross context VM structure.
  */
-static void gimR3HvTermDebugSupport(PVM pVM)
+static void gimR3HvTermHypercallSupport(PVM pVM)
 {
     PGIMHV pHv = &pVM->gim.s.u.Hv;
@@ -952,18 +998,82 @@
  * @param   pVM         The cross context VM structure.
  * @param   pvBuf       Where to read the data.
- * @param   cbBuf       Size of the read buffer @a pvBuf.
+ * @param   cbBuf       Size of the read buffer @a pvBuf, must be >= @a cbRead.
+ * @param   cbRead      Number of bytes to read.
  * @param   pcbRead     Where to store how many bytes were really read.
  * @param   cMsTimeout  Timeout of the read operation in milliseconds.
+ * @param   fUdpPkt     Whether the debug data returned in @a pvBuf needs to be
+ *                      encapsulated in a UDP frame.
  *
  * @thread  EMT.
  */
-static int gimR3HvDebugRead(PVM pVM, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead, uint32_t cMsTimeout)
-{
-    NOREF(cMsTimeout);      /** @todo implement */
-    PGIMHV pHv = &pVM->gim.s.u.Hv;
+VMMR3_INT_DECL(int) gimR3HvDebugRead(PVM pVM, void *pvBuf, uint32_t cbBuf, uint32_t cbRead, uint32_t *pcbRead,
+                                     uint32_t cMsTimeout, bool fUdpPkt)
+{
+    NOREF(cMsTimeout);      /** @todo implement timeout. */
     AssertCompile(sizeof(size_t) >= sizeof(uint32_t));
-    size_t cbRead = cbBuf;
-    int rc = GIMR3DebugRead(pVM, pvBuf, &cbRead);
-    *pcbRead = (uint32_t)cbRead;
+    AssertReturn(cbBuf >= cbRead, VERR_INVALID_PARAMETER);
+
+    /*
+     * Read the data.
+     */
+    size_t cbReallyRead = cbRead;
+    int rc = GIMR3DebugRead(pVM, pvBuf, &cbReallyRead);
+
+    /*
+     * Encapsulate it in a UDP packet if required.
+     */
+    if (   RT_SUCCESS(rc)
+        && fUdpPkt
+        && cbReallyRead > 0)
+    {
+        uint8_t abFrame[sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + sizeof(RTNETUDP)];
+        if (cbReallyRead + sizeof(abFrame) <= cbBuf)
+        {
+            /*
+             * Windows guests pumps ethernet frames over the Hyper-V debug connection as
+             * explained in gimR3HvHypercallPostDebugData(). Here, we reconstruct the packet
+             * with the guest's self-chosen IP ARP address we saved in pHv->DbgGuestAddr.
+             *
+             * Note! We really need to pass the minimum IPv4 header length. The Windows 10 guest
+             * is -not- happy if we include the IPv4 options field, i.e. using sizeof(RTNETIPV4)
+             * instead of RTNETIPV4_MIN_LEN.
+             */
+            PGIMHV pHv = &pVM->gim.s.u.Hv;
+            RT_ZERO(abFrame);
+            PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
+            PRTNETIPV4     pIpHdr  = (PRTNETIPV4)    (pEthHdr + 1);
+            PRTNETUDP      pUdpHdr = (PRTNETUDP)     ((uint8_t *)pIpHdr + RTNETIPV4_MIN_LEN);
+
+            /* Ethernet */
+            pEthHdr->EtherType = RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4);
+            /* IPv4 */
+            pIpHdr->ip_v       = 4;
+            pIpHdr->ip_hl      = RTNETIPV4_MIN_LEN / sizeof(uint32_t);
+            pIpHdr->ip_tos     = 0;
+            pIpHdr->ip_len     = RT_H2N_U16((uint16_t)cbReallyRead + sizeof(RTNETUDP) + RTNETIPV4_MIN_LEN);
+            pIpHdr->ip_id      = 0;
+            pIpHdr->ip_off     = 0;
+            pIpHdr->ip_ttl     = 255;
+            pIpHdr->ip_p       = RTNETIPV4_PROT_UDP;
+            pIpHdr->ip_sum     = 0;
+            pIpHdr->ip_src.u   = 0;
+            pIpHdr->ip_dst.u   = pHv->DbgGuestAddr.u;
+            pIpHdr->ip_sum     = RTNetIPv4HdrChecksum(pIpHdr);
+            /* UDP */
+            pUdpHdr->uh_ulen   = RT_H2N_U16_C((uint16_t)cbReallyRead + sizeof(*pUdpHdr));
+
+            /* Make room by moving the payload and prepending the headers. */
+            uint8_t *pbData = (uint8_t *)pvBuf;
+            memmove(pbData + sizeof(abFrame), pbData, cbReallyRead);
+            memcpy(pbData, &abFrame[0], sizeof(abFrame));
+
+            /* Update the adjusted sizes. */
+            cbReallyRead += sizeof(abFrame);
+        }
+        else
+            rc = VERR_BUFFER_UNDERFLOW;
+    }
+
+    *pcbRead = (uint32_t)cbReallyRead;
     return rc;
 }
@@ -975,18 +1085,119 @@
  * @returns VBox status code.
  * @param   pVM         The cross context VM structure.
- * @param   pvBuf       Pointer to the data to be written.
- * @param   cbBuf       Size of the write buffer @a pvBuf.
- * @param   pcbWritten  Where to store how many bytes were really written.
+ * @param   pvData      Pointer to the data to be written.
+ * @param   cbWrite     Size of the write buffer @a pvData.
+ * @param   pcbWritten  Where to store the number of bytes written.
+ * @param   fUdpPkt     Whether the debug data in @a pvData is encapsulated in a
+ *                      UDP frame.
  *
  * @thread  EMT.
  */
-static int gimR3HvDebugWrite(PVM pVM, void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
-{
-    PGIMHV pHv = &pVM->gim.s.u.Hv;
-    AssertCompile(sizeof(size_t) >= sizeof(uint32_t));
-    size_t cbWrite = cbBuf;
-    int rc = GIMR3DebugWrite(pVM, pvBuf, &cbWrite);
-    *pcbWritten = (uint32_t)cbWrite;
-    return rc;
+VMMR3_INT_DECL(int) gimR3HvDebugWrite(PVM pVM, void *pvData, uint32_t cbWrite, uint32_t *pcbWritten, bool fUdpPkt)
+{
+    Assert(cbWrite > 0);
+
+    PGIMHV    pHv        = &pVM->gim.s.u.Hv;
+    bool      fIgnorePkt = false;
+    uint8_t  *pbData     = (uint8_t *)pvData;
+    if (fUdpPkt)
+    {
+        /*
+         * Windows guests sends us ethernet frames over the Hyper-V debug connection.
+         * It sends DHCP/ARP queries with zero'd out MAC addresses and requires fudging up the
+         * packets somewhere.
+         *
+         * The Microsoft WinDbg debugger talks UDP and thus only expects the actual debug
+         * protocol payload.
+         *
+         * At present, we only handle guests configured with the "nodhcp" option. This makes
+         * the guest send ARP queries with a self-chosen IP and after a couple of attempts of
+         * receiving no replies, the guest picks its own IP address. After this, the guest
+         * starts sending the UDP packets we require. We thus ignore the initial ARP packets
+         * (and to be safe all non-UDP packets) until the guest eventually starts talking
+         * UDP. Then we can finally feed the UDP payload over the debug connection.
+         */
+        if (cbWrite > sizeof(RTNETETHERHDR))
+        {
+            PCRTNETETHERHDR pEtherHdr = (PCRTNETETHERHDR)pbData;
+            if (pEtherHdr->EtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4))
+            {
+                if (cbWrite > sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN)
+                {
+                    size_t const cbMaxIpHdr = cbWrite - sizeof(RTNETETHERHDR) - sizeof(RTNETUDP) - 1;
+                    size_t const cbMaxIpPkt = cbWrite - sizeof(RTNETETHERHDR);
+                    PCRTNETIPV4  pIp4Hdr    = (PCRTNETIPV4)(pbData + sizeof(RTNETETHERHDR));
+                    bool const   fValidIp4  = RTNetIPv4IsHdrValid(pIp4Hdr, cbMaxIpHdr, cbMaxIpPkt, false /*fChecksum*/);
+                    if (   fValidIp4
+                        && pIp4Hdr->ip_p == RTNETIPV4_PROT_UDP)
+                    {
+                        uint32_t const cbIpHdr     = pIp4Hdr->ip_hl * 4;
+                        uint32_t const cbMaxUdpPkt = cbWrite - sizeof(RTNETETHERHDR) - cbIpHdr;
+                        PCRTNETUDP pUdpHdr       = (PCRTNETUDP)((uint8_t *)pIp4Hdr + cbIpHdr);
+                        if (   pUdpHdr->uh_ulen >  RT_H2N_U16(sizeof(RTNETUDP))
+                            && pUdpHdr->uh_ulen <= RT_H2N_U16((uint16_t)cbMaxUdpPkt))
+                        {
+                            /*
+                             * Extract the UDP payload and pass it to the debugger and record the guest IP address.
+                             * Hyper-V sends UDP debugger packets with source and destination port as 0. If we don't
+                             * filter out the ports here, we would receive BOOTP, NETBIOS and other UDP sub-protocol
+                             * packets which the debugger yells as "Bad packet received from...".
+                             */
+                            if (   !pUdpHdr->uh_dport
+                                && !pUdpHdr->uh_sport)
+                            {
+                                uint32_t const cbFrameHdr = sizeof(RTNETETHERHDR) + cbIpHdr + sizeof(RTNETUDP);
+                                pbData  += cbFrameHdr;
+                                cbWrite -= cbFrameHdr;
+                                pHv->DbgGuestAddr = pIp4Hdr->ip_src;
+                            }
+                            else
+                            {
+                                LogFlow(("GIM: HyperV: Ignoring UDP packet not src and dst port 0\n"));
+                                fIgnorePkt = true;
+                            }
+                        }
+                        else
+                        {
+                            LogFlow(("GIM: HyperV: Ignoring malformed UDP packet. cbMaxUdpPkt=%u UdpPkt.len=%u\n", cbMaxUdpPkt,
+                                     RT_N2H_U16(pUdpHdr->uh_ulen)));
+                            fIgnorePkt = true;
+                        }
+                    }
+                    else
+                    {
+                        LogFlow(("GIM: HyperV: Ignoring non-IP / non-UDP packet. fValidIp4=%RTbool Proto=%u\n", fValidIp4,
+                                  pIp4Hdr->ip_p));
+                        fIgnorePkt = true;
+                    }
+                }
+                else
+                {
+                    LogFlow(("GIM: HyperV: Ignoring IPv4 packet; too short to be valid UDP. cbWrite=%u\n", cbWrite));
+                    fIgnorePkt = true;
+                }
+            }
+            else
+            {
+                LogFlow(("GIM: HyperV: Ignoring non-IP packet. Ethertype=%#x\n", RT_N2H_U16(pEtherHdr->EtherType)));
+                fIgnorePkt = true;
+            }
+        }
+    }
+
+    if (!fIgnorePkt)
+    {
+        AssertCompile(sizeof(size_t) >= sizeof(uint32_t));
+        size_t cbWriteBuf = cbWrite;
+        int rc = GIMR3DebugWrite(pVM, pbData, &cbWriteBuf);
+        if (   RT_SUCCESS(rc)
+            && cbWriteBuf == cbWrite)
+            *pcbWritten = (uint32_t)cbWriteBuf;
+        else
+            *pcbWritten = 0;
+    }
+    else
+        *pcbWritten = cbWrite;
+
+    return VINF_SUCCESS;
 }
 
@@ -1020,6 +1231,4 @@
 
     PGIMHVDEBUGPOSTOUT pOut = (PGIMHVDEBUGPOSTOUT)pHv->pbHypercallOut;
-    AssertPtrReturn(pOut, VERR_GIM_IPE_2);
-    uint32_t *pcbPendingWrite = &pOut->cbPending;
 
     /*
@@ -1034,118 +1243,20 @@
         rcHv = GIM_HV_STATUS_INVALID_PARAMETER;
     else if (!cbWrite)
+    {
         rcHv = GIM_HV_STATUS_SUCCESS;
+        pOut->cbPending = 0;
+    }
     else if (cbWrite > 0)
     {
-        bool fIgnorePacket = false;
-        if (pHv->fIsVendorMsHv)
+        uint32_t cbWritten = 0;
+        int rc2 = gimR3HvDebugWrite(pVM, pbData, cbWrite, &cbWritten, pHv->fIsVendorMsHv /*fUdpPkt*/);
+        if (   RT_SUCCESS(rc2)
+            && cbWritten == cbWrite)
         {
-            /*
-             * Windows guests sends us ethernet frames over the Hyper-V debug connection.
-             * It sends DHCP/ARP queries with zero'd out MAC addresses and requires fudging up the
-             * packets somewhere.
-             *
-             * The Microsoft WinDbg debugger talks UDP and thus only expects the actual debug
-             * protocol payload.
-             *
-             * At present, we only handle guests configured with the "nodhcp" option. This makes
-             * the guest send ARP queries with a self-chosen IP and after a couple of attempts of
-             * receiving no replies, the guest picks its own IP address. After this, the guest
-             * starts sending the UDP packets we require. We thus ignore the initial ARP packets
-             * (and to be safe all non-UDP packets) until the guest eventually starts talking
-             * UDP. Then we can finally feed the UDP payload over the debug connection.
-             */
-            if (cbWrite > sizeof(RTNETETHERHDR))
-            {
-                PCRTNETETHERHDR pEtherHdr = (PCRTNETETHERHDR)pbData;
-                if (pEtherHdr->EtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4))
-                {
-                    if (cbWrite > sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN)
-                    {
-                        size_t const cbMaxIpHdr = cbWrite - sizeof(RTNETETHERHDR) - sizeof(RTNETUDP) - 1;
-                        size_t const cbMaxIpPkt = cbWrite - sizeof(RTNETETHERHDR);
-                        PCRTNETIPV4  pIp4Hdr    = (PCRTNETIPV4)(pbData + sizeof(RTNETETHERHDR));
-                        bool const   fValidIp4  = RTNetIPv4IsHdrValid(pIp4Hdr, cbMaxIpHdr, cbMaxIpPkt, false /*fChecksum*/);
-                        if (   fValidIp4
-                            && pIp4Hdr->ip_p == RTNETIPV4_PROT_UDP)
-                        {
-                            uint32_t const cbIpHdr     = pIp4Hdr->ip_hl * 4;
-                            uint32_t const cbMaxUdpPkt = cbWrite - sizeof(RTNETETHERHDR) - cbIpHdr;
-                            PCRTNETUDP pUdpHdr       = (PCRTNETUDP)((uint8_t *)pIp4Hdr + cbIpHdr);
-                            if (   pUdpHdr->uh_ulen >  RT_H2N_U16(sizeof(RTNETUDP))
-                                && pUdpHdr->uh_ulen <= RT_H2N_U16((uint16_t)cbMaxUdpPkt))
-                            {
-                                /*
-                                 * Extract the UDP payload and pass it to the debugger and record the guest IP address.
-                                 * Hyper-V sends UDP debugger packets with source and destination port as 0. If we don't
-                                 * filter out the ports here, we would receive BOOTP, NETBIOS and other UDP sub-protocol
-                                 * packets which the debugger yells as "Bad packet received from...".
-                                 */
-                                if (   !pUdpHdr->uh_dport
-                                    && !pUdpHdr->uh_sport)
-                                {
-                                    uint32_t const cbFrameHdr = sizeof(RTNETETHERHDR) + cbIpHdr + sizeof(RTNETUDP);
-                                    pbData  += cbFrameHdr;
-                                    cbWrite -= cbFrameHdr;
-                                    pHv->DbgGuestAddr = pIp4Hdr->ip_src;
-                                }
-                                else
-                                {
-                                    LogFlow(("GIM: HyperV: Ignoring UDP packet not src and dst port 0\n"));
-                                    fIgnorePacket = true;
-                                }
-                            }
-                            else
-                            {
-                                LogFlow(("GIM: HyperV: Ignoring malformed UDP packet. cbMaxUdpPkt=%u UdpPkt.len=%u\n",
-                                         cbMaxUdpPkt, RT_N2H_U16(pUdpHdr->uh_ulen)));
-                                fIgnorePacket = true;
-                            }
-                        }
-                        else
-                        {
-                            LogFlow(("GIM: HyperV: Ignoring non-IP / non-UDP packet. fValidIp4=%RTBool Proto=%u\n", fValidIp4,
-                                      pIp4Hdr->ip_p));
-                            fIgnorePacket = true;
-                        }
-                    }
-                    else
-                    {
-                        LogFlow(("GIM: HyperV: Ignoring IPv4 packet; too short to be valid UDP. cbWrite=%u\n", cbWrite));
-                        fIgnorePacket = true;
-                    }
-                }
-                else
-                {
-                    LogFlow(("GIM: HyperV: Ignoring non-IP packet. Ethertype=%#x\n", RT_N2H_U16(pEtherHdr->EtherType)));
-                    fIgnorePacket = true;
-                }
-            }
-        }
-
-        if (!fIgnorePacket)
-        {
-            uint32_t cbReallyWritten = 0;
-            int rc2 = gimR3HvDebugWrite(pVM, pbData, cbWrite, &cbReallyWritten);
-            if (   RT_SUCCESS(rc2)
-                && cbReallyWritten == cbWrite)
-            {
-                *pcbPendingWrite = 0;
-                rcHv = GIM_HV_STATUS_SUCCESS;
-            }
-            else
-            {
-                /*
-                 * No need to update "*pcbPendingWrite" here as the guest isn't supposed to/doesn't
-                 * look at any of the output parameters when we fail the hypercall operation.
-                 */
-                rcHv = GIM_HV_STATUS_INSUFFICIENT_BUFFERS;
-            }
+            pOut->cbPending = 0;
+            rcHv = GIM_HV_STATUS_SUCCESS;
         }
         else
-        {
-            /* Pretend success. */
-            *pcbPendingWrite = 0;
-            rcHv = GIM_HV_STATUS_SUCCESS;
-        }
+            rcHv = GIM_HV_STATUS_INSUFFICIENT_BUFFER;
     }
 
@@ -1217,54 +1328,11 @@
     else if (cbRead > 0)
     {
-        int rc2 = gimR3HvDebugRead(pVM, pvData, cbRead, pcbReallyRead, cMsTimeout);
+        int rc2 = gimR3HvDebugRead(pVM, pvData, GIM_HV_PAGE_SIZE, cbRead, pcbReallyRead, cMsTimeout,
+                                   pHv->fIsVendorMsHv /*fUdpPkt*/);
         Assert(*pcbReallyRead <= cbRead);
         if (   RT_SUCCESS(rc2)
             && *pcbReallyRead > 0)
         {
-            uint8_t abFrame[sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + sizeof(RTNETUDP)];
-            if (   pHv->fIsVendorMsHv
-                && *pcbReallyRead + sizeof(abFrame) <= GIM_HV_PAGE_SIZE)
-            {
-                /*
-                 * Windows guests pumps ethernet frames over the Hyper-V debug connection as
-                 * explained in gimR3HvHypercallPostDebugData(). Here, we reconstruct the packet
-                 * with the guest's self-chosen IP ARP address we saved in pHv->DbgGuestAddr.
-                 *
-                 * Note! We really need to pass the minimum IPv4 header length. The Windows 10 guest
-                 * is -not- happy if we include the IPv4 options field, i.e. using sizeof(RTNETIPV4)
-                 * instead of RTNETIPV4_MIN_LEN.
-                 */
-                RT_ZERO(abFrame);
-                PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
-                PRTNETIPV4     pIpHdr  = (PRTNETIPV4)    (pEthHdr + 1);
-                PRTNETUDP      pUdpHdr = (PRTNETUDP)     ((uint8_t *)pIpHdr + RTNETIPV4_MIN_LEN);
-
-                /* Ethernet */
-                pEthHdr->EtherType = RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4);
-                /* IPv4 */
-                pIpHdr->ip_v       = 4;
-                pIpHdr->ip_hl      = RTNETIPV4_MIN_LEN / sizeof(uint32_t);
-                pIpHdr->ip_tos     = 0;
-                pIpHdr->ip_len     = RT_H2N_U16((uint16_t)*pcbReallyRead + sizeof(RTNETUDP) + RTNETIPV4_MIN_LEN);
-                pIpHdr->ip_id      = 0;
-                pIpHdr->ip_off     = 0;
-                pIpHdr->ip_ttl     = 255;
-                pIpHdr->ip_p       = RTNETIPV4_PROT_UDP;
-                pIpHdr->ip_sum     = 0;
-                pIpHdr->ip_src.u   = 0;
-                pIpHdr->ip_dst.u   = pHv->DbgGuestAddr.u;
-                pIpHdr->ip_sum     = RTNetIPv4HdrChecksum(pIpHdr);
-                /* UDP */
-                pUdpHdr->uh_ulen   = RT_H2N_U16_C((uint16_t)*pcbReallyRead + sizeof(*pUdpHdr));
-
-                /* Make room by moving the payload and prepending the headers. */
-                uint8_t *pbData = (uint8_t *)pvData;
-                memmove(pbData + sizeof(abFrame), pbData, *pcbReallyRead);
-                memcpy(pbData, &abFrame[0], sizeof(abFrame));
-
-                /* Update the adjusted sizes. */
-                *pcbReallyRead += sizeof(abFrame);
-                *pcbRemainingRead = cbRead - *pcbReallyRead;
-            }
+            *pcbRemainingRead = cbRead -  *pcbReallyRead;
             rcHv = GIM_HV_STATUS_SUCCESS;
         }
Index: trunk/src/VBox/VMM/include/GIMHvInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/GIMHvInternal.h	(revision 58247)
+++ trunk/src/VBox/VMM/include/GIMHvInternal.h	(revision 58248)
@@ -243,5 +243,5 @@
 /** Base address of synthetic interrupt event flag (R/W) */
 #define MSR_GIM_HV_SIEFP                          UINT32_C(0x40000082)
-/** Base address of synthetic interrupt parameter page (R/W) */
+/** Base address of synthetic interrupt message page (R/W) */
 #define MSR_GIM_HV_SIMP                           UINT32_C(0x40000083)
 /** End-Of-Message in synthetic interrupt parameter page (W) */
@@ -644,5 +644,29 @@
 
 
-/** @name Hyper-V debug support.
+/** @name Hyper-V MSR - Debug control (MSR_GIM_HV_SYNTH_DEBUG_CONTROL).
+ * @{
+ */
+/** Perform debug write. */
+#define MSR_GIM_HV_SYNTH_DEBUG_CONTROL_IS_WRITE(a)     RT_BOOL((a) & RT_BIT_64(0))
+/** Perform debug read. */
+#define MSR_GIM_HV_SYNTH_DEBUG_CONTROL_IS_READ(a)      RT_BOOL((a) & RT_BIT_64(1))
+/** Returns length of the debug write buffer. */
+#define MSR_GIM_HV_SYNTH_DEBUG_CONTROL_W_LEN(a)        (((a) & UINT64_C(0xffff0000)) >> 16)
+/** @} */
+
+
+/** @name Hyper-V MSR - Debug status (MSR_GIM_HV_SYNTH_DEBUG_STATUS).
+ * @{
+ */
+/** Debug send buffer operation success. */
+#define MSR_GIM_HV_SYNTH_DEBUG_STATUS_W_SUCCESS_BIT    RT_BIT_32(0)
+/** Debug receive buffer operation success. */
+#define MSR_GIM_HV_SYNTH_DEBUG_STATUS_R_SUCCESS_BIT    RT_BIT_32(2)
+/** Debug connection was reset. */
+#define MSR_GIM_HV_SYNTH_DEBUG_STATUS_CONN_RESET_BIT   RT_BIT_32(3)
+/** @} */
+
+
+/** @name Hyper-V hypercall debug support.
  * Options and constants for Hyper-V debug hypercalls.
  * @{
@@ -837,10 +861,20 @@
     /** @name Guest debugging.
      * @{ */
-    /** Whether we're posing as the official Microsoft vendor. */
+    /** Whether we're posing as the Microsoft vendor. */
     bool                        fIsVendorMsHv;
-    bool                        afAlignment0[7];
+    /** Whether we're posing as the Microsoft virtualization service. */
+    bool                        fIsInterfaceVs;
+    bool                        afAlignment0[6];
     /** The auto IP address last chosen by the guest after failed ARP queries. */
     RTNETADDRIPV4               DbgGuestAddr;
     uint32_t                    uAlignment1;
+    /** Debug send buffer MSR. */
+    uint64_t                    uDebugSendBufferMsr;
+    /** Debug receive buffer MSR. */
+    uint64_t                    uDebugRecvBufferMsr;
+    /** Debug pending buffer MSR. */
+    uint64_t                    uDebugPendingBufferMsr;
+    /** Debug status MSR. */
+    uint64_t                    uDebugStatusMsr;
     /** @} */
 
@@ -864,5 +898,5 @@
 
 #ifdef IN_RING3
-VMMR3_INT_DECL(int)             gimR3HvInit(PVM pVM);
+VMMR3_INT_DECL(int)             gimR3HvInit(PVM pVM, PCFGMNODE pGimCfg);
 VMMR3_INT_DECL(int)             gimR3HvInitCompleted(PVM pVM);
 VMMR3_INT_DECL(int)             gimR3HvTerm(PVM pVM);
@@ -880,4 +914,7 @@
 VMMR3_INT_DECL(int)             gimR3HvHypercallPostDebugData(PVM pVM, RTGCPHYS GCPhysOut, int *prcHv);
 VMMR3_INT_DECL(int)             gimR3HvHypercallRetrieveDebugData(PVM pVM, RTGCPHYS GCPhysOut, int *prcHv);
+VMMR3_INT_DECL(int)             gimR3HvDebugWrite(PVM pVM, void *pvData, uint32_t cbWrite, uint32_t *pcbWritten, bool fUdpPkt);
+VMMR3_INT_DECL(int)             gimR3HvDebugRead(PVM pVM, void *pvBuf, uint32_t cbBuf, uint32_t cbRead, uint32_t *pcbRead,
+                                                 uint32_t cMsTimeout, bool fUdpPkt);
 #endif /* IN_RING3 */
 
