Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 98063)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 98064)
@@ -546,5 +546,5 @@
   VBoxDD_SOURCES        += \
   	VirtIO/VirtioCore.cpp \
- 	Network/DevVirtioNet_1_0.cpp
+ 	Network/DevVirtioNet.cpp
  endif
 
@@ -1095,5 +1095,5 @@
    VBoxDDRC_SOURCES     += \
   	VirtIO/VirtioCore.cpp \
-  	Network/DevVirtioNet_1_0.cpp
+  	Network/DevVirtioNet.cpp
   endif
 
@@ -1313,5 +1313,5 @@
   VBoxDDR0_SOURCES      += \
   	VirtIO/VirtioCore.cpp \
-  	Network/DevVirtioNet_1_0.cpp
+  	Network/DevVirtioNet.cpp
  endif
 
Index: /trunk/src/VBox/Devices/Network/DevVirtioNet.cpp
===================================================================
--- /trunk/src/VBox/Devices/Network/DevVirtioNet.cpp	(revision 98063)
+++ /trunk/src/VBox/Devices/Network/DevVirtioNet.cpp	(revision 98064)
@@ -1,9 +1,17 @@
-/* $Id$ */
+/* $Id$ $Revision$ $Date$ $Author$ */
+
 /** @file
- * DevVirtioNet - Virtio Network Device
+ * VBox storage devices - Virtio NET Driver
+ *
+ * Log-levels used:
+ *    - Level 1:   The most important (but usually rare) things to note
+ *    - Level 2:   NET command logging
+ *    - Level 3:   Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
+ *    - Level 6:   Device <-> Guest Driver negotation, traffic, notifications and state handling
+ *    - Level 12:  Brief formatted hex dumps of I/O data
  */
 
 /*
- * Copyright (C) 2009-2022 Oracle and/or its affiliates.
+ * Copyright (C) 2006-2022 Oracle and/or its affiliates.
  *
  * This file is part of VirtualBox base platform packages, as
@@ -26,157 +34,470 @@
  */
 
-
-/*********************************************************************************************************************************
-*   Header Files                                                                                                                 *
-*********************************************************************************************************************************/
-#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
-#define VNET_WITH_GSO
-#define VNET_WITH_MERGEABLE_RX_BUFS
-
+/*******************************************************************************************************************************
+*   Header Files                                                                                                               *
+***************************************************************************************************************************** **/
+#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
+#define VIRTIONET_WITH_GSO
+
+#include <iprt/types.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include <VBox/sup.h>
 #include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/stam.h>
+#include <VBox/vmm/pdmcritsect.h>
 #include <VBox/vmm/pdmnetifs.h>
-#include <iprt/asm.h>
-#include <iprt/net.h>
-#include <iprt/semaphore.h>
-#include <iprt/string.h>
+#include <VBox/msi.h>
+#include <VBox/version.h>
+#include <VBox/log.h>
+
+
 #ifdef IN_RING3
+# include <VBox/VBoxPktDmp.h>
+# include <iprt/alloc.h>
+# include <iprt/memcache.h>
+# include <iprt/semaphore.h>
+# include <iprt/sg.h>
+# include <iprt/param.h>
 # include <iprt/uuid.h>
 #endif
-#include <VBox/VBoxPktDmp.h>
+#include "../VirtIO/VirtioCore.h"
+
 #include "VBoxDD.h"
-#include "../VirtIO/Virtio.h"
-
-
-/*********************************************************************************************************************************
-*   Defined Constants And Macros                                                                                                 *
-*********************************************************************************************************************************/
-#ifndef VBOX_DEVICE_STRUCT_TESTCASE
-
-#define INSTANCE(pThis) pThis->VPCI.szInstance
+
+#define VIRTIONET_TRANSITIONAL_ENABLE_FLAG             1       /** < If set behave as VirtIO "transitional" device  */
+
+/** The current saved state version for the virtio core. */
+#define VIRTIONET_SAVEDSTATE_VERSION                   UINT32_C(1)
+#define VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY  UINT32_C(1) /**< Grandfathered in from DevVirtioNet.cpp      */
+#define VIRTIONET_SAVEDSTATE_VERSION_LEGACY            UINT32_C(2) /**< Grandfathered in from DevVirtioNet.cpp      */
+#define VIRTIONET_VERSION_MARKER_MAC_ADDR { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /**  SSM handling                  */
+
+/*
+ * Glossary of networking acronyms used in feature names below:
+ *
+ * GSO = Generic Segmentation Offload
+ * TSO = TCP Segmentation Offload
+ * UFO = UDP Fragmentation Offload
+ * ECN = Explicit Congestion Notification
+ */
+
+/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
+ * @{  */
+#define VIRTIONET_F_CSUM                 RT_BIT_64(0)          /**< Handle packets with partial checksum            */
+#define VIRTIONET_F_GUEST_CSUM           RT_BIT_64(1)          /**< Handles packets with partial checksum           */
+#define VIRTIONET_F_CTRL_GUEST_OFFLOADS  RT_BIT_64(2)          /**< Control channel offloads reconfig support       */
+#define VIRTIONET_F_MAC                  RT_BIT_64(5)          /**< Device has given MAC address                    */
+#define VIRTIONET_F_GUEST_TSO4           RT_BIT_64(7)          /**< Driver can receive TSOv4                        */
+#define VIRTIONET_F_GUEST_TSO6           RT_BIT_64(8)          /**< Driver can receive TSOv6                        */
+#define VIRTIONET_F_GUEST_ECN            RT_BIT_64(9)          /**< Driver can receive TSO with ECN                 */
+#define VIRTIONET_F_GUEST_UFO            RT_BIT_64(10)         /**< Driver can receive UFO                          */
+#define VIRTIONET_F_HOST_TSO4            RT_BIT_64(11)         /**< Device can receive TSOv4                        */
+#define VIRTIONET_F_HOST_TSO6            RT_BIT_64(12)         /**< Device can receive TSOv6                        */
+#define VIRTIONET_F_HOST_ECN             RT_BIT_64(13)         /**< Device can receive TSO with ECN                 */
+#define VIRTIONET_F_HOST_UFO             RT_BIT_64(14)         /**< Device can receive UFO                          */
+#define VIRTIONET_F_MRG_RXBUF            RT_BIT_64(15)         /**< Driver can merge receive buffers                */
+#define VIRTIONET_F_STATUS               RT_BIT_64(16)         /**< Config status field is available                */
+#define VIRTIONET_F_CTRL_VQ              RT_BIT_64(17)         /**< Control channel is available                    */
+#define VIRTIONET_F_CTRL_RX              RT_BIT_64(18)         /**< Control channel RX mode + MAC addr filtering    */
+#define VIRTIONET_F_CTRL_VLAN            RT_BIT_64(19)         /**< Control channel VLAN filtering                  */
+#define VIRTIONET_F_CTRL_RX_EXTRA        RT_BIT_64(20)         /**< Control channel RX mode extra functions         */
+#define VIRTIONET_F_GUEST_ANNOUNCE       RT_BIT_64(21)         /**< Driver can send gratuitous packets              */
+#define VIRTIONET_F_MQ                   RT_BIT_64(22)         /**< Support ultiqueue with auto receive steering    */
+#define VIRTIONET_F_CTRL_MAC_ADDR        RT_BIT_64(23)         /**< Set MAC address through control channel         */
+/** @} */
 
 #ifdef IN_RING3
-
-# define VNET_PCI_CLASS              0x0200
-# define VNET_N_QUEUES               3
-
-# if 0
-/* Virtio Block Device */
-#  define VNET_PCI_CLASS             0x0180
-#  define VNET_N_QUEUES              2
-# endif
-
-#endif /* IN_RING3 */
-
-#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
+static const VIRTIO_FEATURES_LIST s_aDevSpecificFeatures[] =
+{
+    { VIRTIONET_F_STATUS,              "   STATUS               Configuration status field is available.\n" },
+    { VIRTIONET_F_MAC,                 "   MAC                  Host has given MAC address.\n" },
+    { VIRTIONET_F_CTRL_VQ,             "   CTRL_VQ              Control channel is available.\n" },
+    { VIRTIONET_F_CTRL_MAC_ADDR,       "   CTRL_MAC_ADDR        Set MAC address through control channel.\n" },
+    { VIRTIONET_F_CTRL_RX,             "   CTRL_RX              Control channel RX mode support.\n" },
+    { VIRTIONET_F_CTRL_VLAN,           "   CTRL_VLAN            Control channel VLAN filtering.\n" },
+    { VIRTIONET_F_CTRL_GUEST_OFFLOADS, "   CTRL_GUEST_OFFLOADS  Control channel offloads reconfiguration support.\n" },
+    { VIRTIONET_F_GUEST_CSUM,          "   GUEST_CSUM           Guest handles packets with partial checksum.\n" },
+    { VIRTIONET_F_GUEST_ANNOUNCE,      "   GUEST_ANNOUNCE       Guest can send gratuitous packets.\n" },
+    { VIRTIONET_F_GUEST_TSO4,          "   GUEST_TSO4           Guest can receive TSOv4.\n" },
+    { VIRTIONET_F_GUEST_TSO6,          "   GUEST_TSO6           Guest can receive TSOv6.\n" },
+    { VIRTIONET_F_GUEST_ECN,           "   GUEST_ECN            Guest can receive TSO with ECN.\n" },
+    { VIRTIONET_F_GUEST_UFO,           "   GUEST_UFO            Guest can receive UFO.\n" },
+    { VIRTIONET_F_HOST_TSO4,           "   HOST_TSO4            Host can receive TSOv4.\n" },
+    { VIRTIONET_F_HOST_TSO6,           "   HOST_TSO6            Host can receive TSOv6.\n" },
+    { VIRTIONET_F_HOST_ECN,            "   HOST_ECN             Host can receive TSO with ECN.\n" },
+    { VIRTIONET_F_HOST_UFO,            "   HOST_UFO             Host can receive UFO.\n" },
+    { VIRTIONET_F_MQ,                  "   MQ                   Host supports multiqueue with automatic receive steering.\n" },
+    { VIRTIONET_F_CSUM,                "   CSUM                 Host handles packets with partial checksum.\n" },
+    { VIRTIONET_F_MRG_RXBUF,           "   MRG_RXBUF            Guest can merge receive buffers.\n" },
+};
+#endif
+
+#ifdef VIRTIONET_WITH_GSO
+# define VIRTIONET_HOST_FEATURES_GSO    \
+      VIRTIONET_F_CSUM                  \
+    | VIRTIONET_F_HOST_TSO4             \
+    | VIRTIONET_F_HOST_TSO6             \
+    | VIRTIONET_F_HOST_UFO              \
+    | VIRTIONET_F_GUEST_TSO4            \
+    | VIRTIONET_F_GUEST_TSO6            \
+    | VIRTIONET_F_GUEST_UFO             \
+    | VIRTIONET_F_GUEST_CSUM                                   /* @bugref(4796) Guest must handle partial chksums   */
+#else
+# define VIRTIONET_HOST_FEATURES_GSO
+#endif
+
+#define VIRTIONET_HOST_FEATURES_OFFERED \
+      VIRTIONET_F_STATUS                \
+    | VIRTIONET_F_GUEST_ANNOUNCE        \
+    | VIRTIONET_F_MAC                   \
+    | VIRTIONET_F_CTRL_VQ               \
+    | VIRTIONET_F_CTRL_RX               \
+    | VIRTIONET_F_CTRL_VLAN             \
+    | VIRTIONET_HOST_FEATURES_GSO       \
+    | VIRTIONET_F_MRG_RXBUF
+
+#define FEATURE_ENABLED(feature)        RT_BOOL(!!(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature))
+#define FEATURE_DISABLED(feature)       (!FEATURE_ENABLED(feature))
+#define FEATURE_OFFERED(feature)        VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
+
+#if FEATURE_OFFERED(MQ)
+/* Instance data doesn't allow an array large enough to contain VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX entries */
+#   define VIRTIONET_MAX_QPAIRS         1  /* This should be increased at some point and made to work */
+#else
+#   define VIRTIONET_MAX_QPAIRS         VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN /* default, VirtIO 1.0, 5.1.6.5.5 */
+#endif
+
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS      64
+#define VIRTIONET_MAX_WORKERS           VIRTIONET_MAX_QPAIRS + 1
+#define VIRTIONET_MAX_VIRTQS            (VIRTIONET_MAX_QPAIRS * 2 + 1)
+#define VIRTIONET_MAX_FRAME_SIZE        65535 + 18  /**< Max IP pkt size + Eth. header w/VLAN tag  */
+#define VIRTIONET_MAC_FILTER_LEN        64
+#define VIRTIONET_MAX_VLAN_ID           4096
+#define VIRTIONET_RX_SEG_COUNT          32
+
+#define VIRTQNAME(uVirtqNbr)            (pThis->aVirtqs[uVirtqNbr]->szName)
+#define CBVIRTQNAME(uVirtqNbr)          RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
+
+#define IS_TX_VIRTQ(n)                  ((n) != CTRLQIDX && ((n) & 1))
+#define IS_RX_VIRTQ(n)                  ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
+#define IS_CTRL_VIRTQ(n)                ((n) == CTRLQIDX)
 
 /*
- * Commenting out VNET_TX_DELAY enables async transmission in a dedicated thread.
- * When VNET_TX_DELAY is defined, a timer handler does the job.
- */
-//#define VNET_TX_DELAY           150   /**< 150 microseconds */
-#define VNET_MAX_FRAME_SIZE     65535 + 18  /**< Max IP packet size + Ethernet header with VLAN tag */
-#define VNET_MAC_FILTER_LEN     32
-#define VNET_MAX_VID            (1 << 12)
-
-/** @name Virtio net features
+ * Macros to calculate queue type-pecific index number regardless of scale. VirtIO 1.0, 5.1.2
+ */
+#define RXQIDX(qPairIdx)                (qPairIdx * 2)
+#define TXQIDX(qPairIdx)                (RXQIDX(qPairIdx) + 1)
+#define CTRLQIDX                        (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
+
+#define IS_LINK_UP(pState)              !!(pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
+#define IS_LINK_DOWN(pState)            !IS_LINK_UP(pState)
+
+#define SET_LINK_UP(pState) \
+            LogFunc(("SET_LINK_UP\n")); \
+            pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
+            virtioCoreNotifyConfigChanged(&pThis->Virtio)
+
+#define SET_LINK_DOWN(pState) \
+            LogFunc(("SET_LINK_DOWN\n")); \
+            pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
+            virtioCoreNotifyConfigChanged(&pThis->Virtio)
+
+#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
+            (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
+
+#define PCI_DEVICE_ID_VIRTIONET_HOST               0x1000      /**< VirtIO transitional device ID for network card  */
+#define PCI_CLASS_BASE_NETWORK_CONTROLLER          0x0200      /**< PCI Network device class                        */
+#define PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER      0x00        /**< PCI NET Controller subclass                     */
+#define PCI_CLASS_PROG_UNSPECIFIED                 0x00        /**< Programming interface. N/A.                     */
+#define VIRTIONET_PCI_CLASS                        0x01        /**< Base class Mass Storage?                        */
+
+/**
+ * VirtIO Network (virtio-net) device-specific configuration subregion (VirtIO 1.0, 5.1.4)
+ * Guest MMIO is processed through callback to VirtIO core which forwards references to network configuration
+ * fields to this device-specific code through a callback.
+ */
+#pragma pack(1)
+
+    typedef struct virtio_net_config
+    {
+        RTMAC  uMacAddress;                                     /**< mac                                            */
+
+#if     FEATURE_OFFERED(STATUS)
+               uint16_t uStatus;                                /**< status                                         */
+#endif
+
+#if     FEATURE_OFFERED(MQ)
+               uint16_t uMaxVirtqPairs;                         /**< max_virtq_pairs                                */
+#endif
+
+    } VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
+
+#pragma pack()
+
+#define VIRTIONET_F_LINK_UP                          1          /**< config status: Link is up                      */
+#define VIRTIONET_F_ANNOUNCE                         2          /**< config status: Announce                        */
+
+/** @name VirtIO 1.0 NET Host Device device specific control types
  * @{  */
-#define VNET_F_CSUM       0x00000001  /**< Host handles pkts w/ partial csum */
-#define VNET_F_GUEST_CSUM 0x00000002  /**< Guest handles pkts w/ partial csum */
-#define VNET_F_MAC        0x00000020  /**< Host has given MAC address. */
-#define VNET_F_GSO        0x00000040  /**< Host handles pkts w/ any GSO type */
-#define VNET_F_GUEST_TSO4 0x00000080  /**< Guest can handle TSOv4 in. */
-#define VNET_F_GUEST_TSO6 0x00000100  /**< Guest can handle TSOv6 in. */
-#define VNET_F_GUEST_ECN  0x00000200  /**< Guest can handle TSO[6] w/ ECN in. */
-#define VNET_F_GUEST_UFO  0x00000400  /**< Guest can handle UFO in. */
-#define VNET_F_HOST_TSO4  0x00000800  /**< Host can handle TSOv4 in. */
-#define VNET_F_HOST_TSO6  0x00001000  /**< Host can handle TSOv6 in. */
-#define VNET_F_HOST_ECN   0x00002000  /**< Host can handle TSO[6] w/ ECN in. */
-#define VNET_F_HOST_UFO   0x00004000  /**< Host can handle UFO in. */
-#define VNET_F_MRG_RXBUF  0x00008000  /**< Host can merge receive buffers. */
-#define VNET_F_STATUS     0x00010000  /**< virtio_net_config.status available */
-#define VNET_F_CTRL_VQ    0x00020000  /**< Control channel available */
-#define VNET_F_CTRL_RX    0x00040000  /**< Control channel RX mode support */
-#define VNET_F_CTRL_VLAN  0x00080000  /**< Control channel VLAN filtering */
+#define VIRTIONET_HDR_F_NEEDS_CSUM                   1          /**< flags: Packet needs checksum                   */
+#define VIRTIONET_HDR_GSO_NONE                       0          /**< gso_type: No Global Segmentation Offset        */
+#define VIRTIONET_HDR_GSO_TCPV4                      1          /**< gso_type: Global Segment Offset for TCPV4      */
+#define VIRTIONET_HDR_GSO_UDP                        3          /**< gso_type: Global Segment Offset for UDP        */
+#define VIRTIONET_HDR_GSO_TCPV6                      4          /**< gso_type: Global Segment Offset for TCPV6      */
+#define VIRTIONET_HDR_GSO_ECN                     0x80          /**< gso_type: Explicit Congestion Notification     */
 /** @} */
 
-#define VNET_S_LINK_UP    1
-
-
-/*********************************************************************************************************************************
-*   Structures and Typedefs                                                                                                      *
-*********************************************************************************************************************************/
-#ifdef _MSC_VER
-struct VNetPCIConfig
-#else /* !_MSC_VER */
-struct __attribute__ ((__packed__)) VNetPCIConfig /** @todo r=bird: Use #pragma pack if necessary, that's portable! */
-#endif /* !_MSC_VER */
-{
-    RTMAC    mac;
-    uint16_t uStatus;
+/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
+#pragma pack(1)
+struct virtio_net_pkt_hdr {
+    uint8_t  uFlags;                                            /**< flags                                          */
+    uint8_t  uGsoType;                                          /**< gso_type                                       */
+    uint16_t uHdrLen;                                           /**< hdr_len                                        */
+    uint16_t uGsoSize;                                          /**< gso_size                                       */
+    uint16_t uChksumStart;                                      /**< Chksum_start                                   */
+    uint16_t uChksumOffset;                                     /**< Chksum_offset                                  */
+    uint16_t uNumBuffers;                                       /**< num_buffers                                    */
 };
-AssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
-
-/**
- * The virtio-net shared instance data.
- *
- * @extends     VPCISTATE
- */
-typedef struct VNETSTATE
-{
-    VPCISTATE               VPCI;
-
-//    PDMCRITSECT             csRx;                           /**< Protects RX queue. */
-
-#ifdef VNET_TX_DELAY
-    /** Transmit Delay Timer. */
-    TMTIMERHANDLE           hTxTimer;
-    uint32_t                u32i;
-    uint32_t                u32AvgDiff;
-    uint32_t                u32MinDiff;
-    uint32_t                u32MaxDiff;
-    uint64_t                u64NanoTS;
-#else  /* !VNET_TX_DELAY */
-    /** The event semaphore TX thread waits on. */
-    SUPSEMEVENT             hTxEvent;
-#endif /* !VNET_TX_DELAY */
+#pragma pack()
+typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
+AssertCompileSize(VIRTIONETPKTHDR, 12);
+
+/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
+#pragma pack(1)
+struct virtio_net_ctrl_hdr {
+    uint8_t uClass;                                             /**< class                                          */
+    uint8_t uCmd;                                               /**< command                                        */
+};
+#pragma pack()
+typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
+
+typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
+
+/* Command entry fAck values */
+#define VIRTIONET_OK                               0            /**< Internal success status                        */
+#define VIRTIONET_ERROR                            1            /**< Internal failure status                        */
+
+/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
+ * @{  */
+#define VIRTIONET_CTRL_RX                           0           /**< Control class: Receive filtering               */
+#define VIRTIONET_CTRL_RX_PROMISC                   0           /**< Promiscuous mode                               */
+#define VIRTIONET_CTRL_RX_ALLMULTI                  1           /**< All-multicast receive                          */
+#define VIRTIONET_CTRL_RX_ALLUNI                    2           /**< All-unicast receive                            */
+#define VIRTIONET_CTRL_RX_NOMULTI                   3           /**< No multicast receive                           */
+#define VIRTIONET_CTRL_RX_NOUNI                     4           /**< No unicast receive                             */
+#define VIRTIONET_CTRL_RX_NOBCAST                   5           /**< No broadcast receive                           */
+/** @} */
+
+typedef uint8_t  VIRTIONET_MAC_ADDRESS[6];
+typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
+typedef uint8_t  VIRTIONET_CTRL_MAC_ENTRIES[][6];
+
+/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
+ * @{  */
+#define VIRTIONET_CTRL_MAC                          1           /**< Control class: MAC address filtering           */
+#define VIRTIONET_CTRL_MAC_TABLE_SET                0           /**< Set MAC table                                  */
+#define VIRTIONET_CTRL_MAC_ADDR_SET                 1           /**< Set default MAC address                        */
+/** @} */
+
+/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
+ * @{  */
+#define VIRTIONET_CTRL_VLAN                         2           /**< Control class: VLAN filtering                  */
+#define VIRTIONET_CTRL_VLAN_ADD                     0           /**< Add VLAN to filter table                       */
+#define VIRTIONET_CTRL_VLAN_DEL                     1           /**< Delete VLAN from filter table                  */
+/** @} */
+
+/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
+ * @{  */
+#define VIRTIONET_CTRL_ANNOUNCE                     3           /**< Control class: Gratuitous Packet Sending       */
+#define VIRTIONET_CTRL_ANNOUNCE_ACK                 0           /**< Gratuitous Packet Sending ACK                  */
+/** @} */
+
+struct virtio_net_ctrl_mq {
+    uint16_t    uVirtqueuePairs;                                /**<  virtqueue_pairs                               */
+};
+
+/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
+ * @{  */
+#define VIRTIONET_CTRL_MQ                           4           /**< Control class: Receive steering                */
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET              0           /**< Set number of TX/RX queues                     */
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN              1           /**< Minimum number of TX/RX queues                 */
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX         0x8000           /**< Maximum number of TX/RX queues                 */
+/** @} */
+
+uint64_t    uOffloads;                                          /**< offloads                                       */
+
+/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
+ * @{  */
+#define VIRTIONET_CTRL_GUEST_OFFLOADS             5            /**< Control class: Offloads state configuration    */
+#define VIRTIONET_CTRL_GUEST_OFFLOADS_SET         0            /**< Apply new offloads configuration               */
+/** @} */
+
+typedef enum VIRTIONETPKTHDRTYPE
+{
+    kVirtioNetUninitializedPktHdrType   = 0,                   /**< Uninitialized (default) packet header type      */
+    kVirtioNetModernPktHdrWithoutMrgRx  = 1,                   /**< Packets should not be merged (modern driver)    */
+    kVirtioNetModernPktHdrWithMrgRx     = 2,                   /**< Packets should be merged (modern driver)        */
+    kVirtioNetLegacyPktHdrWithoutMrgRx  = 3,                   /**< Packets should not be merged (legacy driver)    */
+    kVirtioNetLegacyPktHdrWithMrgRx     = 4,                   /**< Packets should be merged (legacy driver)        */
+    kVirtioNetFor32BitHack              = 0x7fffffff
+} VIRTIONETPKTHDRTYPE;
+
+/**
+ * device-specific queue info
+ */
+struct VIRTIONETWORKER;
+struct VIRTIONETWORKERR3;
+
+typedef struct VIRTIONETVIRTQ
+{
+    uint16_t                       uIdx;                        /**< Index of this queue                            */
+    uint16_t                       align;
+    bool                           fCtlVirtq;                   /**< If set this queue is the control queue         */
+    bool                           fHasWorker;                  /**< If set this queue has an associated worker     */
+    bool                           fAttachedToVirtioCore;       /**< Set if queue attached to virtio core           */
+    char                           szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name                              */
+} VIRTIONETVIRTQ, *PVIRTIONETVIRTQ;
+
+/**
+ * Worker thread context, shared state.
+ */
+typedef struct VIRTIONETWORKER
+{
+    SUPSEMEVENT                     hEvtProcess;                /**< handle of associated sleep/wake-up semaphore   */
+    uint16_t                        uIdx;                       /**< Index of this worker                           */
+    bool volatile                   fSleeping;                  /**< Flags whether worker thread is sleeping or not */
+    bool volatile                   fNotified;                  /**< Flags whether worker thread notified           */
+    bool                            fAssigned;                  /**< Flags whether worker thread has been set up    */
+    uint8_t                         pad;
+} VIRTIONETWORKER;
+/** Pointer to a virtio net worker. */
+typedef VIRTIONETWORKER *PVIRTIONETWORKER;
+
+/**
+ * Worker thread context, ring-3 state.
+ */
+typedef struct VIRTIONETWORKERR3
+{
+    R3PTRTYPE(PPDMTHREAD)           pThread;                    /**< pointer to worker thread's handle              */
+    uint16_t                        uIdx;                       /**< Index of this worker                           */
+    uint16_t                        pad;
+} VIRTIONETWORKERR3;
+/** Pointer to a virtio net worker. */
+typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
+
+/**
+ * VirtIO Host NET device state, shared edition.
+ *
+ * @extends     VIRTIOCORE
+ */
+typedef struct VIRTIONET
+{
+    /** The core virtio state.   */
+    VIRTIOCORE              Virtio;
+
+    /** Virtio device-specific configuration */
+    VIRTIONET_CONFIG_T      virtioNetConfig;
+
+    /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
+    VIRTIONETWORKER         aWorkers[VIRTIONET_MAX_VIRTQS];
+
+    /** Track which VirtIO queues we've attached to */
+    VIRTIONETVIRTQ          aVirtqs[VIRTIONET_MAX_VIRTQS];
+
+    /** PDM device Instance name */
+    char                    szInst[16];
+
+    /** VirtIO features negotiated with the guest, including generic core and device specific */
+    uint64_t                fNegotiatedFeatures;
+
+    /** Number of Rx/Tx queue pairs (only one if MQ feature not negotiated */
+    uint16_t                cVirtqPairs;
+
+    /** Number of Rx/Tx queue pairs that have already been initialized */
+    uint16_t                cInitializedVirtqPairs;
+
+    /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
+    uint16_t                cVirtqs;
+
+    /** Number of worker threads (one for the control queue and one for each Tx queue) */
+    uint16_t                cWorkers;
+
+    /** Alignment */
+    uint16_t                alignment;
 
     /** Indicates transmission in progress -- only one thread is allowed. */
     uint32_t                uIsTransmitting;
 
-    /** PCI config area holding MAC address as well as TBD. */
-    struct VNetPCIConfig    config;
+    /** Link up delay (in milliseconds). */
+    uint32_t                cMsLinkUpDelay;
+
+    /** The number of actually used slots in aMacMulticastFilter. */
+    uint32_t                cMulticastFilterMacs;
+
+    /** The number of actually used slots in aMacUniicastFilter. */
+    uint32_t                cUnicastFilterMacs;
+
+    /** Semaphore leaf device's thread waits on until guest driver sends empty Rx bufs */
+    SUPSEMEVENT             hEventRxDescAvail;
+
+    /** Array of MAC multicast addresses accepted by RX filter. */
+    RTMAC                   aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
+
+    /** Array of MAC unicast addresses accepted by RX filter. */
+    RTMAC                   aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
+
+    /** Default MAC address which rx filtering accepts */
+    RTMAC                   rxFilterMacDefault;
+
     /** MAC address obtained from the configuration. */
     RTMAC                   macConfigured;
+
+    /** Bit array of VLAN filter, one bit per VLAN ID. */
+    uint8_t                 aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
+
+    /** Set if PDM leaf device at the network interface is starved for Rx buffers */
+    bool volatile           fLeafWantsEmptyRxBufs;
+
+    /** Number of packet being sent/received to show in debug log. */
+    uint32_t                uPktNo;
+
+    /** Flags whether VirtIO core is in ready state */
+    uint8_t                 fVirtioReady;
+
+    /** Resetting flag */
+    uint8_t                 fResetting;
+
+    /** Promiscuous mode -- RX filter accepts all packets. */
+    uint8_t                 fPromiscuous;
+
+    /** All multicast mode -- RX filter accepts all multicast packets. */
+    uint8_t                 fAllMulticast;
+
+    /** All unicast mode -- RX filter accepts all unicast packets. */
+    uint8_t                 fAllUnicast;
+
+    /** No multicast mode - Supresses multicast receive */
+    uint8_t                 fNoMulticast;
+
+    /** No unicast mode - Suppresses unicast receive */
+    uint8_t                 fNoUnicast;
+
+    /** No broadcast mode - Supresses broadcast receive */
+    uint8_t                 fNoBroadcast;
+
+    /** Type of network pkt header based on guest driver version/features */
+    VIRTIONETPKTHDRTYPE     ePktHdrType;
+
+    /** Size of network pkt header based on guest driver version/features */
+    uint16_t                cbPktHdr;
+
     /** True if physical cable is attached in configuration. */
     bool                    fCableConnected;
-    /** Link up delay (in milliseconds). */
-    uint32_t                cMsLinkUpDelay;
-
-    uint32_t                alignment;
-
-    /** Number of packet being sent/received to show in debug log. */
-    uint32_t                u32PktNo;
-
-    /** N/A: */
-    bool volatile           fMaybeOutOfSpace;
-
-    /** Promiscuous mode -- RX filter accepts all packets. */
-    bool                    fPromiscuous;
-    /** AllMulti mode -- RX filter accepts all multicast packets. */
-    bool                    fAllMulti;
-    /** The number of actually used slots in aMacTable. */
-    uint32_t                cMacFilterEntries;
-    /** Array of MAC addresses accepted by RX filter. */
-    RTMAC                   aMacFilter[VNET_MAC_FILTER_LEN];
-    /** Bit array of VLAN filter, one bit per VLAN ID. */
-    uint8_t                 aVlanFilter[VNET_MAX_VID / sizeof(uint8_t)];
-
-    /* Receive-blocking-related fields ***************************************/
-
-    /** EMT: Gets signalled when more RX descriptors become available. */
-    SUPSEMEVENT             hEventMoreRxDescAvail;
-
-    /** Handle of the I/O port range.  */
-    IOMIOPORTHANDLE         hIoPorts;
+
+    /** True if this device should offer legacy virtio support to the guest */
+    bool                    fOfferLegacy;
 
     /** @name Statistic
@@ -197,193 +518,232 @@
     STAMCOUNTER             StatTransmitByNetwork;
     STAMCOUNTER             StatTransmitByThread;
+    /** @}  */
 #endif
-    /** @}  */
-} VNETSTATE;
-/** Pointer to the virtio-net shared instance data. */
-typedef VNETSTATE *PVNETSTATE;
-
-
-/**
- * The virtio-net ring-3 instance data.
- *
- * @extends     VPCISTATER3
- * @implements  PDMINETWORKDOWN
- * @implements  PDMINETWORKCONFIG
- */
-typedef struct VNETSTATER3
-{
-    VPCISTATER3             VPCI;
-
-    PDMINETWORKDOWN         INetworkDown;
-    PDMINETWORKCONFIG       INetworkConfig;
-    R3PTRTYPE(PPDMIBASE)    pDrvBase;                 /**< Attached network driver. */
-    R3PTRTYPE(PPDMINETWORKUP) pDrv;    /**< Connector of attached network driver. */
+} VIRTIONET;
+/** Pointer to the shared state of the VirtIO Host NET device. */
+typedef VIRTIONET *PVIRTIONET;
+
+/**
+ * VirtIO Host NET device state, ring-3 edition.
+ *
+ * @extends     VIRTIOCORER3
+ */
+typedef struct VIRTIONETR3
+{
+    /** The core virtio ring-3 state. */
+    VIRTIOCORER3                    Virtio;
+
+    /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
+    VIRTIONETWORKERR3               aWorkers[VIRTIONET_MAX_VIRTQS];
+
     /** The device instance.
-     * @note This is _only_ for use when dealing with interface callbacks. */
-    PPDMDEVINSR3            pDevIns;
-
-#ifndef VNET_TX_DELAY
-    R3PTRTYPE(PPDMTHREAD)   pTxThread;
+     * @note This is _only_ for use whxen dealing with interface callbacks. */
+    PPDMDEVINSR3                    pDevIns;
+
+    /** Status LUN: Base interface. */
+    PDMIBASE                        IBase;
+
+    /** Status LUN: LED port interface. */
+    PDMILEDPORTS                    ILeds;
+
+    /** Status LUN: LED connector (peer). */
+    R3PTRTYPE(PPDMILEDCONNECTORS)   pLedsConnector;
+
+    /** Status: LED */
+    PDMLED                          led;
+
+    /** Attached network driver. */
+    R3PTRTYPE(PPDMIBASE)            pDrvBase;
+
+    /** Network port interface (down) */
+    PDMINETWORKDOWN                 INetworkDown;
+
+    /** Network config port interface (main). */
+    PDMINETWORKCONFIG               INetworkConfig;
+
+    /** Connector of attached network driver. */
+    R3PTRTYPE(PPDMINETWORKUP)       pDrv;
+
+    /** Link Up(/Restore) Timer. */
+    TMTIMERHANDLE                   hLinkUpTimer;
+
+} VIRTIONETR3;
+
+/** Pointer to the ring-3 state of the VirtIO Host NET device. */
+typedef VIRTIONETR3 *PVIRTIONETR3;
+
+/**
+ * VirtIO Host NET device state, ring-0 edition.
+ */
+typedef struct VIRTIONETR0
+{
+    /** The core virtio ring-0 state. */
+    VIRTIOCORER0                    Virtio;
+} VIRTIONETR0;
+/** Pointer to the ring-0 state of the VirtIO Host NET device. */
+typedef VIRTIONETR0 *PVIRTIONETR0;
+
+/**
+ * VirtIO Host NET device state, raw-mode edition.
+ */
+typedef struct VIRTIONETRC
+{
+    /** The core virtio raw-mode state. */
+    VIRTIOCORERC                    Virtio;
+} VIRTIONETRC;
+/** Pointer to the ring-0 state of the VirtIO Host NET device. */
+typedef VIRTIONETRC *PVIRTIONETRC;
+
+/** @typedef VIRTIONETCC
+ * The instance data for the current context. */
+typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
+
+/** @typedef PVIRTIONETCC
+ * Pointer to the instance data for the current context. */
+typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
+
+#ifdef IN_RING3
+static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
+static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
+
+/**
+ * Helper function used when logging state of a VM thread.
+ *
+ * @param   Thread
+ *
+ * @return  Associated name of thread as a pointer to a zero-terminated string.
+ */
+DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
+{
+    if (!pThread)
+        return "<null>";
+
+    switch(pThread->enmState)
+    {
+        case PDMTHREADSTATE_INVALID:
+            return "invalid state";
+        case PDMTHREADSTATE_INITIALIZING:
+            return "initializing";
+        case PDMTHREADSTATE_SUSPENDING:
+            return "suspending";
+        case PDMTHREADSTATE_SUSPENDED:
+            return "suspended";
+        case PDMTHREADSTATE_RESUMING:
+            return "resuming";
+        case PDMTHREADSTATE_RUNNING:
+            return "running";
+        case PDMTHREADSTATE_TERMINATING:
+            return "terminating";
+        case PDMTHREADSTATE_TERMINATED:
+            return "terminated";
+        default:
+            return "unknown state";
+    }
+}
 #endif
 
-    R3PTRTYPE(PVQUEUE)      pRxQueue;
-    R3PTRTYPE(PVQUEUE)      pTxQueue;
-    R3PTRTYPE(PVQUEUE)      pCtlQueue;
-
-    /** Link Up(/Restore) Timer. */
-    TMTIMERHANDLE           hLinkUpTimer;
-} VNETSTATER3;
-/** Pointer to the virtio-net ring-3 instance data. */
-typedef VNETSTATER3 *PVNETSTATER3;
-
-
-/**
- * The virtio-net ring-0 instance data.
- *
- * @extends     VPCISTATER0
- */
-typedef struct VNETSTATER0
-{
-    VPCISTATER0             VPCI;
-} VNETSTATER0;
-/** Pointer to the virtio-net ring-0 instance data. */
-typedef VNETSTATER0 *PVNETSTATER0;
-
-
-/**
- * The virtio-net raw-mode instance data.
- *
- * @extends     VPCISTATERC
- */
-typedef struct VNETSTATERC
-{
-    VPCISTATERC             VPCI;
-} VNETSTATERC;
-/** Pointer to the virtio-net ring-0 instance data. */
-typedef VNETSTATERC *PVNETSTATERC;
-
-
-/** The virtio-net currenct context instance data. */
-typedef CTX_SUFF(VNETSTATE) VNETSTATECC;
-/** Pointer to the virtio-net currenct context instance data. */
-typedef CTX_SUFF(PVNETSTATE) PVNETSTATECC;
-
-
-
-#ifndef VBOX_DEVICE_STRUCT_TESTCASE
-
-#define VNETHDR_F_NEEDS_CSUM     1       // Use u16CSumStart, u16CSumOffset
-
-#define VNETHDR_GSO_NONE         0       // Not a GSO frame
-#define VNETHDR_GSO_TCPV4        1       // GSO frame, IPv4 TCP (TSO)
-#define VNETHDR_GSO_UDP          3       // GSO frame, IPv4 UDP (UFO)
-#define VNETHDR_GSO_TCPV6        4       // GSO frame, IPv6 TCP
-#define VNETHDR_GSO_ECN          0x80    // TCP has ECN set
-
-struct VNetHdr
-{
-    uint8_t  u8Flags;
-    uint8_t  u8GSOType;
-    uint16_t u16HdrLen;
-    uint16_t u16GSOSize;
-    uint16_t u16CSumStart;
-    uint16_t u16CSumOffset;
-};
-typedef struct VNetHdr VNETHDR;
-typedef VNETHDR *PVNETHDR;
-AssertCompileSize(VNETHDR, 10);
-
-struct VNetHdrMrx
-{
-    VNETHDR  Hdr;
-    uint16_t u16NumBufs;
-};
-typedef struct VNetHdrMrx VNETHDRMRX;
-typedef VNETHDRMRX *PVNETHDRMRX;
-AssertCompileSize(VNETHDRMRX, 12);
-
-AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
-
-#define VNET_OK                    0
-#define VNET_ERROR                 1
-typedef uint8_t VNETCTLACK;
-
-#define VNET_CTRL_CLS_RX_MODE          0
-#define VNET_CTRL_CMD_RX_MODE_PROMISC  0
-#define VNET_CTRL_CMD_RX_MODE_ALLMULTI 1
-
-#define VNET_CTRL_CLS_MAC              1
-#define VNET_CTRL_CMD_MAC_TABLE_SET    0
-
-#define VNET_CTRL_CLS_VLAN             2
-#define VNET_CTRL_CMD_VLAN_ADD         0
-#define VNET_CTRL_CMD_VLAN_DEL         1
-
-
-typedef struct VNetCtlHdr
-{
-    uint8_t  u8Class;
-    uint8_t  u8Command;
-} VNETCTLHDR;
-AssertCompileSize(VNETCTLHDR, 2);
-typedef VNETCTLHDR *PVNETCTLHDR;
-
-
-
-#ifdef IN_RING3
-
-/** Returns true if large packets are written into several RX buffers. */
-DECLINLINE(bool) vnetR3MergeableRxBuffers(PVNETSTATE pThis)
-{
-    return !!(pThis->VPCI.uGuestFeatures & VNET_F_MRG_RXBUF);
-}
-
-#define VNET_R3_CS_ENTER_RETURN_VOID(a_pDevIns, a_pThis) VPCI_R3_CS_ENTER_RETURN_VOID(a_pDevIns, &(a_pThis)->VPCI)
-
-DECLINLINE(int) vnetR3CsEnter(PPDMDEVINS pDevIns, PVNETSTATE pThis, int rcBusy)
-{
-    return vpciCsEnter(pDevIns, &pThis->VPCI, rcBusy);
-}
-
-DECLINLINE(void) vnetR3CsLeave(PPDMDEVINS pDevIns, PVNETSTATE pThis)
-{
-    vpciCsLeave(pDevIns, &pThis->VPCI);
-}
-
-
-DECLINLINE(int) vnetCsRxEnter(PVNETSTATE pThis, int rcBusy)
-{
-    RT_NOREF_PV(pThis);
-    RT_NOREF_PV(rcBusy);
-    // STAM_PROFILE_START(&pThis->CTXSUFF(StatCsRx), a);
-    // int rc = PDMCritSectEnter(&pThis->csRx, rcBusy);
-    // STAM_PROFILE_STOP(&pThis->CTXSUFF(StatCsRx), a);
-    // return rc;
-    return VINF_SUCCESS;
-}
-
-DECLINLINE(void) vnetCsRxLeave(PVNETSTATE pThis)
-{
-    RT_NOREF_PV(pThis);
-    // PDMCritSectLeave(&pThis->csRx);
-}
-
-/**
- * A helper function to detect the link state to the other side of "the wire".
- *
- * When deciding to bring up the link we need to take into account both if the
- * cable is connected and if our device is actually connected to the outside
- * world. If no driver is attached we won't start the TX thread nor we will
- * initialize the TX semaphore, which is a problem for the TX queue handler.
- *
- * @returns true if the device is connected to something.
- *
- * @param   pDevIns     The device instance.
- */
-DECLINLINE(bool) vnetR3IsConnected(PPDMDEVINS pDevIns)
-{
-    PVNETSTATE   pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    return pThis->fCableConnected && pThisCC->pDrv;
+/**
+ * Wakeup PDM managed downstream (e.g. hierarchically inferior device's) RX thread
+ */
+static DECLCALLBACK(void) virtioNetWakeupRxBufWaiter(PPDMDEVINS pDevIns)
+{
+    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+    AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
+
+    STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
+    if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
+    {
+        Log10Func(("[%s] Waking downstream device's Rx buf waiter thread\n", pThis->szInst));
+        int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
+        AssertRC(rc);
+    }
+}
+
+/**
+ * Guest notifying us of its activity with a queue. Figure out which queue and respond accordingly.
+ *
+ * @callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
+ */
+static DECLCALLBACK(void) virtioNetVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
+{
+    RT_NOREF(pVirtio);
+    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+    PVIRTIONETVIRTQ  pVirtq  = &pThis->aVirtqs[uVirtqNbr];
+    PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+
+#if defined (IN_RING3) && defined (LOG_ENABLED)
+    RTLogFlush(NULL);
+#endif
+    if (IS_RX_VIRTQ(uVirtqNbr))
+    {
+        uint16_t cBufsAvailable = virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr);
+
+        if (cBufsAvailable)
+        {
+            Log10Func(("%s %u empty bufs added to %s by guest (notifying leaf device)\n",
+                       pThis->szInst, cBufsAvailable, pVirtq->szName));
+            virtioNetWakeupRxBufWaiter(pDevIns);
+        }
+        else
+            Log10Func(("%s \n\n***WARNING: %s notified but no empty bufs added by guest! (skip leaf dev. notification)\n\n",
+                    pThis->szInst, pVirtq->szName));
+    }
+    else if (IS_TX_VIRTQ(uVirtqNbr) || IS_CTRL_VIRTQ(uVirtqNbr))
+    {
+        /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
+        if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
+        {
+            if (ASMAtomicReadBool(&pWorker->fSleeping))
+            {
+                Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
+
+                int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
+                AssertRC(rc);
+            }
+            else
+                Log10Func(("[%s] %s has available buffers - worker already awake\n", pThis->szInst, pVirtq->szName));
+        }
+        else
+            Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
+    }
+    else
+        LogRelFunc(("[%s] unrecognized queue %s (idx=%d) notified\n", pThis->szInst, pVirtq->szName, uVirtqNbr));
+}
+
+#ifdef IN_RING3 /* spans most of the file, at the moment. */
+
+/**
+ * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
+ */
+static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+    PVIRTIONET       pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
+
+    Log10Func(("[%s]\n", pThis->szInst));
+    RT_NOREF(pThis);
+    return PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
+}
+
+/**
+ * Set queue names, distinguishing between modern or legacy mode.
+ *
+ * @note This makes it obvious during logging which mode this transitional device is
+ *       operating in, legacy or modern.
+ *
+ * @param  pThis        Device specific device state
+ * @param  fLegacy      (input) true if running in legacy mode
+ *                              false if running in modern mode
+ */
+DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis, uint32_t fLegacy)
+{
+    RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, fLegacy ? "legacy-ctrlq" : " modern-ctrlq");
+    for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
+    {
+        RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-recvq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
+        RTStrPrintf(pThis->aVirtqs[TXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-xmitq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
+    }
 }
 
@@ -396,234 +756,342 @@
  * @param   pszText     A string denoting direction of packet transfer.
  */
-DECLINLINE(void) vnetR3PacketDump(PVNETSTATE pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
-{
-# ifdef DEBUG
-#  if 0
-    Log(("%s %s packet #%d (%d bytes):\n",
-         INSTANCE(pThis), pszText, ++pThis->u32PktNo, cb));
-    Log3(("%.*Rhxd\n", cb, pbPacket));
-#  else
-    vboxEthPacketDump(INSTANCE(pThis), pszText, pbPacket, (uint32_t)cb);
-#  endif
-# else
-    RT_NOREF4(pThis, pbPacket, cb, pszText);
-# endif
-}
-#endif /* IN_RING3 */
-
-/**
- * Print features given in uFeatures to debug log.
- *
- * @param   pThis      The virtio-net shared instance data.
- * @param   fFeatures   Descriptions of which features to print.
- * @param   pcszText    A string to print before the list of features.
- */
-DECLINLINE(void) vnetPrintFeatures(PVNETSTATE pThis, uint32_t fFeatures, const char *pcszText)
+DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
 {
 #ifdef LOG_ENABLED
-    static struct
-    {
-        uint32_t fMask;
-        const char *pcszDesc;
-    } const s_aFeatures[] =
-    {
-        { VNET_F_CSUM,       "host handles pkts w/ partial csum" },
-        { VNET_F_GUEST_CSUM, "guest handles pkts w/ partial csum" },
-        { VNET_F_MAC,        "host has given MAC address" },
-        { VNET_F_GSO,        "host handles pkts w/ any GSO type" },
-        { VNET_F_GUEST_TSO4, "guest can handle TSOv4 in" },
-        { VNET_F_GUEST_TSO6, "guest can handle TSOv6 in" },
-        { VNET_F_GUEST_ECN,  "guest can handle TSO[6] w/ ECN in" },
-        { VNET_F_GUEST_UFO,  "guest can handle UFO in" },
-        { VNET_F_HOST_TSO4,  "host can handle TSOv4 in" },
-        { VNET_F_HOST_TSO6,  "host can handle TSOv6 in" },
-        { VNET_F_HOST_ECN,   "host can handle TSO[6] w/ ECN in" },
-        { VNET_F_HOST_UFO,   "host can handle UFO in" },
-        { VNET_F_MRG_RXBUF,  "host can merge receive buffers" },
-        { VNET_F_STATUS,     "virtio_net_config.status available" },
-        { VNET_F_CTRL_VQ,    "control channel available" },
-        { VNET_F_CTRL_RX,    "control channel RX mode support" },
-        { VNET_F_CTRL_VLAN,  "control channel VLAN filtering" }
-    };
-
-    Log3(("%s %s:\n", INSTANCE(pThis), pcszText));
-    for (unsigned i = 0; i < RT_ELEMENTS(s_aFeatures); ++i)
-    {
-        if (s_aFeatures[i].fMask & fFeatures)
-            Log3(("%s --> %s\n", INSTANCE(pThis), s_aFeatures[i].pcszDesc));
-    }
-#else  /* !LOG_ENABLED */
-    RT_NOREF3(pThis, fFeatures, pcszText);
-#endif /* !LOG_ENABLED */
-}
-
-/**
- * @interface_method_impl{VPCIIOCALLBACKS,pfnGetHostFeatures}
- */
-static DECLCALLBACK(uint32_t) vnetIoCb_GetHostFeatures(PVPCISTATE pVPciState)
-{
-    RT_NOREF_PV(pVPciState);
-
-    /* We support:
-     * - Host-provided MAC address
-     * - Link status reporting in config space
-     * - Control queue
-     * - RX mode setting
-     * - MAC filter table
-     * - VLAN filter
-     */
-    return VNET_F_MAC
-         | VNET_F_STATUS
-         | VNET_F_CTRL_VQ
-         | VNET_F_CTRL_RX
-         | VNET_F_CTRL_VLAN
-#ifdef VNET_WITH_GSO
-         | VNET_F_CSUM
-         | VNET_F_HOST_TSO4
-         | VNET_F_HOST_TSO6
-         | VNET_F_HOST_UFO
-         | VNET_F_GUEST_CSUM   /* We expect the guest to accept partial TCP checksums (see @bugref{4796}) */
-         | VNET_F_GUEST_TSO4
-         | VNET_F_GUEST_TSO6
-         | VNET_F_GUEST_UFO
+    if (!LogIs12Enabled())
+        return;
 #endif
-#ifdef VNET_WITH_MERGEABLE_RX_BUFS
-         | VNET_F_MRG_RXBUF
+    vboxEthPacketDump(pThis->szInst, pszText, pbPacket, (uint32_t)cb);
+}
+
+#ifdef LOG_ENABLED
+void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONETPKTHDR pRxPktHdr,
+                     uint16_t cVirtqBufs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS GCPhysRxBuf, uint8_t cbRxBuf)
+{
+    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    pRxPktHdr->uNumBuffers = cVirtqBufs;
+    if (pRxPktHdr)
+    {
+        LogFunc(("%*c\nrxPktHdr\n"
+                 "    uFlags ......... %2.2x\n    uGsoType ....... %2.2x\n    uHdrLen ........ %4.4x\n"
+                 "    uGsoSize ....... %4.4x\n    uChksumStart ... %4.4x\n    uChksumOffset .. %4.4x\n",
+                        60, ' ', pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
+                        pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset));
+        if (!virtioCoreIsLegacyMode(&pThis->Virtio) || FEATURE_ENABLED(MRG_RXBUF))
+            LogFunc(("    uNumBuffers .... %4.4x\n", pRxPktHdr->uNumBuffers));
+        virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONETPKTHDR), 0, "Dump of virtual rPktHdr");
+    }
+    virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+    LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
+    virtioCoreGCPhysHexDump(pDevIns, GCPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
+    LogFunc(("%*c", 60, '-'));
+}
+
+#endif /* LOG_ENABLED */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
+ */
+static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns,  PVIRTIONET);
+    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+    bool fNone     = pszArgs && *pszArgs == '\0';
+    bool fAll      = pszArgs && (*pszArgs == 'a' || *pszArgs == 'A'); /* "all"      */
+    bool fNetwork  = pszArgs && (*pszArgs == 'n' || *pszArgs == 'N'); /* "network"  */
+    bool fFeatures = pszArgs && (*pszArgs == 'f' || *pszArgs == 'F'); /* "features" */
+    bool fState    = pszArgs && (*pszArgs == 's' || *pszArgs == 'S'); /* "state"    */
+    bool fPointers = pszArgs && (*pszArgs == 'p' || *pszArgs == 'P'); /* "pointers" */
+    bool fVirtqs   = pszArgs && (*pszArgs == 'q' || *pszArgs == 'Q'); /* "queues    */
+
+    /* Show basic information. */
+    pHlp->pfnPrintf(pHlp,
+        "\n"
+        "---------------------------------------------------------------------------\n"
+        "Debug Info: %s\n"
+        "        (options: [a]ll, [n]et, [f]eatures, [s]tate, [p]ointers, [q]ueues)\n"
+        "---------------------------------------------------------------------------\n\n",
+        pThis->szInst);
+
+    if (fNone)
+        return;
+
+    /* Show offered/unoffered, accepted/rejected features */
+    if (fAll || fFeatures)
+    {
+        virtioCorePrintDeviceFeatures(&pThis->Virtio, pHlp, s_aDevSpecificFeatures,
+            RT_ELEMENTS(s_aDevSpecificFeatures));
+        pHlp->pfnPrintf(pHlp, "\n");
+    }
+
+    /* Show queues (and associate worker info if applicable) */
+    if (fAll || fVirtqs)
+    {
+        pHlp->pfnPrintf(pHlp, "Virtq information:\n\n");
+        for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+        {
+            PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+
+            if (pVirtq->fHasWorker)
+            {
+                PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[uVirtqNbr];
+                PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
+
+                Assert((pWorker->uIdx == pVirtq->uIdx));
+                Assert((pWorkerR3->uIdx == pVirtq->uIdx));
+
+                if (pWorker->fAssigned)
+                {
+                    pHlp->pfnPrintf(pHlp, "    %-15s (pThread: %p %s) ",
+                        pVirtq->szName,
+                        pWorkerR3->pThread,
+                        virtioNetThreadStateName(pWorkerR3->pThread));
+                    if (pVirtq->fAttachedToVirtioCore)
+                    {
+                        pHlp->pfnPrintf(pHlp, "worker: ");
+                        pHlp->pfnPrintf(pHlp, "%s", pWorker->fSleeping ? "blocking" : "unblocked");
+                        pHlp->pfnPrintf(pHlp, "%s", pWorker->fNotified ? ", notified" : "");
+                    }
+                    else
+                    if (pWorker->fNotified)
+                        pHlp->pfnPrintf(pHlp, "not attached to virtio core");
+                }
+            }
+            else
+            {
+                pHlp->pfnPrintf(pHlp, "    %-15s (INetworkDown's thread) %s", pVirtq->szName,
+                    pVirtq->fAttachedToVirtioCore  ? "" : "not attached to virtio core");
+            }
+            pHlp->pfnPrintf(pHlp, "\n");
+            virtioCoreR3VirtqInfo(pDevIns, pHlp, pszArgs, uVirtqNbr);
+            pHlp->pfnPrintf(pHlp, "    ---------------------------------------------------------------------\n");
+            pHlp->pfnPrintf(pHlp, "\n");
+        }
+        pHlp->pfnPrintf(pHlp, "\n");
+    }
+
+    /* Show various pointers */
+    if (fAll || fPointers)
+    {
+        pHlp->pfnPrintf(pHlp, "Internal Pointers (for instance \"%s\"):\n\n", pThis->szInst);
+        pHlp->pfnPrintf(pHlp, "    pDevIns ................... %p\n",   pDevIns);
+        pHlp->pfnPrintf(pHlp, "    PVIRTIOCORE ............... %p\n",   &pThis->Virtio);
+        pHlp->pfnPrintf(pHlp, "    PVIRTIONET ................ %p\n",   pThis);
+        pHlp->pfnPrintf(pHlp, "    PVIRTIONETCC .............. %p\n",   pThisCC);
+        pHlp->pfnPrintf(pHlp, "    VIRTIONETVIRTQ[] .......... %p\n",   pThis->aVirtqs);
+        pHlp->pfnPrintf(pHlp, "    pDrvBase .................. %p\n",   pThisCC->pDrvBase);
+        pHlp->pfnPrintf(pHlp, "    pDrv ...................... %p\n",   pThisCC->pDrv);
+        pHlp->pfnPrintf(pHlp, "    pDrv ...................... %p\n",   pThisCC->pDrv);
+        pHlp->pfnPrintf(pHlp, "\n");
+    }
+
+    /* Show device state info */
+    if (fAll || fState)
+    {
+        pHlp->pfnPrintf(pHlp, "Device state:\n\n");
+        uint32_t fTransmitting = ASMAtomicReadU32(&pThis->uIsTransmitting);
+
+        pHlp->pfnPrintf(pHlp, "    Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
+        pHlp->pfnPrintf(pHlp, "\n");
+        pHlp->pfnPrintf(pHlp, "Misc state\n");
+        pHlp->pfnPrintf(pHlp, "\n");
+        pHlp->pfnPrintf(pHlp, "    fOfferLegacy .............. %d\n",   pThis->fOfferLegacy);
+        pHlp->pfnPrintf(pHlp, "    fVirtioReady .............. %d\n",   pThis->fVirtioReady);
+        pHlp->pfnPrintf(pHlp, "    fResetting ................ %d\n",   pThis->fResetting);
+        pHlp->pfnPrintf(pHlp, "    fGenUpdatePending ......... %d\n",   pThis->Virtio.fGenUpdatePending);
+        pHlp->pfnPrintf(pHlp, "    fMsiSupport ............... %d\n",   pThis->Virtio.fMsiSupport);
+        pHlp->pfnPrintf(pHlp, "    uConfigGeneration ......... %d\n",   pThis->Virtio.uConfigGeneration);
+        pHlp->pfnPrintf(pHlp, "    uDeviceStatus ............. 0x%x\n", pThis->Virtio.fDeviceStatus);
+        pHlp->pfnPrintf(pHlp, "    cVirtqPairs .,............. %d\n",   pThis->cVirtqPairs);
+        pHlp->pfnPrintf(pHlp, "    cVirtqs .,................. %d\n",   pThis->cVirtqs);
+        pHlp->pfnPrintf(pHlp, "    cWorkers .................. %d\n",   pThis->cWorkers);
+        pHlp->pfnPrintf(pHlp, "    MMIO mapping name ......... %d\n",   pThisCC->Virtio.szMmioName);
+        pHlp->pfnPrintf(pHlp, "\n");
+    }
+
+    /* Show network related information */
+    if (fAll || fNetwork)
+    {
+        pHlp->pfnPrintf(pHlp, "Network configuration:\n\n");
+        pHlp->pfnPrintf(pHlp, "    MAC: ...................... %RTmac\n", &pThis->macConfigured);
+        pHlp->pfnPrintf(pHlp, "\n");
+        pHlp->pfnPrintf(pHlp, "    Cable: .................... %s\n",      pThis->fCableConnected ? "connected" : "disconnected");
+        pHlp->pfnPrintf(pHlp, "    Link-up delay: ............ %d ms\n",   pThis->cMsLinkUpDelay);
+        pHlp->pfnPrintf(pHlp, "\n");
+        pHlp->pfnPrintf(pHlp, "    Accept all multicast: ..... %s\n",      pThis->fAllMulticast  ? "true" : "false");
+        pHlp->pfnPrintf(pHlp, "    Suppress broadcast: ....... %s\n",      pThis->fNoBroadcast   ? "true" : "false");
+        pHlp->pfnPrintf(pHlp, "    Suppress unicast: ......... %s\n",      pThis->fNoUnicast     ? "true" : "false");
+        pHlp->pfnPrintf(pHlp, "    Suppress multicast: ....... %s\n",      pThis->fNoMulticast   ? "true" : "false");
+        pHlp->pfnPrintf(pHlp, "    Promiscuous: .............. %s\n",      pThis->fPromiscuous   ? "true" : "false");
+        pHlp->pfnPrintf(pHlp, "\n");
+        pHlp->pfnPrintf(pHlp, "    Default Rx MAC filter: .... %RTmac\n", pThis->rxFilterMacDefault);
+        pHlp->pfnPrintf(pHlp, "\n");
+
+        pHlp->pfnPrintf(pHlp, "    Unicast filter MACs:\n");
+
+        if (!pThis->cUnicastFilterMacs)
+            pHlp->pfnPrintf(pHlp, "        <none>\n");
+
+        for (uint32_t i = 0; i < pThis->cUnicastFilterMacs; i++)
+            pHlp->pfnPrintf(pHlp, "        %RTmac\n", &pThis->aMacUnicastFilter[i]);
+
+        pHlp->pfnPrintf(pHlp, "\n    Multicast filter MACs:\n");
+
+        if (!pThis->cMulticastFilterMacs)
+            pHlp->pfnPrintf(pHlp, "        <none>\n");
+
+        for (uint32_t i = 0; i < pThis->cMulticastFilterMacs; i++)
+            pHlp->pfnPrintf(pHlp, "        %RTmac\n", &pThis->aMacMulticastFilter[i]);
+
+        pHlp->pfnPrintf(pHlp, "\n\n");
+        pHlp->pfnPrintf(pHlp, "    Leaf starved: ............. %s\n",      pThis->fLeafWantsEmptyRxBufs ? "true" : "false");
+        pHlp->pfnPrintf(pHlp, "\n");
+    }
+    /** @todo implement this
+      * pHlp->pfnPrintf(pHlp, "\n");
+      * virtioCoreR3Info(pDevIns, pHlp, pszArgs);
+      */
+    pHlp->pfnPrintf(pHlp, "\n");
+}
+
+/**
+ * Checks whether certain mutually dependent negotiated features are clustered in required combinations.
+ *
+ * @note See VirtIO 1.0 spec, Section 5.1.3.1
+ *
+ * @param fFeatures     Bitmask of negotiated features to evaluate
+ *
+ * @returns             true if valid feature combination(s) found.
+ *                      false if non-valid feature set.
+ */
+DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
+{
+    uint32_t fGuestChksumRequired =   fFeatures & VIRTIONET_F_GUEST_TSO4
+                                   || fFeatures & VIRTIONET_F_GUEST_TSO6
+                                   || fFeatures & VIRTIONET_F_GUEST_UFO;
+
+    uint32_t fHostChksumRequired =    fFeatures & VIRTIONET_F_HOST_TSO4
+                                   || fFeatures & VIRTIONET_F_HOST_TSO6
+                                   || fFeatures & VIRTIONET_F_HOST_UFO;
+
+    uint32_t fCtrlVqRequired =        fFeatures & VIRTIONET_F_CTRL_RX
+                                   || fFeatures & VIRTIONET_F_CTRL_VLAN
+                                   || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
+                                   || fFeatures & VIRTIONET_F_MQ
+                                   || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
+
+    if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
+        return false;
+
+    if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
+        return false;
+
+    if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
+        return false;
+
+    if (   fFeatures & VIRTIONET_F_GUEST_ECN
+        && !(   fFeatures & VIRTIONET_F_GUEST_TSO4
+             || fFeatures & VIRTIONET_F_GUEST_TSO6))
+                    return false;
+
+    if (   fFeatures & VIRTIONET_F_HOST_ECN
+        && !(   fFeatures & VIRTIONET_F_HOST_TSO4
+             || fFeatures & VIRTIONET_F_HOST_TSO6))
+                    return false;
+    return true;
+}
+
+/**
+ * Read or write device-specific configuration parameters.
+ * This is called by VirtIO core code a guest-initiated MMIO access is made to access device-specific
+ * configuration
+ *
+ * @note  See VirtIO 1.0 spec, 2.3 Device Configuration Space
+ *
+ * @param pThis             Pointer to device-specific state
+ * @param uOffsetOfAccess   Offset (within VIRTIONET_CONFIG_T)
+ * @param pv                Pointer to data to read or write
+ * @param cb                Number of bytes to read or write
+ * @param fWrite            True if writing, false if reading
+ *
+ * @returns VINF_SUCCESS if successful, or VINF_IOM_MMIO_UNUSED if fails (bad offset or size)
+ */
+static int virtioNetR3DevCfgAccess(PVIRTIONET pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
+{
+    AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
+
+    if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uMacAddress,      VIRTIONET_CONFIG_T, uOffsetOfAccess))
+        VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMacAddress,      VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
+#if FEATURE_OFFERED(STATUS)
+    else
+    if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uStatus,          VIRTIONET_CONFIG_T, uOffsetOfAccess))
+        VIRTIO_DEV_CONFIG_ACCESS_READONLY( uStatus,          VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
 #endif
-         ;
-}
-
-/**
- * @interface_method_impl{VPCIIOCALLBACKS,pfnGetHostMinimalFeatures}
- */
-static DECLCALLBACK(uint32_t) vnetIoCb_GetHostMinimalFeatures(PVPCISTATE pVPciState)
-{
-    RT_NOREF_PV(pVPciState);
-    return VNET_F_MAC;
-}
-
-/**
- * @interface_method_impl{VPCIIOCALLBACKS,pfnSetHostFeatures}
- */
-static DECLCALLBACK(void) vnetIoCb_SetHostFeatures(PVPCISTATE pVPciState, uint32_t fFeatures)
-{
-    PVNETSTATE pThis = RT_FROM_MEMBER(pVPciState, VNETSTATE, VPCI);
-    /** @todo Nothing to do here yet */
-    LogFlow(("%s vnetIoCb_SetHostFeatures: uFeatures=%x\n", INSTANCE(pThis), fFeatures));
-    vnetPrintFeatures(pThis, fFeatures, "The guest negotiated the following features");
-}
-
-/**
- * @interface_method_impl{VPCIIOCALLBACKS,pfnGetConfig}
- */
-static DECLCALLBACK(int) vnetIoCb_GetConfig(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *data)
-{
-    PVNETSTATE pThis = RT_FROM_MEMBER(pVPciState, VNETSTATE, VPCI);
-    if (offCfg + cb > sizeof(struct VNetPCIConfig))
-    {
-        Log(("%s vnetIoCb_GetConfig: Read beyond the config structure is attempted (offCfg=%#x cb=%x).\n", INSTANCE(pThis), offCfg, cb));
-        return VERR_IOM_IOPORT_UNUSED;
-    }
-    memcpy(data, (uint8_t *)&pThis->config + offCfg, cb);
+#if FEATURE_OFFERED(MQ)
+    else
+    if (VIRTIO_DEV_CONFIG_MATCH_MEMBER(    uMaxVirtqPairs,   VIRTIONET_CONFIG_T, uOffsetOfAccess))
+        VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxVirtqPairs,   VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
+#endif
+    else
+    {
+        LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n",
+                pThis->szInst, uOffsetOfAccess, uOffsetOfAccess, cb));
+        return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
+    }
     return VINF_SUCCESS;
 }
 
 /**
- * @interface_method_impl{VPCIIOCALLBACKS,pfnSetConfig}
- */
-static DECLCALLBACK(int) vnetIoCb_SetConfig(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *data)
-{
-    PVNETSTATE pThis = RT_FROM_MEMBER(pVPciState, VNETSTATE, VPCI);
-    if (offCfg + cb > sizeof(struct VNetPCIConfig))
-    {
-        Log(("%s vnetIoCb_SetConfig: Write beyond the config structure is attempted (offCfg=%#x cb=%x).\n", INSTANCE(pThis), offCfg, cb));
-        if (offCfg < sizeof(struct VNetPCIConfig))
-            memcpy((uint8_t *)&pThis->config + offCfg, data, sizeof(struct VNetPCIConfig) - offCfg);
-        return VINF_SUCCESS;
-    }
-    memcpy((uint8_t *)&pThis->config + offCfg, data, cb);
-    return VINF_SUCCESS;
-}
-
-/**
- * @interface_method_impl{VPCIIOCALLBACKS,pfnReset}
- *
- * Hardware reset. Revert all registers to initial values.
- */
-static DECLCALLBACK(int) vnetIoCb_Reset(PPDMDEVINS pDevIns)
-{
-#ifndef IN_RING3
-    RT_NOREF(pDevIns);
-    return VINF_IOM_R3_IOPORT_WRITE;
-#else
-    PVNETSTATE   pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-
-    Log(("%s Reset triggered\n", INSTANCE(pThis)));
-
-    int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
-    if (RT_UNLIKELY(rc != VINF_SUCCESS))
-    {
-        LogRel(("vnetIoCb_Reset failed to enter RX critical section!\n"));
-        return rc;
-    }
-    vpciReset(pDevIns, &pThis->VPCI);
-    vnetCsRxLeave(pThis);
-
-    /// @todo Implement reset
-    if (vnetR3IsConnected(pDevIns))
-        pThis->config.uStatus = VNET_S_LINK_UP;
-    else
-        pThis->config.uStatus = 0;
-    Log(("%s vnetIoCb_Reset: Link is %s\n", INSTANCE(pThis), vnetR3IsConnected(pDevIns) ? "up" : "down"));
-
-    /*
-     * By default we pass all packets up since the older guests cannot control
-     * virtio mode.
-     */
-    pThis->fPromiscuous      = true;
-    pThis->fAllMulti         = false;
-    pThis->cMacFilterEntries = 0;
-    memset(pThis->aMacFilter,  0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
-    memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
-    pThis->uIsTransmitting   = 0;
-    if (pThisCC->pDrv)
-        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
-    return VINF_SUCCESS;
-#endif
-}
-
-
-/**
- * Wakeup the RX thread.
- */
-static void vnetWakeupReceive(PPDMDEVINS pDevIns)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    if (    pThis->fMaybeOutOfSpace
-        &&  pThis->hEventMoreRxDescAvail != NIL_SUPSEMEVENT)
-    {
-        STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
-        Log(("%s Waking up Out-of-RX-space semaphore\n",  INSTANCE(pThis)));
-        int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventMoreRxDescAvail);
-        AssertRC(rc);
-    }
-}
-
-#ifdef IN_RING3
-
-/**
- * Helper function that raises an interrupt if the guest is ready to receive it.
- */
-static int vnetR3RaiseInterrupt(PPDMDEVINS pDevIns, PVNETSTATE pThis, int rcBusy, uint8_t u8IntCause)
-{
-    if (pThis->VPCI.uStatus & VPCI_STATUS_DRV_OK)
-        return vpciRaiseInterrupt(pDevIns, &pThis->VPCI, rcBusy, u8IntCause);
-    return rcBusy;
-}
-
-
-/**
- * Takes down the link temporarily if it's current status is up.
+ * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
+ */
+static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
+{
+    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+    RT_NOREF(pThis);
+    return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
+}
+
+/**
+ * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
+ */
+static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
+{
+    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+    Log10Func(("[%s] uOffset: %u, cb: %u: %.*Rhxs\n", pThis->szInst, uOffset, cb, cb, pv));
+    RT_NOREF(pThis);
+    return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
+}
+
+static int virtioNetR3VirtqDestroy(PVIRTIOCORE pVirtio, PVIRTIONETVIRTQ pVirtq)
+{
+    PVIRTIONET         pThis     = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
+    PVIRTIONETCC       pThisCC   = PDMDEVINS_2_DATA_CC(pVirtio->pDevInsR3, PVIRTIONETCC);
+    PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[pVirtq->uIdx];
+    PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[pVirtq->uIdx];
+
+    int rc = VINF_SUCCESS, rcThread;
+    Log10Func(("[%s] Destroying \"%s\"", pThis->szInst, pVirtq->szName));
+    if (pVirtq->fHasWorker)
+    {
+        Log10((" and its worker"));
+        rc = PDMDevHlpSUPSemEventClose(pVirtio->pDevInsR3, pWorker->hEvtProcess);
+        AssertRCReturn(rc, rc);
+        pWorker->hEvtProcess = 0;
+        rc = PDMDevHlpThreadDestroy(pVirtio->pDevInsR3,  pWorkerR3->pThread, &rcThread);
+        AssertRCReturn(rc, rc);
+        pWorkerR3->pThread = 0;
+        pVirtq->fHasWorker = false;
+    }
+    pWorker->fAssigned = false;
+    pVirtq->fCtlVirtq  = false;
+    Log10(("\n"));
+    return rc;
+}
+
+/**
+ * Takes down the link temporarily if its current status is up.
  *
  * This is used during restore and when replumbing the network link.
@@ -637,102 +1105,523 @@
  * @param   pThisCC     The virtio-net ring-3 instance data.
  */
-static void vnetR3TempLinkDown(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
-{
-    if (pThis->config.uStatus & VNET_S_LINK_UP)
-    {
-        pThis->config.uStatus &= ~VNET_S_LINK_UP;
-        vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
-        /* Restore the link back in 5 seconds. */
+static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
+{
+    if (IS_LINK_UP(pThis))
+    {
+        SET_LINK_DOWN(pThis);
+
+        /* Re-establish link in 5 seconds. */
         int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
         AssertRC(rc);
-        Log(("%s vnetR3TempLinkDown: Link is down temporarily\n", INSTANCE(pThis)));
-    }
-}
-
-
-/**
- * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
- */
-static DECLCALLBACK(void) vnetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
-{
-    PVNETSTATE   pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    RT_NOREF(hTimer, pvUser);
-
-    VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
-
-    Log(("%s vnetR3LinkUpTimer: connected=%s\n", INSTANCE(pThis), vnetR3IsConnected(pDevIns)?"true":"false"));
-    /* Do not bring up the link if the device is not connected. */
-    if (vnetR3IsConnected(pDevIns))
-    {
-        pThis->config.uStatus |= VNET_S_LINK_UP;
-        vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
-        vnetWakeupReceive(pDevIns);
-    }
-
-    vnetR3CsLeave(pDevIns, pThis);
-
-    Log(("%s vnetR3LinkUpTimer: Link is up\n", INSTANCE(pThis)));
+
+        LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
+    }
+}
+
+
+static void virtioNetConfigurePktHdr(PVIRTIONET pThis, uint32_t fLegacy)
+{
+    /* Calculate network packet header type and size based on what we know now */
+    pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
+    if (!fLegacy)
+        /* Modern (e.g. >= VirtIO 1.0) device specification's pkt size rules */
+        if (FEATURE_ENABLED(MRG_RXBUF))
+            pThis->ePktHdrType = kVirtioNetModernPktHdrWithMrgRx;
+        else /* Modern guest driver with MRG_RX feature disabled */
+            pThis->ePktHdrType = kVirtioNetModernPktHdrWithoutMrgRx;
+    else
+    {
+        /* Legacy (e.g. < VirtIO 1.0) device specification's pkt size rules */
+        if (FEATURE_ENABLED(MRG_RXBUF))
+            pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithMrgRx;
+        else /* Legacy guest with MRG_RX feature disabled */
+        {
+            pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
+            pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
+        }
+    }
+}
+
+
+/*********************************************************************************************************************************
+*   Saved state                                                                                                                  *
+*********************************************************************************************************************************/
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ *
+ * @note: This is included to accept and migrate VMs that had used the original VirtualBox legacy-only virtio-net (network card)
+ *        controller device emulator ("DevVirtioNet.cpp") to work with this superset of VirtIO compatibility known
+ *        as a transitional device (see PDM-invoked device constructor comments for more information)
+ */
+static DECLCALLBACK(int) virtioNetR3LegacyDeviceLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass,
+                                                    RTMAC uMacLoaded)
+{
+    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+    PCPDMDEVHLPR3  pHlp    = pDevIns->pHlpR3;
+    int rc;
+
+    Log7Func(("[%s] LOAD EXEC (LEGACY)!!\n", pThis->szInst));
+
+    if (   memcmp(&uMacLoaded.au8, &pThis->macConfigured.au8, sizeof(uMacLoaded))
+        && (   uPass == 0
+            || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
+        LogRelFunc(("[%s]: The mac address differs: config=%RTmac saved=%RTmac\n",
+            pThis->szInst, &pThis->macConfigured, &uMacLoaded));
+
+    if (uPass == SSM_PASS_FINAL)
+    {
+        /* Call the virtio core to have it load legacy device state */
+        rc = virtioCoreR3LegacyDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion, VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY);
+        AssertRCReturn(rc, rc);
+        /*
+         * Scan constructor-determined virtqs to determine if they are all valid-as-restored.
+         * If so, nudge them with a signal, otherwise destroy the unusable queue(s)
+         * to avoid tripping up the other queue processing logic.
+         */
+        int cVirtqsToRemove = 0;
+        for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+        {
+            PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+            if (pVirtq->fHasWorker)
+            {
+                if (!virtioCoreR3VirtqIsEnabled(&pThis->Virtio, uVirtqNbr))
+                {
+                    virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
+                    ++cVirtqsToRemove;
+                }
+                else
+                {
+                    if (virtioCoreR3VirtqIsAttached(&pThis->Virtio, uVirtqNbr))
+                    {
+                        Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
+                        rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[pVirtq->uIdx].hEvtProcess);
+                        AssertRCReturn(rc, rc);
+                    }
+                }
+            }
+        }
+        AssertMsg(cVirtqsToRemove < 2, ("Multiple unusable queues in saved state unexpected\n"));
+        pThis->cVirtqs -= cVirtqsToRemove;
+
+        pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus;
+        pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
+
+        rc = pHlp->pfnSSMGetMem(pSSM, pThis->virtioNetConfig.uMacAddress.au8, sizeof(pThis->virtioNetConfig.uMacAddress));
+        AssertRCReturn(rc, rc);
+
+        if (uVersion > VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY)
+        {
+            /* Zero-out the the Unicast/Multicast filter table */
+            memset(&pThis->aMacUnicastFilter[0], 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
+
+            rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
+            AssertRCReturn(rc, rc);
+            rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
+            AssertRCReturn(rc, rc);
+            /*
+             * The 0.95 legacy virtio spec defines a control queue command VIRTIO_NET_CTRL_MAC_TABLE_SET,
+             * wherein guest driver configures two variable length mac filter tables: A unicast filter,
+             * and a multicast filter. However original VBox virtio-net saved both sets of filter entries
+             * in a single table, abandoning the distinction between unicast and multicast filters. It preserved
+             * only *one* filter's table length, leaving no way to separate table back out into respective unicast
+             * and multicast tables this device implementation preserves. Deduced from legacy code, the original
+             * assumption was that the both MAC filters are whitelists that can be processed identically
+             * (from the standpoint of a *single* host receiver), such that the distinction between unicast and
+             * multicast doesn't matter in any one VM's context. Little choice here but to save the undifferentiated
+             * unicast & multicast MACs to the unicast filter table and leave multicast table empty/unused.
+             */
+            uint32_t cCombinedUnicastMulticastEntries;
+            rc = pHlp->pfnSSMGetU32(pSSM, &cCombinedUnicastMulticastEntries);
+            AssertRCReturn(rc, rc);
+            AssertReturn(cCombinedUnicastMulticastEntries <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
+            pThis->cUnicastFilterMacs = cCombinedUnicastMulticastEntries;
+            rc = pHlp->pfnSSMGetMem(pSSM, pThis->aMacUnicastFilter, cCombinedUnicastMulticastEntries * sizeof(RTMAC));
+            AssertRCReturn(rc, rc);
+            rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
+            AssertRCReturn(rc, rc);
+        }
+        else
+        {
+            pThis->fAllMulticast = false;
+            pThis->cUnicastFilterMacs = 0;
+            memset(&pThis->aMacUnicastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
+
+            memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
+
+            pThis->fPromiscuous = true;
+            if (pThisCC->pDrv)
+                pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
+        }
+
+        /*
+         * Log the restored VirtIO feature selection.
+         */
+        pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
+        /** @todo shouldn't we update the virtio header size here? it depends on the negotiated features. */
+        virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
+
+        /*
+         * Configure remaining transitional device parameters presumably or deductively
+         * as these weren't part of the legacy device code thus it didn't save them to SSM
+         */
+        pThis->fCableConnected = 1;
+        pThis->fAllUnicast     = 0;
+        pThis->fNoMulticast    = 0;
+        pThis->fNoUnicast      = 0;
+        pThis->fNoBroadcast    = 0;
+
+        /* Zero out the multicast table and count, all MAC filters, if any, are in the unicast filter table */
+        pThis->cMulticastFilterMacs = 0;
+        memset(&pThis->aMacMulticastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
+    }
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ *
+ * @note: This loads state saved by a Modern (VirtIO 1.0+) device, of which this transitional device is one,
+ *        and thus supports both legacy and modern guest virtio drivers.
+ */
+static DECLCALLBACK(int) virtioNetR3ModernLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+    PCPDMDEVHLPR3  pHlp    = pDevIns->pHlpR3;
+    int rc;
+
+    RT_NOREF(pThisCC);
+
+    RTMAC uMacLoaded, uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
+    rc = pHlp->pfnSSMGetMem(pSSM, &uMacLoaded.au8, sizeof(uMacLoaded.au8));
+    AssertRCReturn(rc, rc);
+    if (memcmp(&uMacLoaded.au8, uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8)))
+    {
+        rc = virtioNetR3LegacyDeviceLoadExec(pDevIns, pSSM, uVersion, uPass, uMacLoaded);
+        return rc;
+    }
+
+    Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
+
+    AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
+    AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVEDSTATE_VERSION,
+                          ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+
+    virtioNetR3SetVirtqNames(pThis, false /* fLegacy */);
+
+    pHlp->pfnSSMGetU64(     pSSM, &pThis->fNegotiatedFeatures);
+
+    pHlp->pfnSSMGetU16(     pSSM, &pThis->cVirtqs);
+    AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2) + 1, VERR_OUT_OF_RANGE);
+    pHlp->pfnSSMGetU16(     pSSM, &pThis->cWorkers);
+    AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS , VERR_OUT_OF_RANGE);
+
+    for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+            pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
+
+    /* Config checks */
+    RTMAC macConfigured;
+    rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
+    AssertRCReturn(rc, rc);
+    if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
+        && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
+            LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
+                    pThis->szInst, &pThis->macConfigured, &macConfigured));
+    memcpy(pThis->virtioNetConfig.uMacAddress.au8, macConfigured.au8, sizeof(macConfigured.au8));
+
+#if FEATURE_OFFERED(STATUS)
+    uint16_t fChkStatus;
+    pHlp->pfnSSMGetU16(     pSSM, &fChkStatus);
+    if (fChkStatus == 0xffff)
+    {
+        /* Dummy value in saved state because status feature wasn't enabled at the time */
+        pThis->virtioNetConfig.uStatus = 0; /* VIRTIO_NET_S_ANNOUNCE disabled */
+        pThis->virtioNetConfig.uStatus = !!IS_LINK_UP(pThis); /* VIRTIO_NET_IS_LINK_UP (bit 0) */
+    }
+    else
+        pThis->virtioNetConfig.uStatus = fChkStatus;
+#else
+    uint16_t fDiscard;
+    pHlp->pfnSSMGetU16(     pSSM, &fDiscard);
+#endif
+
+#if FEATURE_OFFERED(MQ)
+    uint16_t uCheckMaxVirtqPairs;
+    pHlp->pfnSSMGetU16(     pSSM, &uCheckMaxVirtqPairs);
+    if (uCheckMaxVirtqPairs)
+        pThis->virtioNetConfig.uMaxVirtqPairs = uCheckMaxVirtqPairs;
+    else
+        pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_CTRL_MQ_VQ_PAIRS;
+#else
+    uint16_t fDiscard;
+    pHlp->pfnSSMGetU16(     pSSM, &fDiscard);
+#endif
+
+    /* Save device-specific part */
+    pHlp->pfnSSMGetBool(    pSSM, &pThis->fCableConnected);
+    pHlp->pfnSSMGetU8(      pSSM, &pThis->fPromiscuous);
+    pHlp->pfnSSMGetU8(      pSSM, &pThis->fAllMulticast);
+    pHlp->pfnSSMGetU8(      pSSM, &pThis->fAllUnicast);
+    pHlp->pfnSSMGetU8(      pSSM, &pThis->fNoMulticast);
+    pHlp->pfnSSMGetU8(      pSSM, &pThis->fNoUnicast);
+    pHlp->pfnSSMGetU8(      pSSM, &pThis->fNoBroadcast);
+
+    pHlp->pfnSSMGetU32(     pSSM, &pThis->cMulticastFilterMacs);
+    AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
+    pHlp->pfnSSMGetMem(     pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
+
+    if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
+        memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
+               (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
+
+    pHlp->pfnSSMGetU32(     pSSM, &pThis->cUnicastFilterMacs);
+    AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
+    pHlp->pfnSSMGetMem(     pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
+
+    if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
+        memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
+               (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
+
+    rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
+    AssertRCReturn(rc, rc);
+    /*
+     * Call the virtio core to let it load its state.
+     */
+    rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion,
+                                          VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
+    AssertRCReturn(rc, rc);
+    /*
+     * Since the control queue is created proactively in the constructor to accomodate worst-case
+     * legacy guests, even though the queue may have been deducted from queue count while saving state,
+     * we must explicitly remove queue and associated worker thread and context at this point,
+     * or presence of bogus control queue will confuse operations.
+     */
+    PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[CTRLQIDX];
+    if (FEATURE_DISABLED(CTRL_VQ) || !virtioCoreIsVirtqEnabled(&pThis->Virtio, CTRLQIDX))
+    {
+        virtioCoreR3VirtqDetach(&pThis->Virtio, CTRLQIDX);
+        virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
+        pVirtq->fAttachedToVirtioCore = false;
+        --pThis->cWorkers;
+    }
+    /*
+     * Nudge queue workers
+     */
+    for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+    {
+        pVirtq  = &pThis->aVirtqs[uVirtqNbr];
+        if (pVirtq->fAttachedToVirtioCore)
+        {
+            if (pVirtq->fHasWorker)
+            {
+                PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+                Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
+                rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
+                AssertRCReturn(rc, rc);
+            }
+        }
+    }
+    pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus; /* reflects state to guest driver */
+    pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
+    virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
+    return rc;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
+ *                      loading.}
+ */
+static DECLCALLBACK(int) virtioNetR3ModernLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+    RT_NOREF(pSSM);
+
     if (pThisCC->pDrv)
-        pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
-}
-
-#endif /* IN_RING3 */
-
-/**
- * @interface_method_impl{VPCIIOCALLBACKS,pfnReady}
- *
- * This function is called when the driver becomes ready.
- */
-static DECLCALLBACK(void) vnetIoCb_Ready(PPDMDEVINS pDevIns)
-{
-    Log(("%s Driver became ready, waking up RX thread...\n", INSTANCE(PDMDEVINS_2_DATA(pDevIns, PVNETSTATE))));
-    vnetWakeupReceive(pDevIns);
-}
-
-
-/**
- * I/O port callbacks.
- */
-static const VPCIIOCALLBACKS g_IOCallbacks =
-{
-     vnetIoCb_GetHostFeatures,
-     vnetIoCb_GetHostMinimalFeatures,
-     vnetIoCb_SetHostFeatures,
-     vnetIoCb_GetConfig,
-     vnetIoCb_SetConfig,
-     vnetIoCb_Reset,
-     vnetIoCb_Ready,
-};
-
-
-/**
- * @callback_method_impl{FNIOMIOPORTNEWIN}
- */
-static DECLCALLBACK(VBOXSTRICTRC) vnetIOPortIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    RT_NOREF(pvUser);
-    return vpciIOPortIn(pDevIns, &pThis->VPCI, offPort, pu32, cb, &g_IOCallbacks);
-}
-
-
-/**
- * @callback_method_impl{FNIOMIOPORTNEWOUT}
- */
-static DECLCALLBACK(VBOXSTRICTRC) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
-{
-    PVNETSTATE   pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    RT_NOREF(pvUser);
-    return vpciIOPortOut(pDevIns, &pThis->VPCI, &pThisCC->VPCI, offPort, u32, cb, &g_IOCallbacks);
-}
-
+        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulticast));
+
+    /*
+     * Indicate link down to the guest OS that all network connections have
+     * been lost, unless we've been teleported here.
+     */
+    if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
+        virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) virtioNetR3ModernSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+    PCPDMDEVHLPR3  pHlp    = pDevIns->pHlpR3;
+
+    RT_NOREF(pThisCC);
+    Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
+
+    /* Store a dummy MAC address that would never be actually assigned to a NIC
+     * so that when load exec handler is called it can be easily determined
+     * whether saved state is modern or legacy. This works because original
+     * legacy code stored assigned NIC address as the first item of SSM state
+     */
+    RTMAC uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
+    pHlp->pfnSSMPutMem(pSSM, &uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8));
+
+    pHlp->pfnSSMPutU64(     pSSM, pThis->fNegotiatedFeatures);
+
+    pHlp->pfnSSMPutU16(     pSSM, pThis->cVirtqs);
+    pHlp->pfnSSMPutU16(     pSSM, pThis->cWorkers);
+
+    for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+        pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
+    /*
+
+     * Save device config area (accessed via MMIO)
+     */
+    pHlp->pfnSSMPutMem(     pSSM, pThis->virtioNetConfig.uMacAddress.au8,
+                            sizeof(pThis->virtioNetConfig.uMacAddress.au8));
+#if FEATURE_OFFERED(STATUS)
+    pHlp->pfnSSMPutU16(     pSSM, pThis->virtioNetConfig.uStatus);
+#else
+    /*
+     * Relevant values are lower bits. Forcing this to 0xffff let's loadExec know this
+     * feature was not enabled in saved state. VirtIO 1.0, 5.1.4
+     */
+    pHlp->pfnSSMPutU16(     pSSM, 0xffff);
+
+#endif
+#if FEATURE_OFFERED(MQ)
+    pHlp->pfnSSMPutU16(     pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
+#else
+    /*
+     * Legal values for max_virtqueue_pairs are 0x1 -> 0x8000 *. Forcing zero let's loadExec know this
+     * feature was not enabled in saved state. VirtIO 1.0, 5.1.4.1
+     */
+    pHlp->pfnSSMPutU16(     pSSM, 0);
+#endif
+
+    /* Save device-specific part */
+    pHlp->pfnSSMPutBool(    pSSM, pThis->fCableConnected);
+    pHlp->pfnSSMPutU8(      pSSM, pThis->fPromiscuous);
+    pHlp->pfnSSMPutU8(      pSSM, pThis->fAllMulticast);
+    pHlp->pfnSSMPutU8(      pSSM, pThis->fAllUnicast);
+    pHlp->pfnSSMPutU8(      pSSM, pThis->fNoMulticast);
+    pHlp->pfnSSMPutU8(      pSSM, pThis->fNoUnicast);
+    pHlp->pfnSSMPutU8(      pSSM, pThis->fNoBroadcast);
+
+    pHlp->pfnSSMPutU32(     pSSM, pThis->cMulticastFilterMacs);
+    pHlp->pfnSSMPutMem(     pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
+
+    pHlp->pfnSSMPutU32(     pSSM, pThis->cUnicastFilterMacs);
+    pHlp->pfnSSMPutMem(     pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
+
+    int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Call the virtio core to let it save its state.
+     */
+    return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
+}
+
+
+/*********************************************************************************************************************************
+*   Device interface.                                                                                                            *
+*********************************************************************************************************************************/
 
 #ifdef IN_RING3
 
 /**
- * Check if the device can receive data now.
- * This must be called before the pfnRecieve() method is called.
+ * Perform 16-bit 1's compliment checksum on provided packet in accordance with VirtIO specification,
+ * pertinent to VIRTIO_NET_F_CSUM feature, which 'offloads' the Checksum feature from the driver
+ * to save processor cycles, which is ironic in our case, where the controller device ('network card')
+ * is emulated on the virtualization host.
+ *
+ * @note See VirtIO 1.0 spec, 5.1.6.2 Packet Transmission
+ *
+ * @param pBuf          Pointer to r/w buffer with any portion to calculate checksum for
+ * @param cbSize        Number of bytes to checksum
+ * @param uStart        Where to start the checksum within the buffer
+ * @param uOffset       Offset past uStart point in the buffer to store checksum result
+ *
+ */
+DECLINLINE(void) virtioNetR3Calc16BitChecksum(uint8_t *pBuf, size_t cb, uint16_t uStart, uint16_t uOffset)
+{
+    AssertReturnVoid(uStart < cb);
+    AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cb);
+
+    uint32_t  chksum = 0;
+    uint16_t *pu = (uint16_t *)(pBuf + uStart);
+
+    cb -= uStart;
+    while (cb > 1)
+    {
+        chksum += *pu++;
+        cb -= 2;
+    }
+    if (cb)
+        chksum += *(uint8_t *)pu;
+    while (chksum >> 16)
+        chksum = (chksum >> 16) + (chksum & 0xFFFF);
+
+    /* Store 1's compliment of calculated sum */
+    *(uint16_t *)(pBuf + uStart + uOffset) = ~chksum;
+}
+
+/**
+ * Turns on/off the read status LED.
+ *
+ * @returns VBox status code.
+ * @param   pThis          Pointer to the device state structure.
+ * @param   fOn             New LED state.
+ */
+void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
+{
+    if (fOn)
+        pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
+    else
+        pThisR3->led.Actual.s.fReading = fOn;
+}
+
+/**
+ * Turns on/off the write status LED.
+ *
+ * @returns VBox status code.
+ * @param   pThis          Pointer to the device state structure.
+ * @param   fOn            New LED state.
+ */
+void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
+{
+    if (fOn)
+        pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
+    else
+        pThisR3->led.Actual.s.fWriting = fOn;
+}
+
+/**
+ * Check that the core is setup and ready and co-configured with guest virtio driver,
+ * and verifies that the VM is running.
+ *
+ * @returns true if VirtIO core and device are in a running and operational state
+ */
+DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
+{
+    if (RT_LIKELY(pThis->fVirtioReady))
+    {
+        VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
+        if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
+            return true;
+    }
+    return false;
+}
+
+/**
+ * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
+ * available. This must be called before the pfnRecieve() method is called.
  *
  * @remarks As a side effect this function enables queue notification
@@ -743,86 +1632,167 @@
  * @thread  RX
  */
-static int vnetR3CanReceive(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
-{
-    int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
-    AssertRCReturn(rc, rc);
-
-    LogFlow(("%s vnetR3CanReceive\n", INSTANCE(pThis)));
-    if (!(pThis->VPCI.uStatus & VPCI_STATUS_DRV_OK))
-        rc = VERR_NET_NO_BUFFER_SPACE;
-    else if (!vqueueIsReady(pThisCC->pRxQueue))
-        rc = VERR_NET_NO_BUFFER_SPACE;
-    else if (vqueueIsEmpty(pDevIns, pThisCC->pRxQueue))
-    {
-        vringSetNotification(pDevIns, &pThisCC->pRxQueue->VRing, true);
-        rc = VERR_NET_NO_BUFFER_SPACE;
-    }
+static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
+{
+    int rc = VERR_INVALID_STATE;
+    Log8Func(("[%s] ", pThis->szInst));
+    if (!virtioNetIsOperational(pThis, pDevIns))
+        Log8(("No Rx bufs available. (VirtIO core not ready)\n"));
+
+    else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
+        Log8(("[No Rx bufs available. (%s not enabled)\n", pRxVirtq->szName));
+
+    else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio,  pRxVirtq->uIdx))
+        Log8(("No Rx bufs available. (%s empty)\n", pRxVirtq->szName));
+
     else
     {
-        vringSetNotification(pDevIns, &pThisCC->pRxQueue->VRing, false);
+        Log8(("%s has %d empty guest bufs in avail ring\n", pRxVirtq->szName,
+                virtioCoreVirtqAvailBufCount(pDevIns,  &pThis->Virtio, pRxVirtq->uIdx)));
         rc = VINF_SUCCESS;
     }
-
-    LogFlow(("%s vnetR3CanReceive -> %Rrc\n", INSTANCE(pThis), rc));
-    vnetCsRxLeave(pThis);
+    virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
     return rc;
 }
 
 /**
+ * Find an Rx queue that has Rx packets in it, if *any* do.
+ *
+ * @todo When multiqueue (MQ) mode is fully supported and tested, some kind of round-robin
+ *       or randomization scheme should probably be incorporated here.
+ *
+ * @returns true if Rx pkts avail on queue and sets pRxVirtq to point to queue w/pkts found
+ * @thread  RX
+ *
+ */
+static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
+{
+    for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
+    {
+        PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
+        if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
+        {
+            if (pRxVirtq)
+                *pRxVirtq = pThisRxVirtq;
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
  * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
  */
-static DECLCALLBACK(int) vnetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
-{
-    PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkDown);
+static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
+{
+    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
     PPDMDEVINS   pDevIns = pThisCC->pDevIns;
-    PVNETSTATE   pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    LogFlow(("%s vnetR3NetworkDown_WaitReceiveAvail(cMillies=%u)\n", INSTANCE(pThis), cMillies));
-
-    int rc = vnetR3CanReceive(pDevIns, pThis, pThisCC);
-    if (RT_SUCCESS(rc))
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+    if (!virtioNetIsOperational(pThis, pDevIns))
+        return VERR_INTERRUPTED;
+
+    if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
+    {
+        Log10Func(("[%s] Rx bufs available, releasing waiter...\n", pThis->szInst));
         return VINF_SUCCESS;
-
-    if (cMillies == 0)
+    }
+    if (!timeoutMs)
         return VERR_NET_NO_BUFFER_SPACE;
 
-    rc = VERR_INTERRUPTED;
-    ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
+    LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
+
+    ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
     STAM_PROFILE_START(&pThis->StatRxOverflow, a);
 
-    VMSTATE enmVMState;
-    while (RT_LIKELY(   (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
-                     ||  enmVMState == VMSTATE_RUNNING_LS))
-    {
-        int rc2 = vnetR3CanReceive(pDevIns, pThis, pThisCC);
-        if (RT_SUCCESS(rc2))
+    do {
+        if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
         {
-            rc = VINF_SUCCESS;
+            Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
+            ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
+            return VINF_SUCCESS;
+        }
+        Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
+
+        int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
+
+        if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
+        {
+            LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
+
+            if (!virtioNetIsOperational(pThis, pDevIns))
+                break;
+
+            continue;
+        }
+        if (RT_FAILURE(rc)) {
+            LogFunc(("Waken due to failure %Rrc\n", rc));
+            RTThreadSleep(1);
+        }
+    } while (virtioNetIsOperational(pThis, pDevIns));
+
+    STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
+    ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
+
+    Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
+    return VERR_INTERRUPTED;
+}
+
+/**
+ * Sets up the GSO context according to the Virtio header.
+ *
+ * @param   pGso                The GSO context to setup.
+ * @param   pCtx                The context descriptor.
+ */
+DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
+{
+    pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
+
+    if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
+    {
+        AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
+        return NULL;
+    }
+    switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
+    {
+        case VIRTIONET_HDR_GSO_TCPV4:
+            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
+            pGso->cbHdrsSeg = pPktHdr->uHdrLen;
             break;
-        }
-        Log(("%s vnetR3NetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", INSTANCE(pThis), cMillies));
-        rc2 = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventMoreRxDescAvail, cMillies);
-        if (RT_FAILURE(rc2) && rc2 != VERR_TIMEOUT && rc2 != VERR_INTERRUPTED)
-            RTThreadSleep(1);
-    }
-    STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
-    ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
-
-    LogFlow(("%s vnetR3NetworkDown_WaitReceiveAvail -> %d\n", INSTANCE(pThis), rc));
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{PDMIBASE,pfnQueryInterface,
- * For VNETSTATECC::VPCI.IBase}
- */
-static DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
-{
-    PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, VPCI.IBase);
-
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
-
-    return vpciR3QueryInterface(&pThisCC->VPCI, pszIID);
+        case VIRTIONET_HDR_GSO_TCPV6:
+            pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
+            pGso->cbHdrsSeg = pPktHdr->uHdrLen;
+            break;
+        case VIRTIONET_HDR_GSO_UDP:
+            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
+            pGso->cbHdrsSeg = pPktHdr->uChksumStart;
+            break;
+        default:
+            return NULL;
+    }
+    if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
+        pGso->offHdr2  = pPktHdr->uChksumStart;
+    else
+    {
+        AssertMsgFailed(("GSO without checksum offloading!\n"));
+        return NULL;
+    }
+    pGso->offHdr1     = sizeof(RTNETETHERHDR);
+    pGso->cbHdrsTotal = pPktHdr->uHdrLen;
+    pGso->cbMaxSeg    = pPktHdr->uGsoSize;
+    /* Mark GSO frames with zero MSS as PDMNETWORKGSOTYPE_INVALID, so they will be ignored by send. */
+    if (pPktHdr->uGsoType != VIRTIONET_HDR_GSO_NONE && pPktHdr->uGsoSize == 0)
+        pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
+    return pGso;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+    PVIRTIONETCC    pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
+    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
+    memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
+    return VINF_SUCCESS;
 }
 
@@ -833,5 +1803,5 @@
  * @param   pvBuf           The ethernet packet.
  */
-DECLINLINE(bool) vnetR3IsBroadcast(const void *pvBuf)
+DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
 {
     static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
@@ -846,5 +1816,5 @@
  * @param   pvBuf           The ethernet packet.
  */
-DECLINLINE(bool) vnetR3IsMulticast(const void *pvBuf)
+DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
 {
     return (*(char*)pvBuf) & 1;
@@ -856,40 +1826,219 @@
  * @returns true if packet is intended for this node.
  * @param   pThis          Pointer to the state structure.
- * @param   pvBuf           The ethernet packet.
- * @param   cb              Number of bytes available in the packet.
- */
-static bool vnetR3AddressFilter(PVNETSTATE pThis, const void *pvBuf, size_t cb)
-{
-    if (pThis->fPromiscuous)
+ * @param   pvBuf          The ethernet packet.
+ * @param   cb             Number of bytes available in the packet.
+ */
+static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
+{
+
+RT_NOREF(cb);
+
+#ifdef LOG_ENABLED
+    if (LogIs11Enabled())
+    {
+        char *pszType;
+        if (virtioNetR3IsMulticast(pvBuf))
+            pszType = (char *)"mcast";
+        else if (virtioNetR3IsBroadcast(pvBuf))
+            pszType = (char *)"bcast";
+        else
+            pszType = (char *)"ucast";
+
+        LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
+                 pThis->virtioNetConfig.uMacAddress.au8,
+                 pThis->fPromiscuous ? " promisc" : "",
+                 pThis->fAllMulticast ? " all-mcast" : "",
+                 pvBuf,  pszType));
+    }
+#endif
+
+    if (pThis->fPromiscuous) {
+        Log11(("\n"));
         return true;
+    }
 
     /* Ignore everything outside of our VLANs */
-    uint16_t *u16Ptr = (uint16_t*)pvBuf;
+    uint16_t *uPtr = (uint16_t *)pvBuf;
+
     /* Compare TPID with VLAN Ether Type */
-    if (   u16Ptr[6] == RT_H2BE_U16(0x8100)
-        && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(u16Ptr[7]) & 0xFFF))
-    {
-        Log4(("%s vnetR3AddressFilter: not our VLAN, returning false\n", INSTANCE(pThis)));
+    if (   uPtr[6] == RT_H2BE_U16(0x8100)
+        && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
+    {
+        Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
         return false;
     }
 
-    if (vnetR3IsBroadcast(pvBuf))
+    if (virtioNetR3IsBroadcast(pvBuf))
+    {
+        Log11(("acpt (bcast)\n"));
+#ifdef LOG_ENABLED
+        if (LogIs12Enabled())
+            virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
         return true;
-
-    if (pThis->fAllMulti && vnetR3IsMulticast(pvBuf))
+    }
+    if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
+    {
+        Log11(("acpt (all-mcast)\n"));
+#ifdef LOG_ENABLED
+        if (LogIs12Enabled())
+            virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
         return true;
-
-    if (!memcmp(pThis->config.mac.au8, pvBuf, sizeof(RTMAC)))
+    }
+
+    if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
+    {
+        Log11(("acpt (to-node)\n"));
+#ifdef LOG_ENABLED
+        if (LogIs12Enabled())
+            virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
         return true;
-    Log4(("%s vnetR3AddressFilter: %RTmac (conf) != %RTmac (dest)\n", INSTANCE(pThis), pThis->config.mac.au8, pvBuf));
-
-    for (unsigned i = 0; i < pThis->cMacFilterEntries; i++)
-        if (!memcmp(&pThis->aMacFilter[i], pvBuf, sizeof(RTMAC)))
+    }
+
+    for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
+    {
+        if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
+        {
+            Log11(("acpt (mcast whitelist)\n"));
+#ifdef LOG_ENABLED
+            if (LogIs12Enabled())
+                virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
             return true;
-
-    Log2(("%s vnetR3AddressFilter: failed all tests, returning false, packet dump follows:\n", INSTANCE(pThis)));
-    vnetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+        }
+    }
+
+    for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
+        if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
+        {
+            Log11(("acpt (ucast whitelist)\n"));
+            return true;
+        }
+#ifdef LOG_ENABLED
+    if (LogIs11Enabled())
+        Log(("... reject\n"));
+#endif
 
     return false;
+}
+
+
+/**
+ * This handles the case where Rx packet must be transfered to guest driver via multiple buffers using
+ * copy tactics slower than preferred method using a single virtq buf. Yet this is an available option
+ * for guests. Although cited in the spec it's to accomodate guest that perhaps have memory constraints
+ * wherein guest may benefit from smaller buffers (see MRG_RXBUF feature), in practice it is seen
+ * that without MRG_RXBUF the linux guest enqueues 'huge' multi-segment buffers so that the largest
+ * conceivable Rx packet can be contained in a single buffer, where for most transactions most of that
+ * memory will be unfilled, so it is typically both wasteful and *slower* to avoid MRG_RXBUF.
+ *
+ * As an optimization, this multi-buffer copy is only used when:
+ *
+ *     A. Guest has negotiated MRG_RXBUF
+ *     B. Next packet in the Rx avail queue isn't big enough to contain Rx pkt hdr+data.
+ *
+ * Architecture is defined in VirtIO 1.1 5.1.6 (Device Operations), which has improved
+ * wording over the VirtIO 1.0 specification, but, as an implementation note, there is one
+ * ambiguity that needs clarification:
+ *
+ *    The VirtIO 1.1, 5.1.6.4 explains something in a potentially misleading way. And note,
+ *    the VirtIO spec makes a document-wide assertion that the distinction between
+ *    "SHOULD" and "MUST" is to be taken quite literally.
+ *
+ *    The confusion is that VirtIO 1.1, 5.1.6.3.1 essentially says guest driver "SHOULD" populate
+ *    Rx queue with buffers large enough to accomodate full pkt hdr + data. That's a grammatical
+ *    error (dangling participle).
+ *
+ *    In practice we MUST assume "SHOULD" strictly applies to the word *populate*, -not- to buffer
+ *    size, because ultimately buffer minimum size is predicated on configuration parameters,
+ *    specifically, when MRG_RXBUF feature is disabled, the driver *MUST* provide Rx bufs
+ *    (if and when it can provide them), that are *large enough* to hold pkt hdr + payload.
+ *
+ *    Therefore, proper interpretation of 5.1.6.3.1 is, the guest *should* (ideally) keep Rx virtq
+ *    populated with appropriately sized buffers to *prevent starvation* (i.e. starvation may be
+ *    unavoidable thus can't be prohibited). As it would be a ludicrous to presume 5.1.6.3.1 is
+ *    giving guests leeway to violate MRG_RXBUF feature buf size constraints.
+ *
+ * @param pDevIns               PDM instance
+ * @param pThis                 Device instance
+ * @param pvBuf                 Pointer to incoming GSO Rx data from downstream device
+ * @param cb                    Amount of data given
+ * @param rxPktHdr              Rx pkt Header that's been massaged into VirtIO semantics
+ * @param pRxVirtq              Pointer to Rx virtq
+ * @param pVirtqBuf             Initial virtq buffer to start copying Rx hdr/pkt to guest into
+ *
+ */
+static int virtioNetR3RxPktMultibufXfer(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint8_t *pvPktBuf, size_t cb,
+                                       PVIRTIONETPKTHDR pRxPktHdr, PVIRTIONETVIRTQ pRxVirtq, PVIRTQBUF pVirtqBuf)
+{
+
+    size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
+    size_t cbPktHdr = pThis->cbPktHdr;
+
+    AssertMsgReturn(cbBufRemaining >= pThis->cbPktHdr,
+                    ("guest-provided Rx buf not large enough to store pkt hdr"), VERR_INTERNAL_ERROR);
+
+    Log7Func(("  Sending packet header to guest...\n"));
+
+    /* Copy packet header to rx buf provided by caller. */
+    size_t cbHdrEnqueued = pVirtqBuf->cbPhysReturn == cbPktHdr ? cbPktHdr : 0;
+    virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, cbHdrEnqueued);
+
+    /* Cache address of uNumBuffers field of pkthdr to update ex post facto */
+    RTGCPHYS GCPhysNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
+    uint16_t cVirtqBufsUsed = 0;
+    cbBufRemaining -= cbPktHdr;
+    /*
+     * Copy packet to guest using as many buffers as necessary, tracking and handling whether
+     * the buf containing the packet header was already written to the Rx queue's used buffer ring.
+     */
+    uint64_t uPktOffset = 0;
+    while(uPktOffset < cb)
+    {
+        Log7Func(("  Sending packet data (in buffer #%d) to guest...\n", cVirtqBufsUsed));
+        size_t cbBounded = RT_MIN(cbBufRemaining, cb - uPktOffset);
+        (void) virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbBounded,
+                                    pvPktBuf + uPktOffset, pVirtqBuf, cbBounded + (cbPktHdr - cbHdrEnqueued) /* cbEnqueue */);
+        ++cVirtqBufsUsed;
+        cbBufRemaining -= cbBounded;
+        uPktOffset += cbBounded;
+        if (uPktOffset < cb)
+        {
+            cbHdrEnqueued = cbPktHdr;
+#ifdef VIRTIO_VBUF_ON_STACK
+            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
+#else /* !VIRTIO_VBUF_ON_STACK */
+            virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+
+            AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
+
+#ifdef VIRTIO_VBUF_ON_STACK
+            AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+                            ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
+                            VERR_INTERNAL_ERROR);
+#else /* !VIRTIO_VBUF_ON_STACK */
+            AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+                                ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
+                                virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
+                                VERR_INTERNAL_ERROR);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+            cbBufRemaining = pVirtqBuf->cbPhysReturn;
+        }
+    }
+
+    /* Fix-up pkthdr (in guest phys. memory) with number of buffers (descriptors) that were processed */
+    int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysNumBuffers, &cVirtqBufsUsed, sizeof(cVirtqBufsUsed));
+    AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
+
+#ifndef VIRTIO_VBUF_ON_STACK
+    virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+    virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
+    Log7(("\n"));
+    return rc;
 }
 
@@ -905,133 +2054,587 @@
  * @param   pvBuf           The available data.
  * @param   cb              Number of bytes available in the buffer.
+ * @param   pGso            Pointer to Global Segmentation Offload structure
+ * @param   pRxVirtq        Pointer to Rx virtqueue
  * @thread  RX
  */
-static int vnetR3HandleRxPacket(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC,
-                                const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso)
-{
-    VNETHDRMRX  Hdr;
-    unsigned    cbHdr;
-    RTGCPHYS    addrHdrMrx = 0;
-
+
+static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, const void *pvBuf, size_t cb,
+                                       PVIRTIONETPKTHDR pRxPktHdr, uint8_t cbPktHdr, PVIRTIONETVIRTQ pRxVirtq)
+{
+    RT_NOREF(pThisCC);
+#ifdef VIRTIO_VBUF_ON_STACK
+    VIRTQBUF_T VirtqBuf;
+
+    VirtqBuf.u32Magic  = VIRTQBUF_MAGIC;
+    VirtqBuf.cRefs     = 1;
+
+    PVIRTQBUF pVirtqBuf = &VirtqBuf;
+    int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
+#else /* !VIRTIO_VBUF_ON_STACK */
+    PVIRTQBUF pVirtqBuf;
+    int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+
+    AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
+
+#ifdef VIRTIO_VBUF_ON_STACK
+    AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+                    ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
+                    VERR_INTERNAL_ERROR);
+#else /* !VIRTIO_VBUF_ON_STACK */
+    AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+                        ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
+                        virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
+                        VERR_INTERNAL_ERROR);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+    /*
+     * Try to do fast (e.g. single-buffer) copy to guest, even if MRG_RXBUF feature is enabled
+     */
+    STAM_PROFILE_START(&pThis->StatReceiveStore, a);
+    if (RT_LIKELY(FEATURE_DISABLED(MRG_RXBUF))
+        || RT_LIKELY(pVirtqBuf->cbPhysReturn > cb + cbPktHdr))
+    {
+        Log7Func(("Send Rx packet header and data to guest (single-buffer copy)...\n"));
+        pRxPktHdr->uNumBuffers = 1;
+        rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr,  pRxPktHdr, pVirtqBuf, 0  /* cbEnqueue */);
+        if (rc == VINF_SUCCESS)
+            rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cb, pvBuf, pVirtqBuf, cbPktHdr + cb /* cbEnqueue */);
+#ifndef VIRTIO_VBUF_ON_STACK
+        virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+        virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
+        AssertMsgReturn(rc == VINF_SUCCESS, ("%Rrc\n", rc), rc);
+    }
+    else
+    {
+        Log7Func(("Send Rx pkt to guest (merged-buffer copy [MRG_RXBUF feature])...\n"));
+        rc = virtioNetR3RxPktMultibufXfer(pDevIns, pThis, (uint8_t *)pvBuf, cb, pRxPktHdr, pRxVirtq, pVirtqBuf);
+        return rc;
+    }
+    STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(
+                                 PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso)
+{
+    PVIRTIONETCC    pThisCC  = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
+    PPDMDEVINS      pDevIns  = pThisCC->pDevIns;
+    PVIRTIONET      pThis    = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    VIRTIONETPKTHDR rxPktHdr = { 0, VIRTIONET_HDR_GSO_NONE, 0, 0, 0, 0, 0 };
+
+    if (!pThis->fVirtioReady)
+    {
+        LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
+        return VERR_INTERRUPTED;
+    }
+    /*
+     * If GSO (Global Segment Offloading) was received from downstream PDM network device, massage the
+     * PDM-provided GSO parameters into VirtIO semantics, which get passed to guest virtio-net via
+     * Rx pkt header.  See VirtIO 1.1, 5.1.6 Device Operation for more information.
+     */
     if (pGso)
     {
-        Log2(("%s vnetR3HandleRxPacket: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
-              INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
-        Hdr.Hdr.u8Flags = VNETHDR_F_NEEDS_CSUM;
+        LogFunc(("[%s] (%RTmac) \n", pThis->szInst, pvBuf));
+
+        rxPktHdr.uFlags        = VIRTIONET_HDR_F_NEEDS_CSUM;
+        rxPktHdr.uHdrLen       = pGso->cbHdrsTotal;
+        rxPktHdr.uGsoSize      = pGso->cbMaxSeg;
+        rxPktHdr.uChksumStart  = pGso->offHdr2;
+
         switch (pGso->u8Type)
         {
             case PDMNETWORKGSOTYPE_IPV4_TCP:
-                Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV4;
-                Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
+                rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
+                rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
                 break;
             case PDMNETWORKGSOTYPE_IPV6_TCP:
-                Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV6;
-                Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
+                rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
+                rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
                 break;
             case PDMNETWORKGSOTYPE_IPV4_UDP:
-                Hdr.Hdr.u8GSOType = VNETHDR_GSO_UDP;
-                Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
+                rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
+                rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
                 break;
             default:
+                LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
+                return VERR_NOT_SUPPORTED;
+        }
+        STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
+        Log2Func(("[%s] gso type=%#x, cbHdrsTotal=%u cbHdrsSeg=%u mss=%u offHdr1=%#x offHdr2=%#x\n",
+                        pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
+                        pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
+    }
+
+    /*
+     * Find a virtq with Rx bufs on avail ring, if any, and copy the packet to the guest's Rx buffer.
+     * @todo pk: PROBABLY NOT A SOPHISTICATED ENOUGH QUEUE SELECTION ALGORTITH FOR OPTIMAL MQ (FEATURE) SUPPORT
+     */
+    for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
+    {
+        PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
+        if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
+        {
+            int rc = VINF_SUCCESS;
+            STAM_PROFILE_START(&pThis->StatReceive, a);
+            virtioNetR3SetReadLed(pThisCC, true);
+            if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
+            {
+                /* rxPktHdr is local stack variable that should not go out of scope in this use */
+                rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pThisCC, pvBuf, cb, &rxPktHdr, pThis->cbPktHdr, pRxVirtq);
+                STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
+            }
+            virtioNetR3SetReadLed(pThisCC, false);
+            STAM_PROFILE_STOP(&pThis->StatReceive, a);
+            return rc;
+        }
+    }
+    return VERR_INTERRUPTED;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+
+#ifdef LOG_ENABLED
+    PVIRTIONETCC    pThisCC  = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
+    PPDMDEVINS      pDevIns  = pThisCC->pDevIns;
+    PVIRTIONET      pThis    = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    LogFunc(("[%s] (%RTmac)\n", pThis->szInst, pvBuf));
+#endif
+
+    return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's Rx packet receive filtering.
+ * See VirtIO 1.0, 5.1.6.5.1
+ *
+ * @param pThis         virtio-net instance
+ * @param pCtrlPktHdr   Control packet header (which includes command parameters)
+ * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
+                                 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+
+#define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
+
+    LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
+    switch(pCtrlPktHdr->uCmd)
+    {
+      case VIRTIONET_CTRL_RX_PROMISC:
+        break;
+      case VIRTIONET_CTRL_RX_ALLMULTI:
+        break;
+      case VIRTIONET_CTRL_RX_ALLUNI:
+        /* fallthrough */
+      case VIRTIONET_CTRL_RX_NOMULTI:
+        /* fallthrough */
+      case VIRTIONET_CTRL_RX_NOUNI:
+        /* fallthrough */
+      case VIRTIONET_CTRL_RX_NOBCAST:
+        AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
+                        ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
+                        VIRTIONET_ERROR);
+        /* fall out */
+    }
+
+    uint8_t fOn, fPromiscChanged = false;
+    virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
+
+    switch(pCtrlPktHdr->uCmd)
+    {
+      case VIRTIONET_CTRL_RX_PROMISC:
+        pThis->fPromiscuous = RT_BOOL(fOn);
+        fPromiscChanged = true;
+        LOG_VIRTIONET_FLAG(fPromiscuous);
+        break;
+      case VIRTIONET_CTRL_RX_ALLMULTI:
+        pThis->fAllMulticast = RT_BOOL(fOn);
+        fPromiscChanged = true;
+        LOG_VIRTIONET_FLAG(fAllMulticast);
+        break;
+      case VIRTIONET_CTRL_RX_ALLUNI:
+        pThis->fAllUnicast = RT_BOOL(fOn);
+        LOG_VIRTIONET_FLAG(fAllUnicast);
+        break;
+      case VIRTIONET_CTRL_RX_NOMULTI:
+        pThis->fNoMulticast = RT_BOOL(fOn);
+        LOG_VIRTIONET_FLAG(fNoMulticast);
+        break;
+      case VIRTIONET_CTRL_RX_NOUNI:
+        pThis->fNoUnicast = RT_BOOL(fOn);
+        LOG_VIRTIONET_FLAG(fNoUnicast);
+        break;
+      case VIRTIONET_CTRL_RX_NOBCAST:
+        pThis->fNoBroadcast = RT_BOOL(fOn);
+        LOG_VIRTIONET_FLAG(fNoBroadcast);
+        break;
+    }
+
+    if (pThisCC->pDrv && fPromiscChanged)
+        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
+
+    return VIRTIONET_OK;
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MAC filter tables
+ * See VirtIO 1.0, 5.1.6.5.2
+ *
+ * @param pThis         virtio-net instance
+ * @param pCtrlPktHdr   Control packet header (which includes command parameters)
+ * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+    LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
+
+
+    AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
+                   ("insufficient descriptor space for ctrl pkt hdr"),
+                   VIRTIONET_ERROR);
+
+    size_t cbRemaining = pVirtqBuf->cbPhysSend;
+    switch(pCtrlPktHdr->uCmd)
+    {
+        case VIRTIONET_CTRL_MAC_ADDR_SET:
+        {
+            /* Set default Rx filter MAC */
+            AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
+                            ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
+
+            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(RTMAC));
+            break;
+        }
+        case VIRTIONET_CTRL_MAC_TABLE_SET:
+        {
+            VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
+
+            /* Load unicast MAC filter table */
+            AssertMsgReturn(cbRemaining >= sizeof(cMacs),
+                           ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+            /* Fetch count of unicast filter MACs from guest buffer */
+            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
+            cbRemaining -= sizeof(cMacs);
+
+            Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
+
+            if (cMacs)
+            {
+                uint32_t cbMacs = cMacs * sizeof(RTMAC);
+
+                AssertMsgReturn(cbMacs <= sizeof(pThis->aMacUnicastFilter)  / sizeof(RTMAC),
+                                ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
+
+                AssertMsgReturn(cbRemaining >= cbMacs,
+                                ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+
+                /* Fetch unicast table contents from guest buffer */
+                virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
+                cbRemaining -= cbMacs;
+            }
+            pThis->cUnicastFilterMacs = cMacs;
+
+            /* Load multicast MAC filter table */
+            AssertMsgReturn(cbRemaining >= sizeof(cMacs),
+                            ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+            /* Fetch count of multicast filter MACs from guest buffer */
+            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
+            cbRemaining -= sizeof(cMacs);
+
+            Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
+
+            if (cMacs)
+            {
+                uint32_t cbMacs = cMacs * sizeof(RTMAC);
+
+                AssertMsgReturn(cbMacs <= sizeof(pThis->aMacMulticastFilter)  / sizeof(RTMAC),
+                                ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
+
+                AssertMsgReturn(cbRemaining >= cbMacs,
+                                ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+                /* Fetch multicast table contents from guest buffer */
+                virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
+                cbRemaining -= cbMacs;
+            }
+            pThis->cMulticastFilterMacs = cMacs;
+
+#ifdef LOG_ENABLED
+            LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
+            for(unsigned i = 0; i < pThis->cUnicastFilterMacs; i++)
+                LogFunc(("         %RTmac\n", &pThis->aMacUnicastFilter[i]));
+
+            LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
+            for(unsigned i = 0; i < pThis->cMulticastFilterMacs; i++)
+                LogFunc(("         %RTmac\n", &pThis->aMacMulticastFilter[i]));
+#endif
+            break;
+        }
+        default:
+            LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
+            return VIRTIONET_ERROR;
+    }
+    return VIRTIONET_OK;
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MQ (multiqueue) operations.
+ * See VirtIO 1.0, 5.1.6.5.5
+ *
+ * @param pThis         virtio-net instance
+ * @param pCtrlPktHdr   Control packet header (which includes command parameters)
+ * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+    LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
+
+    uint16_t cVirtqPairs;
+    switch(pCtrlPktHdr->uCmd)
+    {
+        case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
+        {
+            size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
+
+            AssertMsgReturn(cbRemaining > sizeof(cVirtqPairs),
+                ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
+
+            /* Fetch number of virtq pairs from guest buffer */
+            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
+
+            AssertMsgReturn(cVirtqPairs > VIRTIONET_MAX_QPAIRS,
+                ("[%s] Guest CTRL MQ virtq pair count out of range [%d])\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
+
+            LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
+            pThis->cVirtqPairs = cVirtqPairs;
+            break;
+        }
+        default:
+            LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
+            return VIRTIONET_ERROR;
+    }
+
+    /*
+     * The MQ control function is invoked by the guest in an RPC like manner to change
+     * the Rx/Tx queue pair count. If the new value exceeds the number of queues
+     * (and associated workers) already initialized initialize only the new queues and
+     * respective workers.
+     */
+    if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
+    {
+        virtioNetR3SetVirtqNames(pThis, virtioCoreIsLegacyMode(&pThis->Virtio));
+        int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
+        if (RT_FAILURE(rc))
+        {
+            LogRelFunc(("Failed to create worker threads\n"));
+            return VIRTIONET_ERROR;
+        }
+    }
+    return VIRTIONET_OK;
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's VLAN filtering.
+ * See VirtIO 1.0, 5.1.6.5.3
+ *
+ * @param pThis         virtio-net instance
+ * @param pCtrlPktHdr   Control packet header (which includes command parameters)
+ * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+    LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
+
+    uint16_t uVlanId;
+    size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
+
+    AssertMsgReturn(cbRemaining > sizeof(uVlanId),
+        ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
+
+    /* Fetch VLAN ID from guest buffer */
+    virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
+
+    AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
+        ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
+
+    LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
+
+    switch (pCtrlPktHdr->uCmd)
+    {
+        case VIRTIONET_CTRL_VLAN_ADD:
+            ASMBitSet(pThis->aVlanFilter, uVlanId);
+            break;
+        case VIRTIONET_CTRL_VLAN_DEL:
+            ASMBitClear(pThis->aVlanFilter, uVlanId);
+            break;
+        default:
+            LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
+            return VIRTIONET_ERROR;
+    }
+    return VIRTIONET_OK;
+}
+
+/**
+ * Processes control command from guest.
+ * See VirtIO 1.0 spec, 5.1.6 "Device Operation" and 5.1.6.5 "Control Virtqueue".
+ *
+ * The control command is contained in a virtio buffer pulled from the virtio-net defined control queue (ctrlq).
+ * Command type is parsed is dispatched to a command-specific device-configuration handler function (e.g. RX, MAC, VLAN, MQ
+ * and ANNOUNCE).
+ *
+ * This function handles all parts of the host-side of the ctrlq round-trip buffer processing.
+ *
+ * Invoked by worker for virtio-net control queue to process a queued control command buffer.
+ *
+ * @param pDevIns       PDM device instance
+ * @param pThis         virtio-net device instance
+ * @param pThisCC       virtio-net device instance
+ * @param pVirtqBuf     pointer to buffer pulled from virtq (input to this function)
+ */
+static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
+                            PVIRTQBUF pVirtqBuf)
+{
+    if (!(pThis->fNegotiatedFeatures & VIRTIONET_F_CTRL_VQ))
+        LogFunc(("[%s] WARNING: Guest using CTRL queue w/o negotiating VIRTIONET_F_CTRL_VQ feature\n", pThis->szInst));
+
+    LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
+
+    if (pVirtqBuf->cbPhysSend < 2)
+    {
+        LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
+        return;
+    }
+    else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
+    {
+        LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
+        return;
+    }
+
+    /*
+     * Allocate buffer and read in the control command
+     */
+    AssertMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(VIRTIONET_CTRL_HDR_T),
+                        ("DESC chain too small for CTRL pkt header"));
+
+    VIRTIONET_CTRL_HDR_T CtrlPktHdr; RT_ZERO(CtrlPktHdr);
+    virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &CtrlPktHdr,
+                              RT_MIN(pVirtqBuf->cbPhysSend, sizeof(CtrlPktHdr)));
+
+    Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, CtrlPktHdr.uClass, CtrlPktHdr.uCmd));
+
+    uint8_t uAck;
+    switch (CtrlPktHdr.uClass)
+    {
+        case VIRTIONET_CTRL_RX:
+            uAck = virtioNetR3CtrlRx(pThis, pThisCC, &CtrlPktHdr, pVirtqBuf);
+            break;
+        case VIRTIONET_CTRL_MAC:
+            uAck = virtioNetR3CtrlMac(pThis, &CtrlPktHdr, pVirtqBuf);
+            break;
+        case VIRTIONET_CTRL_VLAN:
+            uAck = virtioNetR3CtrlVlan(pThis, &CtrlPktHdr, pVirtqBuf);
+            break;
+        case VIRTIONET_CTRL_MQ:
+            uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, &CtrlPktHdr, pVirtqBuf);
+            break;
+        case VIRTIONET_CTRL_ANNOUNCE:
+            uAck = VIRTIONET_OK;
+            if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
+            {
+                LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
+                         "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
+                break;
+            }
+            if (CtrlPktHdr.uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
+            {
+                LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
+                break;
+            }
+#if FEATURE_OFFERED(STATUS)
+            pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
+#endif
+            Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
+            break;
+        default:
+            LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", CtrlPktHdr.uClass));
+            uAck = VIRTIONET_ERROR;
+    }
+
+    /* Return CTRL packet Ack byte (result code) to guest driver */
+    RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
+    RTSGBUF SgBuf;
+
+    RTSgBufInit(&SgBuf, aStaticSegs, RT_ELEMENTS(aStaticSegs));
+    virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, &SgBuf, pVirtqBuf, true /* fFence */);
+    virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
+
+    LogFunc(("%s Finished processing CTRL command with status %s\n",
+             pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
+}
+
+/**
+ * Reads virtio-net pkt header from provided Phy. addr of virtio descriptor chain
+ * (e.g. S/G segment from guest-driver provided buffer pulled from Tx virtq)
+ * Verifies state and supported modes, sets TCP header size.
+ *
+ * @param pVirtio      VirtIO core instance data
+ * @param pThis        virtio-net instance
+ * @param pDevIns      PDM device instance
+ * @param GCPhys       Phys. Address from where to read virtio-net pkt header
+ * @param pPktHdr      Where to store read Tx pkt hdr (virtio pkt hdr size is determined from instance configuration)
+ * @param cbFrame      Total pkt frame size to inform bounds check
+ */
+static int virtioNetR3ReadVirtioTxPktHdr(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
+{
+    int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
+          pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
+          pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
+
+    if (pPktHdr->uGsoType)
+    {
+        /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
+        AssertMsgReturn(    RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
+                         && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
+                         ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
+
+        uint32_t uTcpHdrSize;
+        switch (pPktHdr->uGsoType)
+        {
+            case VIRTIONET_HDR_GSO_TCPV4:
+            case VIRTIONET_HDR_GSO_TCPV6:
+                uTcpHdrSize = sizeof(RTNETTCP);
+                break;
+            case VIRTIONET_HDR_GSO_UDP:
+                uTcpHdrSize = 0;
+                break;
+            default:
+                LogFunc(("Bad GSO type in packet header\n"));
                 return VERR_INVALID_PARAMETER;
         }
-        Hdr.Hdr.u16HdrLen    = pGso->cbHdrsTotal;
-        Hdr.Hdr.u16GSOSize   = pGso->cbMaxSeg;
-        Hdr.Hdr.u16CSumStart = pGso->offHdr2;
-        Hdr.u16NumBufs       = 0;
-        STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
-    }
-    else
-    {
-        Hdr.Hdr.u8Flags       = 0;
-        Hdr.Hdr.u8GSOType     = VNETHDR_GSO_NONE;
-        Hdr.Hdr.u16HdrLen     = 0;
-        Hdr.Hdr.u16GSOSize    = 0;
-        Hdr.Hdr.u16CSumStart  = 0;
-        Hdr.Hdr.u16CSumOffset = 0;
-        Hdr.u16NumBufs        = 0;
-    }
-
-    if (vnetR3MergeableRxBuffers(pThis))
-        cbHdr = sizeof(VNETHDRMRX);
-    else
-        cbHdr = sizeof(VNETHDR);
-
-    vnetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
-
-    unsigned int uOffset = 0;
-    unsigned int nElem;
-    for (nElem = 0; uOffset < cb; nElem++)
-    {
-        VQUEUEELEM elem;
-        unsigned int nSeg = 0, uElemSize = 0, cbReserved = 0;
-
-        if (!vqueueGet(pDevIns, &pThis->VPCI, pThisCC->pRxQueue, &elem))
-        {
-            /*
-             * @todo: It is possible to run out of RX buffers if only a few
-             * were added and we received a big packet.
-             */
-            Log(("%s vnetR3HandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pThis)));
-            return VERR_INTERNAL_ERROR;
-        }
-
-        if (elem.cIn < 1)
-        {
-            Log(("%s vnetR3HandleRxPacket: No writable descriptors in receive queue!\n", INSTANCE(pThis)));
-            return VERR_INTERNAL_ERROR;
-        }
-
-        if (nElem == 0)
-        {
-            if (vnetR3MergeableRxBuffers(pThis))
-            {
-                addrHdrMrx = elem.aSegsIn[nSeg].addr;
-                cbReserved = cbHdr;
-            }
-            else
-            {
-                /* The very first segment of the very first element gets the header. */
-                if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
-                {
-                    Log(("%s vnetR3HandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pThis)));
-                    return VERR_INTERNAL_ERROR;
-                }
-                elem.aSegsIn[nSeg++].pv = &Hdr;
-            }
-            uElemSize += cbHdr;
-        }
-        while (nSeg < elem.cIn && uOffset < cb)
-        {
-            unsigned int uSize = (unsigned int)RT_MIN(elem.aSegsIn[nSeg].cb - (nSeg?0:cbReserved),
-                                                      cb - uOffset);
-            elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
-            uOffset += uSize;
-            uElemSize += uSize;
-        }
-        STAM_PROFILE_START(&pThis->StatReceiveStore, a);
-        vqueuePut(pDevIns, &pThis->VPCI, pThisCC->pRxQueue, &elem, uElemSize, cbReserved);
-        STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
-        if (!vnetR3MergeableRxBuffers(pThis))
-            break;
-        cbReserved = 0;
-    }
-    if (vnetR3MergeableRxBuffers(pThis))
-    {
-        Hdr.u16NumBufs = nElem;
-        int rc = PDMDevHlpPCIPhysWrite(pDevIns, addrHdrMrx,
-                                       &Hdr, sizeof(Hdr));
-        if (RT_FAILURE(rc))
-        {
-            Log(("%s vnetR3HandleRxPacket: Failed to write merged RX buf header: %Rrc\n", INSTANCE(pThis), rc));
-            return rc;
-        }
-    }
-    vqueueSync(pDevIns, &pThis->VPCI, pThisCC->pRxQueue);
-    if (uOffset < cb)
-    {
-        Log(("%s vnetR3HandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n", INSTANCE(pThis), cb));
-        return VERR_TOO_MUCH_DATA;
-    }
+        /* Header + MSS must not exceed the packet size. */
+        AssertMsgReturn(RT_LIKELY(uTcpHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
+                    ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
+    }
+
+    AssertMsgReturn( !(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
+                    || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
+                 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
+                 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
+                 VERR_BUFFER_OVERFLOW);
 
     return VINF_SUCCESS;
@@ -1039,317 +2642,55 @@
 
 /**
- * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
- */
-static DECLCALLBACK(int) vnetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf,
-                                                      size_t cb, PCPDMNETWORKGSO pGso)
-{
-    PVNETSTATECC    pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkDown);
-    PPDMDEVINS      pDevIns = pThisCC->pDevIns;
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-
+ * Transmits single GSO frame via PDM framework to downstream PDM device, to emit from virtual NIC.
+ *
+ * This does final prep of GSO parameters including checksum calculation if configured
+ * (e.g. if VIRTIONET_HDR_F_NEEDS_CSUM flag is set).
+ *
+ * @param pThis         virtio-net instance
+ * @param pThisCC       virtio-net instance
+ * @param pSgBuf        PDM S/G buffer containing pkt and hdr to transmit
+ * @param pGso          GSO parameters used for the packet
+ * @param pPktHdr       virtio-net pkt header to adapt to PDM semantics
+ */
+static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
+                               PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
+{
+
+    virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
     if (pGso)
     {
-        uint32_t uFeatures = pThis->VPCI.uGuestFeatures;
-
+        /* Some guests (RHEL) may report HdrLen excluding transport layer header!
+         * Thus cannot use cdHdrs provided by the guest because of different ways
+         * it gets filled out by different versions of kernels. */
+        Log4Func(("%s HdrLen before adjustment %d.\n", pThis->szInst, pGso->cbHdrsTotal));
         switch (pGso->u8Type)
         {
             case PDMNETWORKGSOTYPE_IPV4_TCP:
-                uFeatures &= VNET_F_GUEST_TSO4;
-                break;
             case PDMNETWORKGSOTYPE_IPV6_TCP:
-                uFeatures &= VNET_F_GUEST_TSO6;
+                pGso->cbHdrsTotal = pPktHdr->uChksumStart +
+                    ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
+                AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
+                    ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
+                pGso->cbHdrsSeg   = pGso->cbHdrsTotal;
                 break;
             case PDMNETWORKGSOTYPE_IPV4_UDP:
-            case PDMNETWORKGSOTYPE_IPV6_UDP:
-                uFeatures &= VNET_F_GUEST_UFO;
+                pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
+                pGso->cbHdrsSeg = pPktHdr->uChksumStart;
                 break;
-            default:
-                uFeatures = 0;
-                break;
+            case PDMNETWORKGSOTYPE_INVALID:
+                LogFunc(("%s ignoring invalid GSO frame\n", pThis->szInst));
+                return VERR_INVALID_PARAMETER;
         }
-        if (!uFeatures)
-        {
-            Log2(("%s vnetR3NetworkDown_ReceiveGso: GSO type (0x%x) not supported\n", INSTANCE(pThis), pGso->u8Type));
-            return VERR_NOT_SUPPORTED;
-        }
-    }
-
-    Log2(("%s vnetR3NetworkDown_ReceiveGso: pvBuf=%p cb=%u pGso=%p\n", INSTANCE(pThis), pvBuf, cb, pGso));
-    int rc = vnetR3CanReceive(pDevIns, pThis, pThisCC);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /* Drop packets if VM is not running or cable is disconnected. */
-    VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
-    if ((   enmVMState != VMSTATE_RUNNING
-         && enmVMState != VMSTATE_RUNNING_LS)
-        || !(pThis->config.uStatus & VNET_S_LINK_UP))
-        return VINF_SUCCESS;
-
-    STAM_PROFILE_START(&pThis->StatReceive, a);
-    vpciR3SetReadLed(&pThis->VPCI, true);
-    if (vnetR3AddressFilter(pThis, pvBuf, cb))
-    {
-        rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
-        if (RT_SUCCESS(rc))
-        {
-            rc = vnetR3HandleRxPacket(pDevIns, pThis, pThisCC, pvBuf, cb, pGso);
-            STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
-            vnetCsRxLeave(pThis);
-        }
-    }
-    vpciR3SetReadLed(&pThis->VPCI, false);
-    STAM_PROFILE_STOP(&pThis->StatReceive, a);
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
- */
-static DECLCALLBACK(int) vnetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
-{
-    return vnetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
-}
-
-/**
- * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
- */
-static DECLCALLBACK(int) vnetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
-{
-    PVNETSTATECC    pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkConfig);
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVNETSTATE);
-    memcpy(pMac, pThis->config.mac.au8, sizeof(RTMAC));
-    return VINF_SUCCESS;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
- */
-static DECLCALLBACK(PDMNETWORKLINKSTATE) vnetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
-{
-    PVNETSTATECC    pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkConfig);
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVNETSTATE);
-    if (pThis->config.uStatus & VNET_S_LINK_UP)
-        return PDMNETWORKLINKSTATE_UP;
-    return PDMNETWORKLINKSTATE_DOWN;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
- */
-static DECLCALLBACK(int) vnetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
-{
-    PVNETSTATECC    pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkConfig);
-    PPDMDEVINS      pDevIns = pThisCC->pDevIns;
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    bool fOldUp = !!(pThis->config.uStatus & VNET_S_LINK_UP);
-    bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
-
-    Log(("%s vnetR3NetworkConfig_SetLinkState: enmState=%d\n", INSTANCE(pThis), enmState));
-    if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
-    {
-        if (fOldUp)
-        {
-            /*
-             * We bother to bring the link down only if it was up previously. The UP link state
-             * notification will be sent when the link actually goes up in vnetR3LinkUpTimer().
-             */
-            vnetR3TempLinkDown(pDevIns, pThis, pThisCC);
-            if (pThisCC->pDrv)
-                pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
-        }
-    }
-    else if (fNewUp != fOldUp)
-    {
-        if (fNewUp)
-        {
-            pThis->fCableConnected = true;
-            /* The link state depends both on the cable connected and device attached. */
-            if (vnetR3IsConnected(pDevIns))
-            {
-                Log(("%s Link is up\n", INSTANCE(pThis)));
-                pThis->config.uStatus |= VNET_S_LINK_UP;
-                vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
-            }
-        }
-        else
-        {
-            /* The link was brought down explicitly, make sure it won't come up by timer.  */
-            PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
-            Log(("%s Link is down\n", INSTANCE(pThis)));
-            pThis->fCableConnected = false;
-            pThis->config.uStatus &= ~VNET_S_LINK_UP;
-            vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
-        }
-        if (pThisCC->pDrv)
-            pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
-    }
-    return VINF_SUCCESS;
-}
-
-/**
- * @callback_method_impl{FNVPCIQUEUECALLBACK, The RX queue}
- */
-static DECLCALLBACK(void) vnetR3QueueReceive(PPDMDEVINS pDevIns, PVQUEUE pQueue)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    RT_NOREF(pThis, pQueue);
-    Log(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pThis)));
-    vnetWakeupReceive(pDevIns);
-}
-
-/**
- * Sets up the GSO context according to the Virtio header.
- *
- * @param   pGso                The GSO context to setup.
- * @param   pCtx                The context descriptor.
- */
-DECLINLINE(PPDMNETWORKGSO) vnetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VNETHDR const *pHdr)
-{
-    pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
-
-    if (pHdr->u8GSOType & VNETHDR_GSO_ECN)
-    {
-        AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
-        return NULL;
-    }
-    switch (pHdr->u8GSOType & ~VNETHDR_GSO_ECN)
-    {
-        case VNETHDR_GSO_TCPV4:
-            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
-            pGso->cbHdrsSeg = pHdr->u16HdrLen;
-            break;
-        case VNETHDR_GSO_TCPV6:
-            pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
-            pGso->cbHdrsSeg = pHdr->u16HdrLen;
-            break;
-        case VNETHDR_GSO_UDP:
-            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
-            pGso->cbHdrsSeg = pHdr->u16CSumStart;
-            break;
-        default:
-            return NULL;
-    }
-    if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
-        pGso->offHdr2  = pHdr->u16CSumStart;
-    else
-    {
-        AssertMsgFailed(("GSO without checksum offloading!\n"));
-        return NULL;
-    }
-    pGso->offHdr1     = sizeof(RTNETETHERHDR);
-    pGso->cbHdrsTotal = pHdr->u16HdrLen;
-    pGso->cbMaxSeg    = pHdr->u16GSOSize;
-    return pGso;
-}
-
-DECLINLINE(uint16_t) vnetR3CSum16(const void *pvBuf, size_t cb)
-{
-    uint32_t  csum = 0;
-    uint16_t *pu16 = (uint16_t *)pvBuf;
-
-    while (cb > 1)
-    {
-        csum += *pu16++;
-        cb -= 2;
-    }
-    if (cb)
-        csum += *(uint8_t*)pu16;
-    while (csum >> 16)
-        csum = (csum >> 16) + (csum & 0xFFFF);
-    return ~csum;
-}
-
-DECLINLINE(void) vnetR3CompleteChecksum(uint8_t *pBuf, size_t cbSize, uint16_t uStart, uint16_t uOffset)
-{
-    AssertReturnVoid(uStart < cbSize);
-    AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cbSize);
-    *(uint16_t *)(pBuf + uStart + uOffset) = vnetR3CSum16(pBuf + uStart, cbSize - uStart);
-}
-
-static bool vnetR3ReadHeader(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVNETHDR pHdr, uint32_t cbMax)
-{
-    int rc = PDMDevHlpPhysRead(pDevIns, GCPhys, pHdr, sizeof(*pHdr)); /** @todo r=bird: Why not PDMDevHlpPCIPhysRead? */
-    if (RT_FAILURE(rc))
-        return false;
-
-    Log4(("virtio-net: header flags=%x gso-type=%x len=%x gso-size=%x csum-start=%x csum-offset=%x cb=%x\n",
-          pHdr->u8Flags, pHdr->u8GSOType, pHdr->u16HdrLen, pHdr->u16GSOSize, pHdr->u16CSumStart, pHdr->u16CSumOffset, cbMax));
-
-    if (pHdr->u8GSOType)
-    {
-        uint32_t u32MinHdrSize;
-
-        /* Segmentation offloading cannot be done without checksumming. */
-        if (RT_UNLIKELY(!(pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)))
-            return false;
-        /* We do not support ECN. */
-        if (RT_UNLIKELY(pHdr->u8GSOType & VNETHDR_GSO_ECN))
-            return false;
-        switch (pHdr->u8GSOType)
-        {
-            case VNETHDR_GSO_TCPV4:
-            case VNETHDR_GSO_TCPV6:
-                u32MinHdrSize = sizeof(RTNETTCP);
-                break;
-            case VNETHDR_GSO_UDP:
-                u32MinHdrSize = 0;
-                break;
-            default:
-                return false;
-        }
-        /* Header+MSS must not exceed the packet size. */
-        if (RT_UNLIKELY(u32MinHdrSize + pHdr->u16CSumStart + pHdr->u16GSOSize > cbMax))
-            return false;
-    }
-    /* Checksum must fit into the frame (validating both checksum fields). */
-    if (   (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
-        && sizeof(uint16_t) + pHdr->u16CSumStart + pHdr->u16CSumOffset > cbMax)
-        return false;
-    Log4(("virtio-net: return true\n"));
-    return true;
-}
-
-static int vnetR3TransmitFrame(PVNETSTATE pThis, PVNETSTATECC pThisCC, PPDMSCATTERGATHER pSgBuf,
-                               PPDMNETWORKGSO pGso, PVNETHDR pHdr)
-{
-    vnetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
-    if (pGso)
-    {
-        /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
-        /*
-         * We cannot use cdHdrs provided by the guest because of different ways
-         * it gets filled out by different versions of kernels.
-         */
-        //if (pGso->cbHdrs < pHdr->u16CSumStart + pHdr->u16CSumOffset + 2)
-        {
-            Log4(("%s vnetR3TransmitPendingPackets: HdrLen before adjustment %d.\n",
-                  INSTANCE(pThis), pGso->cbHdrsTotal));
-            switch (pGso->u8Type)
-            {
-                case PDMNETWORKGSOTYPE_IPV4_TCP:
-                case PDMNETWORKGSOTYPE_IPV6_TCP:
-                    pGso->cbHdrsTotal = pHdr->u16CSumStart +
-                        ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pHdr->u16CSumStart))->th_off * 4;
-                    AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
-                        ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
-                    pGso->cbHdrsSeg   = pGso->cbHdrsTotal;
-                    break;
-                case PDMNETWORKGSOTYPE_IPV4_UDP:
-                    pGso->cbHdrsTotal = (uint8_t)(pHdr->u16CSumStart + sizeof(RTNETUDP));
-                    pGso->cbHdrsSeg   = pHdr->u16CSumStart;
-                    break;
-            }
-            /* Update GSO structure embedded into the frame */
-            ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
-            ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg   = pGso->cbHdrsSeg;
-            Log4(("%s vnetR3TransmitPendingPackets: adjusted HdrLen to %d.\n",
-                  INSTANCE(pThis), pGso->cbHdrsTotal));
-        }
-        Log2(("%s vnetR3TransmitPendingPackets: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
-              INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
+        /* Update GSO structure embedded into the frame */
+        ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
+        ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg   = pGso->cbHdrsSeg;
+        Log4Func(("%s adjusted HdrLen to %d.\n",
+              pThis->szInst, pGso->cbHdrsTotal));
+        Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
+                  pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
+                  pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
         STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
     }
-    else if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
+    else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
     {
         STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
@@ -1357,37 +2698,47 @@
          * This is not GSO frame but checksum offloading is requested.
          */
-        vnetR3CompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
-                             pHdr->u16CSumStart, pHdr->u16CSumOffset);
-    }
-
-    return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, false);
-}
-
-static void vnetR3TransmitPendingPackets(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC,
-                                         PVQUEUE pQueue, bool fOnWorkerThread)
-{
+        virtioNetR3Calc16BitChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
+                             pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
+    }
+
+    return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
+}
+
+/**
+ * Non-reentrant function transmits all available packets from specified Tx virtq to downstream
+ * PDM device (if cable is connected). For each Tx pkt, virtio-net pkt header is converted
+ * to required GSO information (VBox host network stack semantics)
+ *
+ * @param pDevIns           PDM device instance
+ * @param pThis             virtio-net device instance
+ * @param pThisCC           virtio-net device instance
+ * @param pTxVirtq          Address of transmit virtq
+ * @param fOnWorkerThread   Flag to PDM whether to use caller's or or PDM transmit worker's thread.
+ */
+static int virtioNetR3TransmitPkts(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
+                                         PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
+{
+    PVIRTIOCORE pVirtio = &pThis->Virtio;
+
+
+    if (!pThis->fVirtioReady)
+    {
+        LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
+                pThis->szInst, pThis->virtioNetConfig.uStatus));
+        return VERR_IGNORED;
+    }
+
+    if (!pThis->fCableConnected)
+    {
+        Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
+        return VERR_IGNORED;
+    }
+
     /*
-     * Let's deal with the cases we are not going to transmit anything,
-     * to avoid setting 'uIsTransmitting' on and off.
-     */
-    if ((pThis->VPCI.uStatus & VPCI_STATUS_DRV_OK) == 0)
-    {
-        Log(("%s Ignoring transmit requests from non-existent driver (status=0x%x).\n", INSTANCE(pThis), pThis->VPCI.uStatus));
-        return;
-    }
-
-    if (!vnetR3IsConnected(pDevIns))
-    {
-        Log(("%s Ignoring transmit requests while cable is disconnected.\n", INSTANCE(pThis)));
-        return;
-    }
-
-    /*
-     * Only one thread is allowed to transmit at a time, others should skip
-     * transmission as the packets will be picked up by the transmitting
-     * thread.
+     * Only one thread is allowed to transmit at a time, others should skip transmission as the packets
+     * will be picked up by the transmitting thread.
      */
     if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
-        return;
+        return VERR_IGNORED;
 
     PPDMINETWORKUP pDrv = pThisCC->pDrv;
@@ -1399,101 +2750,166 @@
         {
             ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
-            return;
+            return VERR_TRY_AGAIN;
         }
     }
-
-    unsigned int cbHdr;
-    if (vnetR3MergeableRxBuffers(pThis))
-        cbHdr = sizeof(VNETHDRMRX);
-    else
-        cbHdr = sizeof(VNETHDR);
-
-    Log3(("%s vnetR3TransmitPendingPackets: About to transmit %d pending packets\n",
-          INSTANCE(pThis), vringReadAvailIndex(pDevIns, &pThisCC->pTxQueue->VRing) - pThisCC->pTxQueue->uNextAvailIndex));
-
-    vpciR3SetWriteLed(&pThis->VPCI, true);
-
-    /*
-     * Do not remove descriptors from available ring yet, try to allocate the
-     * buffer first.
-     */
-    VQUEUEELEM elem; /* This bugger is big! ~48KB on 64-bit hosts. */
-    while (vqueuePeek(pDevIns, &pThis->VPCI, pQueue, &elem))
-    {
-        unsigned int uOffset = 0;
-        if (elem.cOut < 2 || elem.aSegsOut[0].cb != cbHdr)
+    int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
+    if (!cPkts)
+    {
+        LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
+
+        if (pDrv)
+            pDrv->pfnEndXmit(pDrv);
+
+        ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
+        return VERR_MISSING;
+    }
+    LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
+
+    virtioNetR3SetWriteLed(pThisCC, true);
+
+    /* Disable notifications until all available descriptors have been processed */
+    if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX))
+        virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, false /* fEnable */);
+
+    int rc;
+#ifdef VIRTIO_VBUF_ON_STACK
+    VIRTQBUF_T VirtqBuf;
+
+    VirtqBuf.u32Magic  = VIRTQBUF_MAGIC;
+    VirtqBuf.cRefs     = 1;
+
+    PVIRTQBUF pVirtqBuf = &VirtqBuf;
+    while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, pVirtqBuf)) == VINF_SUCCESS)
+#else /* !VIRTIO_VBUF_ON_STACK */
+    PVIRTQBUF pVirtqBuf = NULL;
+    while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, &pVirtqBuf)) == VINF_SUCCESS)
+#endif /* !VIRTIO_VBUF_ON_STACK */
+    {
+        Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
+
+        PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
+        PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
+        uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
+        size_t uFrameSize = 0;
+
+        AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
+                        ("Desc chain's first seg has insufficient space for pkt header!\n"),
+                        VERR_INTERNAL_ERROR);
+
+#ifdef VIRTIO_VBUF_ON_STACK
+        VIRTIONETPKTHDR PktHdr;
+        PVIRTIONETPKTHDR pPktHdr = &PktHdr;
+#else /* !VIRTIO_VBUF_ON_STACK */
+        PVIRTIONETPKTHDR pPktHdr = (PVIRTIONETPKTHDR)RTMemAllocZ(pThis->cbPktHdr);
+        AssertMsgReturn(pPktHdr, ("Out of Memory\n"), VERR_NO_MEMORY);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+
+        /* Compute total frame size from guest (including virtio-net pkt hdr) */
+        for (unsigned i = 0; i < cSegsFromGuest && uFrameSize < VIRTIONET_MAX_FRAME_SIZE; i++)
+            uFrameSize +=  paSegsFromGuest[i].cbSeg;
+
+        Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uFrameSize));
+        Assert(uFrameSize <= VIRTIONET_MAX_FRAME_SIZE);
+
+        /* Truncate oversized frames. */
+        if (uFrameSize > VIRTIONET_MAX_FRAME_SIZE)
+            uFrameSize = VIRTIONET_MAX_FRAME_SIZE;
+
+        if (pThisCC->pDrv)
         {
-            Log(("%s vnetR3QueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
-                 INSTANCE(pThis), elem.cOut, elem.aSegsOut[0].cb, cbHdr));
-            break; /* For now we simply ignore the header, but it must be there anyway! */
-        }
-        RT_UNTRUSTED_VALIDATED_FENCE();
-
-        VNETHDR Hdr;
-        unsigned int uSize = 0;
-        STAM_PROFILE_ADV_START(&pThis->StatTransmit, a);
-
-        /* Compute total frame size. */
-        for (unsigned int i = 1; i < elem.cOut && uSize < VNET_MAX_FRAME_SIZE; i++)
-            uSize += elem.aSegsOut[i].cb;
-        Log5(("%s vnetR3TransmitPendingPackets: complete frame is %u bytes.\n", INSTANCE(pThis), uSize));
-        Assert(uSize <= VNET_MAX_FRAME_SIZE);
-
-        /* Truncate oversized frames. */
-        if (uSize > VNET_MAX_FRAME_SIZE)
-            uSize = VNET_MAX_FRAME_SIZE;
-        if (pThisCC->pDrv && vnetR3ReadHeader(pDevIns, elem.aSegsOut[0].addr, &Hdr, uSize))
-        {
-            RT_UNTRUSTED_VALIDATED_FENCE();
-            STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
-            STAM_PROFILE_START(&pThis->StatTransmitSend, a);
-
-            PDMNETWORKGSO Gso;
-            PDMNETWORKGSO *pGso = vnetR3SetupGsoCtx(&Gso, &Hdr);
-
-            /** @todo Optimize away the extra copying! (lazy bird) */
-            PPDMSCATTERGATHER pSgBuf;
-            int rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uSize, pGso, &pSgBuf);
+            uFrameSize -= pThis->cbPktHdr;
+            /*
+             * Peel off pkt header and convert to PDM/GSO semantics.
+             */
+            rc = virtioNetR3ReadVirtioTxPktHdr(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uFrameSize /* cbFrame */);
+            if (RT_FAILURE(rc))
+                return rc;
+            virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
+
+            PDMNETWORKGSO  Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
+
+            /* Allocate PDM transmit buffer to send guest provided network frame from to VBox network leaf device */
+            PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
+            rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uFrameSize, pGso, &pSgBufToPdmLeafDevice);
+
+            /*
+             * Copy virtio-net guest S/G buffer to PDM leaf driver S/G buffer
+             * converting from GCphys to virt memory at the same time
+             */
             if (RT_SUCCESS(rc))
             {
-                Assert(pSgBuf->cSegs == 1);
-                pSgBuf->cbUsed = uSize;
-
-                /* Assemble a complete frame. */
-                for (unsigned int i = 1; i < elem.cOut && uSize > 0; i++)
+                STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
+                STAM_PROFILE_START(&pThis->StatTransmitSend, a);
+
+                size_t cbCopied = 0;
+                size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uFrameSize;
+                uint64_t uOffset = 0;
+                while (cbRemain)
                 {
-                    unsigned int cbSegment = RT_MIN(uSize, elem.aSegsOut[i].cb);
-                    PDMDevHlpPhysRead(pDevIns, elem.aSegsOut[i].addr,
-                                      ((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
-                                      cbSegment);
-                    uOffset += cbSegment;
-                    uSize -= cbSegment;
+                    PVIRTIOSGSEG paSeg  = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
+                    uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
+                    uint64_t srcSgLen   = (uint64_t)paSeg->cbSeg;
+                    uint64_t srcSgCur   = (uint64_t)pSgPhysSend->GCPhysCur;
+                    cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
+                    virtioCoreGCPhysRead(pVirtio, pDevIns,
+                                         (RTGCPHYS)pSgPhysSend->GCPhysCur,
+                                         ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
+                    virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
+                    cbRemain -= cbCopied;
+                    uOffset += cbCopied;
                 }
-                rc = vnetR3TransmitFrame(pThis, pThisCC, pSgBuf, pGso, &Hdr);
+
+                LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
+                     uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
+
+                rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
+                if (RT_FAILURE(rc))
+                {
+                    LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
+                    STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
+                    STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
+                    pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
+                }
+                STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
+                STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
             }
             else
             {
-                Log4(("virtio-net: failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
-                STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
-                STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
+                Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
                 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
+#ifndef VIRTIO_VBUF_ON_STACK
+                virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
                 break;
             }
 
-            STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
-            STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
+            virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
+
+            /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
+            virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
+            virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
         }
 
-        /* Remove this descriptor chain from the available ring */
-        vqueueSkip(pDevIns, &pThis->VPCI, pQueue);
-        vqueuePut(pDevIns, &pThis->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
-        vqueueSync(pDevIns, &pThis->VPCI, pQueue);
-        STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
-    }
-    vpciR3SetWriteLed(&pThis->VPCI, false);
+#ifndef VIRTIO_VBUF_ON_STACK
+        virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
+        pVirtqBuf = NULL;
+#endif /* !VIRTIO_VBUF_ON_STACK */
+        /* Before we break the loop we need to check if the queue is empty,
+         * re-enable notifications, and then re-check again to avoid missing
+         * a notification for the descriptor that is added to the queue
+         * after we have checked it on being empty, but before we re-enabled
+         * notifications.
+         */
+        if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
+            && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
+            virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
+    }
+    virtioNetR3SetWriteLed(pThisCC, false);
 
     if (pDrv)
         pDrv->pfnEndXmit(pDrv);
+
     ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
+    return VINF_SUCCESS;
 }
 
@@ -1501,607 +2917,428 @@
  * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
  */
-static DECLCALLBACK(void) vnetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
-{
-    PVNETSTATECC    pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkDown);
+static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+    LogFunc(("\n"));
+    PVIRTIONETCC    pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
     PPDMDEVINS      pDevIns = pThisCC->pDevIns;
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVNETSTATE);
+    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
+    PVIRTIONETVIRTQ pTxVirtq  = &pThis->aVirtqs[TXQIDX(0)];
     STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
-    vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pThisCC->pTxQueue, false /*fOnWorkerThread*/);
-}
-
-# ifdef VNET_TX_DELAY
-
-/**
- * @callback_method_impl{FNVPCIQUEUECALLBACK, The TX queue}
- */
-static DECLCALLBACK(void) vnetR3QueueTransmit(PPDMDEVINS pDevIns, PVQUEUE pQueue)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-
-    if (PDMDevHlpTimerIsActive(pDevIns, pThis->hTxTimer))
-    {
-        PDMDevHlpTimerStop(pDevIns, pThis->hTxTimer);
-        Log3(("%s vnetR3QueueTransmit: Got kicked with notification disabled, re-enable notification and flush TX queue\n", INSTANCE(pThis)));
-        vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pQueue, false /*fOnWorkerThread*/);
-
-        VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
-
-        vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, true);
-
-        vnetR3CsLeave(pDevIns, pThis);
-    }
-    else
-    {
-        VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
-
-        vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, false);
-        PDMDevHlpTimerSetMicro(pDevIns, pThis->hTxTimer, VNET_TX_DELAY);
-        pThis->u64NanoTS = RTTimeNanoTS();
-
-        vnetR3CsLeave(pDevIns, pThis);
-    }
-}
-
-/**
- * @callback_method_impl{FNTMTIMERDEV, Transmit Delay Timer handler.}
- */
-static DECLCALLBACK(void) vnetR3TxTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
+
+    (void)virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
+}
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
+ */
+static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+    SET_LINK_UP(pThis);
+    virtioNetWakeupRxBufWaiter(pDevIns);
+
+    if (pThisCC->pDrv)
+        pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
+
+    LogFunc(("[%s] Link is up\n", pThis->szInst));
     RT_NOREF(hTimer, pvUser);
-
-    uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pThis->u64NanoTS) / 1000);
-    if (u32MicroDiff < pThis->u32MinDiff)
-        pThis->u32MinDiff = u32MicroDiff;
-    if (u32MicroDiff > pThis->u32MaxDiff)
-        pThis->u32MaxDiff = u32MicroDiff;
-    pThis->u32AvgDiff = (pThis->u32AvgDiff * pThis->u32i + u32MicroDiff) / (pThis->u32i + 1);
-    pThis->u32i++;
-    Log3(("vnetR3TxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
-          u32MicroDiff, pThis->u32AvgDiff, pThis->u32MinDiff, pThis->u32MaxDiff));
-
-//    Log3(("%s vnetR3TxTimer: Expired\n", INSTANCE(pThis)));
-    vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pThisCC->pTxQueue, false /*fOnWorkerThread*/);
-
-    VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
-    vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, true);
-    vnetR3CsLeave(pDevIns, pThis);
-}
-
-DECLINLINE(int) vnetR3CreateTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
-{
-    RT_NOREF(pDevIns, pThis, pThisCC);
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
+    PPDMDEVINS   pDevIns = pThisCC->pDevIns;
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+    bool fRequestedLinkStateIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
+
+#ifdef LOG_ENABLED
+    if (LogIs7Enabled())
+    {
+        LogFunc(("[%s]", pThis->szInst));
+        switch(enmState)
+        {
+        case PDMNETWORKLINKSTATE_UP:
+            Log(("UP\n"));
+            break;
+        case PDMNETWORKLINKSTATE_DOWN:
+            Log(("DOWN\n"));
+            break;
+        case PDMNETWORKLINKSTATE_DOWN_RESUME:
+            Log(("DOWN (RESUME)\n"));
+            break;
+        default:
+            Log(("UNKNOWN)\n"));
+        }
+    }
+#endif
+
+    if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
+    {
+        if (IS_LINK_UP(pThis))
+        {
+            /*
+             * We bother to bring the link down only if it was up previously. The UP link state
+             * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
+             */
+            virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
+            if (pThisCC->pDrv)
+                pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
+        }
+    }
+    else if (fRequestedLinkStateIsUp != IS_LINK_UP(pThis))
+    {
+        if (fRequestedLinkStateIsUp)
+        {
+            Log(("[%s] Link is up\n", pThis->szInst));
+            pThis->fCableConnected = true;
+            SET_LINK_UP(pThis);
+        }
+        else /* Link requested to be brought down */
+        {
+            /* The link was brought down explicitly, make sure it won't come up by timer.  */
+            PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
+            Log(("[%s] Link is down\n", pThis->szInst));
+            pThis->fCableConnected = false;
+            SET_LINK_DOWN(pThis);
+        }
+        if (pThisCC->pDrv)
+            pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
+    }
     return VINF_SUCCESS;
 }
-
-DECLINLINE(void) vnetR3DestroyTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
-{
-    RT_NOREF(pDevIns, pThis, pThisCC);
-}
-
-# else /* !VNET_TX_DELAY */
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
+    PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
+
+    return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
+}
+
+static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
+{
+    Log10Func(("[%s]\n", pThis->szInst));
+    int rc = VINF_SUCCESS;
+    for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
+    {
+        PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[uIdxWorker];
+        PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
+
+        if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
+        {
+            PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
+            pWorker->hEvtProcess = NIL_SUPSEMEVENT;
+        }
+        if (pWorkerR3->pThread)
+        {
+            int rcThread;
+            rc = PDMDevHlpThreadDestroy(pDevIns, pWorkerR3->pThread, &rcThread);
+            if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
+                AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
+            pWorkerR3->pThread = NULL;
+        }
+    }
+    return rc;
+}
+
+/**
+ * Creates a worker for specified queue, along with semaphore to throttle the worker.
+ *
+ * @param pDevIns       - PDM device instance
+ * @param pThis         - virtio-net instance
+ * @param pWorker       - Pointer to worker state
+ * @param pWorkerR3     - Pointer to worker state
+ * @param pVirtq        - Pointer to virtq
+ */
+static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis,
+                                            PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
+                                            PVIRTIONETVIRTQ pVirtq)
+{
+    Log10Func(("[%s]\n", pThis->szInst));
+    RT_NOREF(pThis);
+
+    int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess);
+
+    if (RT_FAILURE(rc))
+        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+                                   N_("DevVirtioNET: Failed to create SUP event semaphore"));
+
+    LogFunc(("creating thread for queue %s\n", pVirtq->szName));
+
+    rc = PDMDevHlpThreadCreate(pDevIns, &pWorkerR3->pThread,
+                               (void *)pWorker, virtioNetR3WorkerThread,
+                               virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, pVirtq->szName);
+    if (RT_FAILURE(rc))
+        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+                                   N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
+
+    pWorker->fAssigned = true;  /* Because worker's state in fixed-size array initialized w/empty slots */
+
+    LogFunc(("%s pThread: %p\n", pVirtq->szName, pWorkerR3->pThread));
+
+    return rc;
+}
+
+static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
+{
+    Log10Func(("[%s]\n", pThis->szInst));
+    int rc;
+
+    /* Create the Control Queue worker anyway whether or not it is feature-negotiated or utilized by the guest.
+     * See related comment for queue construction in the device constructor function for more context.
+     */
+
+    PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
+    rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis,
+                                          &pThis->aWorkers[CTRLQIDX], &pThisCC->aWorkers[CTRLQIDX], pCtlVirtq);
+    AssertRCReturn(rc, rc);
+
+    pCtlVirtq->fHasWorker = true;
+
+    for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
+    {
+        PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
+        PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
+
+        rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, &pThis->aWorkers[TXQIDX(uVirtqPair)],
+                                              &pThisCC->aWorkers[TXQIDX(uVirtqPair)], pTxVirtq);
+        AssertRCReturn(rc, rc);
+
+        pTxVirtq->fHasWorker = true;
+        pRxVirtq->fHasWorker = false;
+    }
+
+    if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
+        pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
+
+    pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
+
+    return rc;
+}
+
 
 /**
  * @callback_method_impl{FNPDMTHREADDEV}
  */
-static DECLCALLBACK(int) vnetR3TxThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
+static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+    PVIRTIONET         pThis     = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC       pThisCC   = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+    PVIRTIONETWORKER   pWorker   = (PVIRTIONETWORKER)pThread->pvUser;
+    PVIRTIONETVIRTQ    pVirtq    = &pThis->aVirtqs[pWorker->uIdx];
+    uint16_t           uIdx      = pWorker->uIdx;
+
+    ASMAtomicWriteBool(&pWorker->fSleeping, false);
+
+    Assert(pWorker->uIdx == pVirtq->uIdx);
 
     if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
         return VINF_SUCCESS;
 
-    int rc = VINF_SUCCESS;
-    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
-    {
-        rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hTxEvent, RT_INDEFINITE_WAIT);
-        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
-            break;
-        STAM_COUNTER_INC(&pThis->StatTransmitByThread);
-        while (true)
+    LogFunc(("[%s] worker thread idx=%d started for %s (virtq idx=%d)\n", pThis->szInst,  pWorker->uIdx, pVirtq->szName, pVirtq->uIdx));
+
+    /** @todo Race w/guest enabling/disabling guest notifications cyclically.
+              See BugRef #8651, Comment #82 */
+    virtioCoreVirtqEnableNotify(&pThis->Virtio, uIdx, true /* fEnable */);
+
+    while (   pThread->enmState != PDMTHREADSTATE_TERMINATING
+           && pThread->enmState != PDMTHREADSTATE_TERMINATED)
+    {
+        if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio,  pVirtq->uIdx))
         {
-            vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pThisCC->pTxQueue, false /*fOnWorkerThread*/); /// @todo shouldn't it be true instead?
-            Log(("vnetR3TxThread: enable kicking and get to sleep\n"));
-            vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, true);
-            /*
-             * Break out of the loop if the device is not connected. Otherwise we will
-             * end up in a tight loop, not being able to transmit, if there is something
-             * in TX queue. See @bugref{10096}.
+            /*  Precisely coordinated atomic interlocks avoid a race condition that results in hung thread
+             *  wherein a sloppily coordinated wake-up notification during a transition into or out
+             * of sleep leaves notifier and target mutually confused about actual & intended state.
              */
-            if (vqueueIsEmpty(pDevIns, pThisCC->pTxQueue) || !vnetR3IsConnected(pDevIns))
-                break;
-            vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, false);
+            ASMAtomicWriteBool(&pWorker->fSleeping, true);
+            bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
+            if (!fNotificationSent)
+            {
+                Log10Func(("[%s] %s worker sleeping...\n\n", pThis->szInst, pVirtq->szName));
+                Assert(ASMAtomicReadBool(&pWorker->fSleeping));
+
+                int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
+                STAM_COUNTER_INC(&pThis->StatTransmitByThread);
+                AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
+                if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
+                    return VINF_SUCCESS;
+                if (rc == VERR_INTERRUPTED)
+                    continue;
+               ASMAtomicWriteBool(&pWorker->fNotified, false);
+            }
+            ASMAtomicWriteBool(&pWorker->fSleeping, false);
         }
-    }
-
-    return rc;
-}
-
-/**
- * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
- */
-static DECLCALLBACK(int) vnetR3TxThreadWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    RT_NOREF(pThread);
-    return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTxEvent);
-}
-
-static int vnetR3CreateTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
-{
-    int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hTxEvent);
-    if (RT_SUCCESS(rc))
-    {
-        rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->pTxThread, NULL, vnetR3TxThread,
-                                   vnetR3TxThreadWakeUp, 0, RTTHREADTYPE_IO, INSTANCE(pThis));
-        if (RT_FAILURE(rc))
-            PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("VNET: Failed to create worker thread %s"), INSTANCE(pThis));
+        /*
+         * Dispatch to the handler for the queue this worker is set up to drive
+         */
+        if (pVirtq->fCtlVirtq)
+        {
+            Log10Func(("[%s] %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
+#ifdef VIRTIO_VBUF_ON_STACK
+            VIRTQBUF_T VirtqBuf;
+            PVIRTQBUF pVirtqBuf = &VirtqBuf;
+            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, pVirtqBuf, true);
+#else /* !VIRTIO_VBUF_ON_STACK */
+            PVIRTQBUF pVirtqBuf = NULL;
+            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, &pVirtqBuf, true);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+            if (rc == VERR_NOT_AVAILABLE)
+            {
+                Log10Func(("[%s] %s worker woken. Nothing found in queue\n", pThis->szInst, pVirtq->szName));
+                continue;
+            }
+            virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
+#ifndef VIRTIO_VBUF_ON_STACK
+            virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+        }
+        else /* Must be Tx queue */
+        {
+            Log10Func(("[%s] %s worker woken. Virtq has data to transmit\n",  pThis->szInst, pVirtq->szName));
+            virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
+        }
+        /* Note: Surprise! Rx queues aren't handled by local worker threads. Instead, the PDM network leaf driver
+         * invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback, which waits until woken by virtioNetVirtqNotified()
+         * indicating that guest IN buffers have been added to Rx virt queue.
+         */
+    }
+    Log10(("[%s] %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
+ *
+ * Called back by the core code when VirtIO's ready state has changed.
+ */
+static DECLCALLBACK(void) virtioNetR3StatusChg(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
+{
+    PVIRTIONET     pThis     = RT_FROM_MEMBER(pVirtio,  VIRTIONET, Virtio);
+    PVIRTIONETCC   pThisCC   = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
+
+    pThis->fVirtioReady = fVirtioReady;
+
+    if (fVirtioReady)
+    {
+#ifdef LOG_ENABLED
+        Log(("\n%-23s: %s *** VirtIO Ready ***\n\n", __FUNCTION__, pThis->szInst));
+        virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
+#endif
+        pThis->fResetting = false;
+        pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(pVirtio);
+        /* Now we can properly figure out the size of virtio header! */
+        virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
+        pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
+
+        for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+        {
+            PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+            PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+
+            Assert(pWorker->uIdx == uVirtqNbr);
+            RT_NOREF(pWorker);
+
+            Assert(pVirtq->uIdx == pWorker->uIdx);
+
+            (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
+            pVirtq->fAttachedToVirtioCore = true;
+            if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
+                virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
+        }
+
+        virtioNetWakeupRxBufWaiter(pThisCC->pDevIns);
     }
     else
-        PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("VNET: Failed to create SUP event semaphore"));
-    return rc;
-}
-
-static void vnetR3DestroyTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
-{
-    if (pThisCC->pTxThread)
-    {
-        /* Destroy the thread. */
-        int rcThread;
-        int rc = PDMDevHlpThreadDestroy(pDevIns, pThisCC->pTxThread, &rcThread);
-        if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
-            AssertMsgFailed(("%s Failed to destroy async IO thread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
-        pThisCC->pTxThread = NULL;
-    }
-
-    if (pThis->hTxEvent != NIL_SUPSEMEVENT)
-    {
-        PDMDevHlpSUPSemEventClose(pDevIns, pThis->hTxEvent);
-        pThis->hTxEvent = NIL_SUPSEMEVENT;
-    }
-}
-
-/**
- * @callback_method_impl{FNVPCIQUEUECALLBACK, The TX queue}
- */
-static DECLCALLBACK(void) vnetR3QueueTransmit(PPDMDEVINS pDevIns, PVQUEUE pQueue)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-
-    Log(("vnetR3QueueTransmit: disable kicking and wake up TX thread\n"));
-    vringSetNotification(pDevIns, &pQueue->VRing, false);
-    int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTxEvent);
-    AssertRC(rc);
-}
-
-# endif /* !VNET_TX_DELAY */
-
-static uint8_t vnetR3ControlRx(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC,
-                               PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
-{
-    uint8_t u8Ack = VNET_OK;
-    uint8_t fOn, fDrvWasPromisc = pThis->fPromiscuous | pThis->fAllMulti;
-    PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr, &fOn, sizeof(fOn));
-    Log(("%s vnetR3ControlRx: uCommand=%u fOn=%u\n", INSTANCE(pThis), pCtlHdr->u8Command, fOn));
-    switch (pCtlHdr->u8Command)
-    {
-        case VNET_CTRL_CMD_RX_MODE_PROMISC:
-            pThis->fPromiscuous = !!fOn;
-            break;
-        case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
-            pThis->fAllMulti = !!fOn;
-            break;
-        default:
-            u8Ack = VNET_ERROR;
-    }
-    if (fDrvWasPromisc != (pThis->fPromiscuous | pThis->fAllMulti) && pThisCC->pDrv)
-        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv,
-            (pThis->fPromiscuous | pThis->fAllMulti));
-
-    return u8Ack;
-}
-
-static uint8_t vnetR3ControlMac(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
-{
-    uint32_t nMacs = 0;
-
-    if (   pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
-        || pElem->cOut != 3
-        || pElem->aSegsOut[1].cb < sizeof(nMacs)
-        || pElem->aSegsOut[2].cb < sizeof(nMacs))
-    {
-        Log(("%s vnetR3ControlMac: Segment layout is wrong (u8Command=%u nOut=%u cb1=%u cb2=%u)\n",
-             INSTANCE(pThis), pCtlHdr->u8Command, pElem->cOut, pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
-        return VNET_ERROR;
-    }
-
-    /* Load unicast addresses */
-    PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr, &nMacs, sizeof(nMacs));
-
-    if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
-    {
-        Log(("%s vnetR3ControlMac: The unicast mac segment is too small (nMacs=%u cb=%u)\n",
-             INSTANCE(pThis), nMacs, pElem->aSegsOut[1].cb));
-        return VNET_ERROR;
-    }
-
-    if (nMacs > VNET_MAC_FILTER_LEN)
-    {
-        Log(("%s vnetR3ControlMac: MAC table is too big, have to use promiscuous mode (nMacs=%u)\n", INSTANCE(pThis), nMacs));
-        pThis->fPromiscuous = true;
-    }
-    else
-    {
-        if (nMacs)
-            PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr + sizeof(nMacs), pThis->aMacFilter, nMacs * sizeof(RTMAC));
-        pThis->cMacFilterEntries = nMacs;
-#ifdef LOG_ENABLED
-        Log(("%s vnetR3ControlMac: unicast macs:\n", INSTANCE(pThis)));
-        for(unsigned i = 0; i < nMacs; i++)
-            Log(("         %RTmac\n", &pThis->aMacFilter[i]));
-#endif
-    }
-
-    /* Load multicast addresses */
-    PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[2].addr, &nMacs, sizeof(nMacs));
-
-    if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
-    {
-        Log(("%s vnetR3ControlMac: The multicast mac segment is too small (nMacs=%u cb=%u)\n",
-             INSTANCE(pThis), nMacs, pElem->aSegsOut[2].cb));
-        return VNET_ERROR;
-    }
-
-    if (nMacs > VNET_MAC_FILTER_LEN - pThis->cMacFilterEntries)
-    {
-        Log(("%s vnetR3ControlMac: MAC table is too big, have to use allmulti mode (nMacs=%u)\n", INSTANCE(pThis), nMacs));
-        pThis->fAllMulti = true;
-    }
-    else
-    {
-        if (nMacs)
-            PDMDevHlpPhysRead(pDevIns,
-                              pElem->aSegsOut[2].addr + sizeof(nMacs),
-                              &pThis->aMacFilter[pThis->cMacFilterEntries],
-                              nMacs * sizeof(RTMAC));
-#ifdef LOG_ENABLED
-        Log(("%s vnetR3ControlMac: multicast macs:\n", INSTANCE(pThis)));
-        for(unsigned i = 0; i < nMacs; i++)
-            Log(("         %RTmac\n",
-                 &pThis->aMacFilter[i+pThis->cMacFilterEntries]));
-#endif
-        pThis->cMacFilterEntries += nMacs;
-    }
-
-    return VNET_OK;
-}
-
-static uint8_t vnetR3ControlVlan(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
-{
-    uint8_t  u8Ack = VNET_OK;
-    uint16_t u16Vid;
-
-    if (pElem->cOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
-    {
-        Log(("%s vnetR3ControlVlan: Segment layout is wrong (u8Command=%u nOut=%u cb=%u)\n",
-             INSTANCE(pThis), pCtlHdr->u8Command, pElem->cOut, pElem->aSegsOut[1].cb));
-        return VNET_ERROR;
-    }
-
-    PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr, &u16Vid, sizeof(u16Vid));
-
-    if (u16Vid >= VNET_MAX_VID)
-    {
-        Log(("%s vnetR3ControlVlan: VLAN ID is out of range (VID=%u)\n", INSTANCE(pThis), u16Vid));
-        return VNET_ERROR;
-    }
-
-    Log(("%s vnetR3ControlVlan: uCommand=%u VID=%u\n", INSTANCE(pThis), pCtlHdr->u8Command, u16Vid));
-
-    switch (pCtlHdr->u8Command)
-    {
-        case VNET_CTRL_CMD_VLAN_ADD:
-            ASMBitSet(pThis->aVlanFilter, u16Vid);
-            break;
-        case VNET_CTRL_CMD_VLAN_DEL:
-            ASMBitClear(pThis->aVlanFilter, u16Vid);
-            break;
-        default:
-            u8Ack = VNET_ERROR;
-    }
-
-    return u8Ack;
-}
-
-
-/**
- * @callback_method_impl{FNVPCIQUEUECALLBACK, The CLT queue}
- */
-static DECLCALLBACK(void) vnetR3QueueControl(PPDMDEVINS pDevIns, PVQUEUE pQueue)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-
-    VQUEUEELEM      elem;
-    while (vqueueGet(pDevIns, &pThis->VPCI, pQueue, &elem))
-    {
-        if (elem.cOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
+    {
+        Log(("\n%-23s: %s VirtIO is resetting ***\n", __FUNCTION__, pThis->szInst));
+
+        pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
+        Log7(("%-23s: %s Link is %s\n", __FUNCTION__, pThis->szInst, pThis->fCableConnected ? "up" : "down"));
+
+        pThis->fPromiscuous         = true;
+        pThis->fAllMulticast        = false;
+        pThis->fAllUnicast          = false;
+        pThis->fNoMulticast         = false;
+        pThis->fNoUnicast           = false;
+        pThis->fNoBroadcast         = false;
+        pThis->uIsTransmitting      = 0;
+        pThis->cUnicastFilterMacs   = 0;
+        pThis->cMulticastFilterMacs = 0;
+
+        memset(pThis->aMacMulticastFilter,  0, sizeof(pThis->aMacMulticastFilter));
+        memset(pThis->aMacUnicastFilter,    0, sizeof(pThis->aMacUnicastFilter));
+        memset(pThis->aVlanFilter,          0, sizeof(pThis->aVlanFilter));
+
+        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
+
+        for (uint16_t uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
         {
-            Log(("%s vnetR3QueueControl: The first 'out' segment is not the header! (%u < 1 || %u < %u).\n",
-                 INSTANCE(pThis), elem.cOut, elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
-            break; /* Skip the element and hope the next one is good. */
+            virtioCoreR3VirtqDetach(&pThis->Virtio, uVirtqNbr);
+            pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore = false;
         }
-        if (   elem.cIn < 1
-            || elem.aSegsIn[elem.cIn - 1].cb < sizeof(VNETCTLACK))
-        {
-            Log(("%s vnetR3QueueControl: The last 'in' segment is too small to hold the acknowledge! (%u < 1 || %u < %u).\n",
-                 INSTANCE(pThis), elem.cIn, elem.aSegsIn[elem.cIn - 1].cb, sizeof(VNETCTLACK)));
-            break; /* Skip the element and hope the next one is good. */
-        }
-
-        uint8_t    bAck;
-        VNETCTLHDR CtlHdr;
-        PDMDevHlpPhysRead(pDevIns, elem.aSegsOut[0].addr, &CtlHdr, sizeof(CtlHdr));
-        switch (CtlHdr.u8Class)
-        {
-            case VNET_CTRL_CLS_RX_MODE:
-                bAck = vnetR3ControlRx(pDevIns, pThis, pThisCC, &CtlHdr, &elem);
-                break;
-            case VNET_CTRL_CLS_MAC:
-                bAck = vnetR3ControlMac(pDevIns, pThis, &CtlHdr, &elem);
-                break;
-            case VNET_CTRL_CLS_VLAN:
-                bAck = vnetR3ControlVlan(pDevIns, pThis, &CtlHdr, &elem);
-                break;
-            default:
-                bAck = VNET_ERROR;
-        }
-        Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pThis), CtlHdr.u8Class, bAck));
-        PDMDevHlpPCIPhysWrite(pDevIns, elem.aSegsIn[elem.cIn - 1].addr, &bAck, sizeof(bAck));
-
-        vqueuePut(pDevIns, &pThis->VPCI, pQueue, &elem, sizeof(bAck));
-        vqueueSync(pDevIns, &pThis->VPCI, pQueue);
-    }
-}
-
-/* -=-=-=-=- Debug info item -=-=-=-=- */
-
-/**
- * @callback_method_impl{FNDBGFINFOARGVDEV}
- */
-static DECLCALLBACK(void) vnetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, int cArgs, char **papszArgs)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    RT_NOREF(cArgs, papszArgs);
-    vpciR3DumpStateWorker(&pThis->VPCI, pHlp);
-}
-
-
-/* -=-=-=-=- Saved state -=-=-=-=- */
-
-/**
- * Saves the configuration.
- *
- * @param   pDevIns     The device instance.
- * @param   pThis       The VNET state.
- * @param   pSSM        The handle to the saved state.
- */
-static void vnetR3SaveConfig(PPDMDEVINS pDevIns, PVNETSTATE pThis, PSSMHANDLE pSSM)
-{
-    pDevIns->pHlpR3->pfnSSMPutMem(pSSM, &pThis->macConfigured, sizeof(pThis->macConfigured));
-}
-
-
-/**
- * @callback_method_impl{FNSSMDEVLIVEEXEC}
- */
-static DECLCALLBACK(int) vnetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    RT_NOREF(uPass);
-    vnetR3SaveConfig(pDevIns, pThis, pSSM);
-    return VINF_SSM_DONT_CALL_AGAIN;
-}
-
-
-/**
- * @callback_method_impl{FNSSMDEVSAVEPREP}
- */
-static DECLCALLBACK(int) vnetR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    RT_NOREF(pSSM);
-
-    int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
-    if (RT_SUCCESS(rc))
-        vnetCsRxLeave(pThis);
-    return rc;
-}
-
-
-/**
- * @callback_method_impl{FNSSMDEVSAVEEXEC}
- */
-static DECLCALLBACK(int) vnetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PCPDMDEVHLPR3   pHlp    = pDevIns->pHlpR3;
-
-    /* Save config first */
-    vnetR3SaveConfig(pDevIns, pThis, pSSM);
-
-    /* Save the common part */
-    int rc = vpciR3SaveExec(pDevIns, pHlp, &pThis->VPCI, pSSM);
-    AssertRCReturn(rc, rc);
-
-    /* Save device-specific part */
-    pHlp->pfnSSMPutMem(     pSSM, pThis->config.mac.au8, sizeof(pThis->config.mac));
-    pHlp->pfnSSMPutBool(    pSSM, pThis->fPromiscuous);
-    pHlp->pfnSSMPutBool(    pSSM, pThis->fAllMulti);
-    pHlp->pfnSSMPutU32(     pSSM, pThis->cMacFilterEntries);
-    pHlp->pfnSSMPutMem(     pSSM, pThis->aMacFilter, pThis->cMacFilterEntries * sizeof(RTMAC));
-    rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
-    AssertRCReturn(rc, rc);
-
-    Log(("%s State has been saved\n", INSTANCE(pThis)));
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @callback_method_impl{FNSSMDEVLOADPREP,
- * Serializes the receive thread - it may be working inside the critsect. }
- */
-static DECLCALLBACK(int) vnetR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
-{
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    RT_NOREF(pSSM);
-
-    int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
-    if (RT_SUCCESS(rc))
-        vnetCsRxLeave(pThis);
-    return rc;
-}
-
-
-/**
- * @callback_method_impl{FNSSMDEVLOADEXEC}
- */
-static DECLCALLBACK(int) vnetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    PCPDMDEVHLPR3   pHlp    = pDevIns->pHlpR3;
-
-    /* config checks */
-    RTMAC macConfigured;
-    int rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured, sizeof(macConfigured));
-    AssertRCReturn(rc, rc);
-    if (memcmp(&macConfigured, &pThis->macConfigured, sizeof(macConfigured))
-        && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
-        LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pThis), &pThis->macConfigured, &macConfigured));
-
-    rc = vpciR3LoadExec(pDevIns, pHlp, &pThis->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
-    AssertRCReturn(rc, rc);
-
-    if (uPass == SSM_PASS_FINAL)
-    {
-        rc = pHlp->pfnSSMGetMem( pSSM, pThis->config.mac.au8, sizeof(pThis->config.mac));
-        AssertRCReturn(rc, rc);
-
-        if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
-        {
-            pHlp->pfnSSMGetBool(pSSM, &pThis->fPromiscuous);
-            pHlp->pfnSSMGetBool(pSSM, &pThis->fAllMulti);
-            pHlp->pfnSSMGetU32(pSSM, &pThis->cMacFilterEntries);
-            pHlp->pfnSSMGetMem(pSSM, pThis->aMacFilter, pThis->cMacFilterEntries * sizeof(RTMAC));
-
-            /* Clear the rest. */
-            if (pThis->cMacFilterEntries < VNET_MAC_FILTER_LEN)
-                memset(&pThis->aMacFilter[pThis->cMacFilterEntries],
-                       0,
-                       (VNET_MAC_FILTER_LEN - pThis->cMacFilterEntries) * sizeof(RTMAC));
-            rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
-            AssertRCReturn(rc, rc);
-        }
-        else
-        {
-            pThis->fPromiscuous = true;
-            pThis->fAllMulti = false;
-            pThis->cMacFilterEntries = 0;
-            memset(pThis->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
-            memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
-            if (pThisCC->pDrv)
-                pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
-        }
-    }
-    Log(("%s State has been restored\n", INSTANCE(pThis)));
-
-    return rc;
-}
-
-
-/**
- * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
- *                      loading.}
- */
-static DECLCALLBACK(int) vnetR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    RT_NOREF(pSSM);
-
-    if (pThisCC->pDrv)
-        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulti));
-    /*
-     * Indicate link down to the guest OS that all network connections have
-     * been lost, unless we've been teleported here.
-     */
-    if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
-    {
-        /*
-         * Since we do not restore the link state, we pretend it is up, so it will be
-         * lowered by vnetR3TempLinkDown, and the guest will be notified. The actual state
-         * of the link will be determined later by vnetR3IsConnected in vnetR3LinkUpTimer.
-         * See @bugref{10096}.
-         */
-        pThis->config.uStatus |= VNET_S_LINK_UP;
-        vnetR3TempLinkDown(pDevIns, pThis, pThisCC);
-    }
-
-    return VINF_SUCCESS;
-}
-
-
-/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
+    }
+}
+
+/**
+ * @callback_method_impl{VIRTIOCORER3,pfnFeatureNegotiationComplete}
+ */
+static DECLCALLBACK(void) pfnFeatureNegotiationComplete(PVIRTIOCORE pVirtio, uint64_t fDriverFeatures, uint32_t fLegacy)
+{
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pVirtio->pDevInsR3, PVIRTIONET);
+
+    LogFunc(("[Feature Negotiation Complete] Guest Driver version is: %s\n", fLegacy ? "legacy" : "modern"));
+    virtioNetConfigurePktHdr(pThis, fLegacy);
+    virtioNetR3SetVirtqNames(pThis, fLegacy);
+
+    /* Senseless for modern guest to use control queue in this case. (See Note 1 in PDM-invoked device constructor) */
+    if (!fLegacy && !(fDriverFeatures & VIRTIONET_F_CTRL_VQ))
+        virtioNetR3VirtqDestroy(pVirtio, &pThis->aVirtqs[CTRLQIDX]);
+}
+
+#endif /* IN_RING3 */
 
 /**
  * @interface_method_impl{PDMDEVREGR3,pfnDetach}
- */
-static DECLCALLBACK(void) vnetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    Log(("%s vnetR3Detach:\n", INSTANCE(pThis)));
+ *
+ * The VM is suspended at this point.
+ */
+static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
     RT_NOREF(fFlags);
 
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+    Log7Func(("[%s]\n", pThis->szInst));
+    RT_NOREF(pThis);
+
     AssertLogRelReturnVoid(iLUN == 0);
 
-    VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
-
-    vnetR3DestroyTxThreadAndEvent(pDevIns, pThis, pThisCC);
-
-    /*
-     * Zero important members.
-     */
     pThisCC->pDrvBase = NULL;
     pThisCC->pDrv     = NULL;
-
-    vnetR3CsLeave(pDevIns, pThis);
-}
-
+}
 
 /**
  * @interface_method_impl{PDMDEVREGR3,pfnAttach}
- */
-static DECLCALLBACK(int) vnetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
-{
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    RT_NOREF(fFlags);
-    LogFlow(("%s vnetR3Attach:\n",  INSTANCE(pThis)));
-
+ *
+ * This is called when we change block driver.
+ */
+static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+    PVIRTIONET       pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC     pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+    Log7Func(("[%s]", pThis->szInst));
     AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
 
-    int rc = vnetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
-    AssertRCReturn(rc, rc);
-
-    /*
-     * Attach the driver.
-     */
-    rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->VPCI.IBase, &pThisCC->pDrvBase, "Network Port");
+    int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
     if (RT_SUCCESS(rc))
     {
@@ -2109,137 +3346,163 @@
         AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
                       rc = VERR_PDM_MISSING_INTERFACE_BELOW);
-
-        vnetR3CreateTxThreadAndEvent(pDevIns, pThis, pThisCC);
     }
     else if (   rc == VERR_PDM_NO_ATTACHED_DRIVER
              || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
-    {
-        /* This should never happen because this function is not called
-         * if there is no driver to attach! */
-        Log(("%s No attached driver!\n", INSTANCE(pThis)));
-    }
+                    Log(("[%s] No attached driver!\n", pThis->szInst));
+
+    RT_NOREF2(pThis, fFlags);
+    return rc;
+}
+
+/**
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
+ */
+static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+    PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
+    if (iLUN)
+        return VERR_PDM_LUN_NOT_FOUND;
+    *ppLed = &pThisR3->led;
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+    PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN,   &pThisCC->INetworkDown);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE,          &pThisCC->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS,      &pThisCC->ILeds);
+    return NULL;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnReset}
+ */
+static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
+{
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+    virtioCoreR3ResetDevice(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
+}
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
+ */
+static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
+{
+    PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+    Log(("[%s] Destroying instance\n", pThis->szInst));
+    if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
+    {
+        PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
+        PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
+        pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
+    }
+
+    virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
+    virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
+ *
+ * Notes about revising originally VirtIO 1.0+ only virtio-net device emulator to be "transitional",
+ * a VirtIO term meaning this now interoperates with both "legacy" (e.g. pre-1.0) and "modern" (1.0+)
+ * guest virtio-net drivers. The changes include migrating VMs saved using prior DevVirtioNet.cpp (0.95)
+ * saveExec/loadExec semantics to use 1.0 save/load semantics.
+ *
+ * Regardless of the 1.0 spec's overall helpful guidance for implementing transitional devices,
+ * A bit is left to the imagination, e.g. some things have to be determined deductively
+ * (AKA "the hard way").
+ *
+ * Case in point: According to VirtIO 0.95 ("legacy") specification, section 2.2.1, "historically"
+ * drivers may start driving prior to feature negotiation and prior to drivers setting DRIVER_OK
+ * status, "provided driver doesn't use features that alter early use of this device".  Interpreted
+ * here to mean a virtio-net driver must respect default settings (such as implicit pkt header default
+ * size, as determined per Note 1 below).
+ *
+ * ----------------------------------------------------------------------------------------------
+ * Transitional device initialization Note 1:  Identifying default value for network Rx pkt hdr size.
+ * (VirtIO 1.0 specification section 5.1.6.1)
+ *
+ * Guest virtio legacy drivers may begin operations prematurely, regardless of early spec's
+ * initialization sequence (see note 2 below). Legacy drivers implicitly default to using the
+ * (historically) shortest-length network packet header *unless* VIRTIONET_F_MRG_RXBUF feature is
+ * negotiated. If feature negotiation phase is [optionally] enacted by a legacy guest (i.e. we strictly
+ * enforce full initialization protocol for modern guests), virtioNetConfigurePktHdr() is invoked again to
+ * finalize device's network packet header size. Best-guess at default packet header size is deduced, e.g.
+ * isn't documented, as follows: A legacy guest with VIRTIONET_F_MRG_RXBUF not-yet-negotiated is the only
+ * case where network I/O could possibly occur with any reasonable assumption about packet type/size,
+ * because logically other permutations couldn't possibly be inferred until feature negotiation
+ * is complete. Specifically, those cases are:
+ *
+ * 1. A modern driver (detected only when VIRTIONET_F_VERSION_1 feature is ack'd by guest, and,
+ * simultaneously, VIRTIONET_F_MRG_RXBUF feature is accepted or declined (determining network receive-packet
+ * processing behavior).
+ *
+ * 2. A legacy driver that has agreed to use VIRTIONET_F_MRG_RXBUF feature, resulting in a two-byte larger pkt hdr,
+ * (as well as deciding Rx packet processing behavior).
+ *
+ * ----------------------------------------------------------------------------------------------
+ * Transitional device initialization Note 2:  Creating unnegotiated control queue.
+ * (VirtIO 1.0 spec, sections 5.1.5 and 5.1.6.5)
+ *
+ * Create all queues immediately, prior to feature negotiation, including control queue (irrespective
+ * of the fact it's too early in initialization for control feature to be approved by guest). This
+ * transitional device must deal with legacy guests which *can* (and on linux have been seen to) use
+ * the control queue prior to feature negotiation.
+ *
+ * The initial assumption is *modern" guest virtio-net drivers out in the wild could never reasonably
+ * attempt something as obviously risky as using ctrlq without first acking VIRTIO_NET_F_CTRL_VQ
+ * feature to establish it. For now, we create the control queue proactively to accomodate a potentially
+ * badly behaved but officially sanctioned legacy virtio-net driver, but *destroy* that same queue
+ * if a driver announces as 'modern' during feature finalization yet leaves VIRTIO_NET_F_CTRL_VQ un-ack'd.
+ */
+static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+    PCPDMDEVHLPR3 pHlp   = pDevIns->pHlpR3;
 
     /*
-     * Temporary set the link down if it was up so that the guest
-     * will know that we have change the configuration of the
-     * network card
+     * Quickly initialize state data to ensure destructor always works.
      */
-    if (RT_SUCCESS(rc))
-        vnetR3TempLinkDown(pDevIns, pThis, pThisCC);
-
-    vnetR3CsLeave(pDevIns, pThis);
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
- */
-static DECLCALLBACK(void) vnetR3Suspend(PPDMDEVINS pDevIns)
-{
-    /* Poke thread waiting for buffer space. */
-    vnetWakeupReceive(pDevIns);
-}
-
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
- */
-static DECLCALLBACK(void) vnetR3PowerOff(PPDMDEVINS pDevIns)
-{
-    /* Poke thread waiting for buffer space. */
-    vnetWakeupReceive(pDevIns);
-}
-
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
- */
-static DECLCALLBACK(int) vnetR3Destruct(PPDMDEVINS pDevIns)
-{
-    PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
-    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-
-# ifdef VNET_TX_DELAY
-    LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n", pThis->u32AvgDiff, pThis->u32MinDiff, pThis->u32MaxDiff));
-# endif
-
-    Log(("%s Destroying instance\n", INSTANCE(pThis)));
-    if (pThis->hEventMoreRxDescAvail != NIL_SUPSEMEVENT)
-    {
-        PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventMoreRxDescAvail);
-        PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventMoreRxDescAvail);
-        pThis->hEventMoreRxDescAvail = NIL_SUPSEMEVENT;
-    }
-
-    // if (PDMCritSectIsInitialized(&pThis->csRx))
-    //     PDMR3CritSectDelete(&pThis->csRx);
-
-    return vpciR3Term(pDevIns, &pThis->VPCI);
-}
-
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
- */
-static DECLCALLBACK(int) vnetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
-{
-    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-    PCPDMDEVHLPR3   pHlp    = pDevIns->pHlpR3;
-    int             rc;
-
-    /*
-     * Initialize the instance data suffiencently for the destructor not to blow up.
-     */
-    RTStrPrintf(pThis->VPCI.szInstance, sizeof(pThis->VPCI.szInstance), "VNet%d", iInstance);
-    pThisCC->pDevIns                = pDevIns;
-    pThis->hEventMoreRxDescAvail    = NIL_SUPSEMEVENT;
-# ifndef VNET_TX_DELAY
-    pThis->hTxEvent                 = NIL_SUPSEMEVENT;
-    pThisCC->pTxThread              = NULL;
-# endif
-
-    /* Initialize state structure */
-    pThis->u32PktNo     = 1;
+    Log7Func(("PDM device instance: %d\n", iInstance));
+    RTStrPrintf(pThis->szInst, sizeof(pThis->szInst), "virtio-net #%d", iInstance);
+
+    pThisCC->pDevIns                          = pDevIns;
+    pThisCC->IBase.pfnQueryInterface          = virtioNetR3QueryInterface;
+    pThisCC->ILeds.pfnQueryStatusLed          = virtioNetR3QueryStatusLed;
+    pThisCC->led.u32Magic = PDMLED_MAGIC;
 
     /* Interfaces */
-    pThisCC->INetworkDown.pfnWaitReceiveAvail = vnetR3NetworkDown_WaitReceiveAvail;
-    pThisCC->INetworkDown.pfnReceive          = vnetR3NetworkDown_Receive;
-    pThisCC->INetworkDown.pfnReceiveGso       = vnetR3NetworkDown_ReceiveGso;
-    pThisCC->INetworkDown.pfnXmitPending      = vnetR3NetworkDown_XmitPending;
-
-    pThisCC->INetworkConfig.pfnGetMac         = vnetR3NetworkConfig_GetMac;
-    pThisCC->INetworkConfig.pfnGetLinkState   = vnetR3NetworkConfig_GetLinkState;
-    pThisCC->INetworkConfig.pfnSetLinkState   = vnetR3NetworkConfig_SetLinkState;
-
-
-    /* Do our own locking. */
-    rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
-    AssertRCReturn(rc, rc);
-
-    /*
-     * Initialize VPCI part.
-     */
-    pThisCC->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
-
-    rc = vpciR3Init(pDevIns, &pThis->VPCI, &pThisCC->VPCI, VIRTIO_NET_ID, VNET_PCI_CLASS, VNET_N_QUEUES);
-    AssertRCReturn(rc, rc);
-
-    pThisCC->pRxQueue  = vpciR3AddQueue(&pThis->VPCI, &pThisCC->VPCI, 256, vnetR3QueueReceive,  "RX ");
-    pThisCC->pTxQueue  = vpciR3AddQueue(&pThis->VPCI, &pThisCC->VPCI, 256, vnetR3QueueTransmit, "TX ");
-    pThisCC->pCtlQueue = vpciR3AddQueue(&pThis->VPCI, &pThisCC->VPCI, 16,  vnetR3QueueControl,  "CTL");
-    AssertLogRelReturn(pThisCC->pCtlQueue && pThisCC->pTxQueue && pThisCC->pRxQueue, VERR_INTERNAL_ERROR_5);
-
-    Log(("%s Constructing new instance\n", INSTANCE(pThis)));
+    pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
+    pThisCC->INetworkDown.pfnReceive          = virtioNetR3NetworkDown_Receive;
+    pThisCC->INetworkDown.pfnReceiveGso       = virtioNetR3NetworkDown_ReceiveGso;
+    pThisCC->INetworkDown.pfnXmitPending      = virtioNetR3NetworkDown_XmitPending;
+    pThisCC->INetworkConfig.pfnGetMac         = virtioNetR3NetworkConfig_GetMac;
+    pThisCC->INetworkConfig.pfnGetLinkState   = virtioNetR3NetworkConfig_GetLinkState;
+    pThisCC->INetworkConfig.pfnSetLinkState   = virtioNetR3NetworkConfig_SetLinkState;
+
+    pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
 
     /*
      * Validate configuration.
      */
-    PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo", "");
+    PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo|Legacy", "");
 
     /* Get config params */
-    rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
+    int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
@@ -2248,12 +3511,4 @@
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
-
-    rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
-    Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
-    if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
-        LogRel(("%s WARNING! Link up delay is set to %u seconds!\n", INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
-    Log(("%s Link up delay is set to %u seconds\n", INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
 
     uint32_t uStatNo = iInstance;
@@ -2262,72 +3517,123 @@
         return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
 
-    vnetPrintFeatures(pThis, vnetIoCb_GetHostFeatures(&pThis->VPCI), "Device supports the following features");
-
-    /* Initialize PCI config space */
-    memcpy(pThis->config.mac.au8, pThis->macConfigured.au8, sizeof(pThis->config.mac.au8));
-    pThis->config.uStatus = 0;
-
-    /* Initialize critical section. */
-    // char szTmp[sizeof(pThis->VPCI.szInstance) + 2];
-    // RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pThis->VPCI.szInstance);
-    // rc = PDMDevHlpCritSectInit(pDevIns, &pThis->csRx, szTmp);
-    // if (RT_FAILURE(rc))
-    //     return rc;
-
-    /* Map our ports to IO space. */
-    rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, 0 /*iPciReion*/, VPCI_CONFIG + sizeof(VNetPCIConfig),
-                                      vnetIOPortOut, vnetIOPortIn, NULL /*pvUser*/, "VirtioNet", NULL /*paExtDescs*/,
-                                      &pThis->hIoPorts);
-    AssertRCReturn(rc, rc);
-
-    /* Register save/restore state handlers. */
-    rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
-                                NULL,           vnetR3LiveExec, NULL,
-                                vnetR3SavePrep, vnetR3SaveExec, NULL,
-                                vnetR3LoadPrep, vnetR3LoadExec, vnetR3LoadDone);
-    AssertRCReturn(rc, rc);
+    rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
+
+    Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
+
+    if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
+        LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
+                pThis->szInst, pThis->cMsLinkUpDelay / 1000));
+
+    Log(("[%s] Link up delay is set to %u seconds\n", pThis->szInst, pThis->cMsLinkUpDelay / 1000));
+
+    /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
+    memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
+
+    Log(("Using MAC address for %s: %2x:%2x:%2x:%2x:%2x:%2x\n", pThis->szInst,
+            pThis->macConfigured.au8[0], pThis->macConfigured.au8[1], pThis->macConfigured.au8[2],
+            pThis->macConfigured.au8[3], pThis->macConfigured.au8[4], pThis->macConfigured.au8[5]));
+
+    LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
+
+    /*
+     * Configure Virtio core (generic Virtio queue and infrastructure management) parameters.
+     */
+#   if FEATURE_OFFERED(STATUS)
+        pThis->virtioNetConfig.uStatus = 0;
+#   endif
+
+    pThis->virtioNetConfig.uMaxVirtqPairs          = VIRTIONET_MAX_QPAIRS;
+    pThisCC->Virtio.pfnFeatureNegotiationComplete  = pfnFeatureNegotiationComplete;
+    pThisCC->Virtio.pfnVirtqNotified               = virtioNetVirtqNotified;
+    pThisCC->Virtio.pfnStatusChanged               = virtioNetR3StatusChg;
+    pThisCC->Virtio.pfnDevCapRead                  = virtioNetR3DevCapRead;
+    pThisCC->Virtio.pfnDevCapWrite                 = virtioNetR3DevCapWrite;
+
+    VIRTIOPCIPARAMS VirtioPciParams;
+    VirtioPciParams.uDeviceId                      = PCI_DEVICE_ID_VIRTIONET_HOST;
+    VirtioPciParams.uClassBase                     = PCI_CLASS_BASE_NETWORK_CONTROLLER;
+    VirtioPciParams.uClassSub                      = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
+    VirtioPciParams.uClassProg                     = PCI_CLASS_PROG_UNSPECIFIED;
+    VirtioPciParams.uSubsystemId                   = DEVICE_PCI_NETWORK_SUBSYSTEM;  /* VirtIO 1.0 allows PCI Device ID here */
+    VirtioPciParams.uInterruptLine                 = 0x00;
+    VirtioPciParams.uInterruptPin                  = 0x01;
+
+    /* Create semaphore used to synchronize/throttle the downstream LUN's Rx waiter thread. */
+    rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
+
+    pThis->fOfferLegacy = VIRTIONET_TRANSITIONAL_ENABLE_FLAG;
+    virtioNetConfigurePktHdr(pThis, pThis->fOfferLegacy); /* set defaults */
+
+    /* Initialize VirtIO core. (*pfnStatusChanged)() callback occurs when both host VirtIO core & guest driver are ready) */
+    rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
+                          VIRTIONET_HOST_FEATURES_OFFERED, pThis->fOfferLegacy,
+                          &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
+
+    pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
+    /** @todo validating features at this point is most probably pointless, as the negotiation hasn't started yet. */
+    if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
+    pThis->cVirtqPairs = pThis->virtioNetConfig.uMaxVirtqPairs;
+    pThis->cVirtqs += pThis->cVirtqPairs * 2 + 1;
+    pThis->aVirtqs[CTRLQIDX].fCtlVirtq = true;
+
+    virtioNetR3SetVirtqNames(pThis, pThis->fOfferLegacy);
+    for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+    {
+        PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+        PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+        PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
+        pVirtq->uIdx = pWorker->uIdx = pWorkerR3->uIdx = uVirtqNbr;
+    }
+    /*
+     * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
+     */
+    rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
 
     /* Create Link Up Timer */
-    rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetR3LinkUpTimer, NULL,
+    rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL,
                               TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
                               "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
+    /*
+     * Attach network driver instance
+     */
+    rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+    if (RT_SUCCESS(rc))
+    {
+        pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+        AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+                      rc = VERR_PDM_MISSING_INTERFACE_BELOW);
+    }
+    else if (   rc == VERR_PDM_NO_ATTACHED_DRIVER
+             || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+    {
+                    Log(("[%s] No attached driver!\n", pThis->szInst));
+                    AssertRCReturn(rc, rc);
+    }
+    /*
+     * Status driver
+     */
+    PPDMIBASE pUpBase;
+    rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
+    if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
+
+    pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
+    /*
+     * Register saved state.
+     */
+    rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIONET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
+                                NULL, NULL, NULL, /** @todo r=aeichner Teleportation? */
+                                NULL, virtioNetR3ModernSaveExec, NULL,
+                                NULL, virtioNetR3ModernLoadExec, virtioNetR3ModernLoadDone);
     AssertRCReturn(rc, rc);
-
-# ifdef VNET_TX_DELAY
-    /* Create Transmit Delay Timer */
-    rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetR3TxTimer, pThis,
-                              TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
-                              "VirtioNet TX Delay", &pThis->hTxTimer);
-    AssertRCReturn(rc, rc);
-
-    pThis->u32i = pThis->u32AvgDiff = pThis->u32MaxDiff = 0;
-    pThis->u32MinDiff = UINT32_MAX;
-# endif /* VNET_TX_DELAY */
-
-    rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->VPCI.IBase, &pThisCC->pDrvBase, "Network Port");
-    if (RT_SUCCESS(rc))
-    {
-        pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
-        AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
-                        VERR_PDM_MISSING_INTERFACE_BELOW);
-
-        vnetR3CreateTxThreadAndEvent(pDevIns, pThis, pThisCC);
-    }
-    else if (   rc == VERR_PDM_NO_ATTACHED_DRIVER
-             || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME )
-    {
-         /* No error! */
-        Log(("%s This adapter is not attached to any network!\n", INSTANCE(pThis)));
-    }
-    else
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
-
-    rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventMoreRxDescAvail);
-    AssertRCReturn(rc, rc);
-
-    ASMCompilerBarrier(); /* paranoia */
-    rc = vnetIoCb_Reset(pDevIns);
-    AssertRCReturn(rc, rc);
-
     /*
      * Statistics and debug stuff.
@@ -2357,10 +3663,12 @@
     PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread,    STAMTYPE_COUNTER, "Transmit/ByThread",      STAMUNIT_COUNT,          "Thread-initiated transmissions");
 # endif
-
-    char szInfo[16];
-    RTStrPrintf(szInfo, sizeof(szInfo), pDevIns->iInstance ? "vionet%u" : "vionet", pDevIns->iInstance);
-    PDMDevHlpDBGFInfoRegisterArgv(pDevIns, szInfo, "virtio-net info", vnetR3Info);
-
-    return VINF_SUCCESS;
+    /*
+     * Register the debugger info callback (ignore errors).
+     */
+    char szTmp[128];
+    rc = PDMDevHlpDBGFInfoRegister(pDevIns, "virtio-net", "Display virtio-net info (help, net, features, state, pointers, queues, all)", virtioNetR3Info);
+    if (RT_FAILURE(rc))
+        LogRel(("Failed to register DBGF info for device %s\n", szTmp));
+    return rc;
 }
 
@@ -2370,17 +3678,11 @@
  * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
  */
-static DECLCALLBACK(int) vnetRZConstruct(PPDMDEVINS pDevIns)
+static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
 {
     PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
-    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
-    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
-
-    int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
-    AssertRCReturn(rc, rc);
-
-    rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, vnetIOPortOut, vnetIOPortIn, NULL /*pvUser*/);
-    AssertRCReturn(rc, rc);
-
-    return vpciRZInit(pDevIns, &pThis->VPCI, &pThisCC->VPCI);
+    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+    pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
+    return virtioCoreRZInit(pDevIns, &pThis->Virtio);
 }
 
@@ -2392,33 +3694,33 @@
 const PDMDEVREG g_DeviceVirtioNet =
 {
-    /* .u32version = */             PDM_DEVREG_VERSION,
+    /* .uVersion = */               PDM_DEVREG_VERSION,
     /* .uReserved0 = */             0,
     /* .szName = */                 "virtio-net",
-    /* .fFlags = */                 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+    /* .fFlags = */                 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
     /* .fClass = */                 PDM_DEVREG_CLASS_NETWORK,
     /* .cMaxInstances = */          ~0U,
     /* .uSharedVersion = */         42,
-    /* .cbInstanceShared = */       sizeof(VNETSTATE),
-    /* .cbInstanceCC = */           sizeof(VNETSTATECC),
-    /* .cbInstanceRC = */           sizeof(VNETSTATERC),
+    /* .cbInstanceShared = */       sizeof(VIRTIONET),
+    /* .cbInstanceCC = */           sizeof(VIRTIONETCC),
+    /* .cbInstanceRC = */           sizeof(VIRTIONETRC),
     /* .cMaxPciDevices = */         1,
-    /* .cMaxMsixVectors = */        0,
-    /* .pszDescription = */         "Virtio Ethernet.\n",
+    /* .cMaxMsixVectors = */        VBOX_MSIX_MAX_ENTRIES,
+    /* .pszDescription = */         "Virtio Host NET.\n",
 #if defined(IN_RING3)
     /* .pszRCMod = */               "VBoxDDRC.rc",
     /* .pszR0Mod = */               "VBoxDDR0.r0",
-    /* .pfnConstruct = */           vnetR3Construct,
-    /* .pfnDestruct = */            vnetR3Destruct,
+    /* .pfnConstruct = */           virtioNetR3Construct,
+    /* .pfnDestruct = */            virtioNetR3Destruct,
     /* .pfnRelocate = */            NULL,
     /* .pfnMemSetup = */            NULL,
     /* .pfnPowerOn = */             NULL,
-    /* .pfnReset = */               NULL,
-    /* .pfnSuspend = */             vnetR3Suspend,
+    /* .pfnReset = */               virtioNetR3Reset,
+    /* .pfnSuspend = */             virtioNetWakeupRxBufWaiter,
     /* .pfnResume = */              NULL,
-    /* .pfnAttach = */              vnetR3Attach,
-    /* .pfnDetach = */              vnetR3Detach,
+    /* .pfnAttach = */              virtioNetR3Attach,
+    /* .pfnDetach = */              virtioNetR3Detach,
     /* .pfnQueryInterface = */      NULL,
     /* .pfnInitComplete = */        NULL,
-    /* .pfnPowerOff = */            vnetR3PowerOff,
+    /* .pfnPowerOff = */            virtioNetWakeupRxBufWaiter,
     /* .pfnSoftReset = */           NULL,
     /* .pfnReserved0 = */           NULL,
@@ -2432,5 +3734,5 @@
 #elif defined(IN_RING0)
     /* .pfnEarlyConstruct = */      NULL,
-    /* .pfnConstruct = */           vnetRZConstruct,
+    /* .pfnConstruct = */           virtioNetRZConstruct,
     /* .pfnDestruct = */            NULL,
     /* .pfnFinalDestruct = */       NULL,
@@ -2445,5 +3747,5 @@
     /* .pfnReserved7 = */           NULL,
 #elif defined(IN_RC)
-    /* .pfnConstruct = */           vnetRZConstruct,
+    /* .pfnConstruct = */           virtioNetRZConstruct,
     /* .pfnReserved0 = */           NULL,
     /* .pfnReserved1 = */           NULL,
@@ -2457,6 +3759,5 @@
 # error "Not in IN_RING3, IN_RING0 or IN_RC!"
 #endif
-    /* .u32VersionEnd = */          PDM_DEVREG_VERSION
+    /* .uVersionEnd = */          PDM_DEVREG_VERSION
 };
 
-#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: unk/src/VBox/Devices/Network/DevVirtioNet_1_0.cpp
===================================================================
--- /trunk/src/VBox/Devices/Network/DevVirtioNet_1_0.cpp	(revision 98063)
+++ 	(revision )
@@ -1,3763 +1,0 @@
-/* $Id$ $Revision$ $Date$ $Author$ */
-
-/** @file
- * VBox storage devices - Virtio NET Driver
- *
- * Log-levels used:
- *    - Level 1:   The most important (but usually rare) things to note
- *    - Level 2:   NET command logging
- *    - Level 3:   Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
- *    - Level 6:   Device <-> Guest Driver negotation, traffic, notifications and state handling
- *    - Level 12:  Brief formatted hex dumps of I/O data
- */
-
-/*
- * Copyright (C) 2006-2022 Oracle and/or its affiliates.
- *
- * This file is part of VirtualBox base platform packages, as
- * available from https://www.virtualbox.org.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation, in version 3 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses>.
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-/*******************************************************************************************************************************
-*   Header Files                                                                                                               *
-***************************************************************************************************************************** **/
-#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
-#define VIRTIONET_WITH_GSO
-
-#include <iprt/types.h>
-#include <iprt/errcore.h>
-#include <iprt/assert.h>
-#include <iprt/string.h>
-
-#include <VBox/sup.h>
-#include <VBox/vmm/pdmdev.h>
-#include <VBox/vmm/stam.h>
-#include <VBox/vmm/pdmcritsect.h>
-#include <VBox/vmm/pdmnetifs.h>
-#include <VBox/msi.h>
-#include <VBox/version.h>
-#include <VBox/log.h>
-
-
-#ifdef IN_RING3
-# include <VBox/VBoxPktDmp.h>
-# include <iprt/alloc.h>
-# include <iprt/memcache.h>
-# include <iprt/semaphore.h>
-# include <iprt/sg.h>
-# include <iprt/param.h>
-# include <iprt/uuid.h>
-#endif
-#include "../VirtIO/VirtioCore.h"
-
-#include "VBoxDD.h"
-
-#define VIRTIONET_TRANSITIONAL_ENABLE_FLAG             1       /** < If set behave as VirtIO "transitional" device  */
-
-/** The current saved state version for the virtio core. */
-#define VIRTIONET_SAVEDSTATE_VERSION                   UINT32_C(1)
-#define VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY  UINT32_C(1) /**< Grandfathered in from DevVirtioNet.cpp      */
-#define VIRTIONET_SAVEDSTATE_VERSION_LEGACY            UINT32_C(2) /**< Grandfathered in from DevVirtioNet.cpp      */
-#define VIRTIONET_VERSION_MARKER_MAC_ADDR { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /**  SSM handling                  */
-
-/*
- * Glossary of networking acronyms used in feature names below:
- *
- * GSO = Generic Segmentation Offload
- * TSO = TCP Segmentation Offload
- * UFO = UDP Fragmentation Offload
- * ECN = Explicit Congestion Notification
- */
-
-/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
- * @{  */
-#define VIRTIONET_F_CSUM                 RT_BIT_64(0)          /**< Handle packets with partial checksum            */
-#define VIRTIONET_F_GUEST_CSUM           RT_BIT_64(1)          /**< Handles packets with partial checksum           */
-#define VIRTIONET_F_CTRL_GUEST_OFFLOADS  RT_BIT_64(2)          /**< Control channel offloads reconfig support       */
-#define VIRTIONET_F_MAC                  RT_BIT_64(5)          /**< Device has given MAC address                    */
-#define VIRTIONET_F_GUEST_TSO4           RT_BIT_64(7)          /**< Driver can receive TSOv4                        */
-#define VIRTIONET_F_GUEST_TSO6           RT_BIT_64(8)          /**< Driver can receive TSOv6                        */
-#define VIRTIONET_F_GUEST_ECN            RT_BIT_64(9)          /**< Driver can receive TSO with ECN                 */
-#define VIRTIONET_F_GUEST_UFO            RT_BIT_64(10)         /**< Driver can receive UFO                          */
-#define VIRTIONET_F_HOST_TSO4            RT_BIT_64(11)         /**< Device can receive TSOv4                        */
-#define VIRTIONET_F_HOST_TSO6            RT_BIT_64(12)         /**< Device can receive TSOv6                        */
-#define VIRTIONET_F_HOST_ECN             RT_BIT_64(13)         /**< Device can receive TSO with ECN                 */
-#define VIRTIONET_F_HOST_UFO             RT_BIT_64(14)         /**< Device can receive UFO                          */
-#define VIRTIONET_F_MRG_RXBUF            RT_BIT_64(15)         /**< Driver can merge receive buffers                */
-#define VIRTIONET_F_STATUS               RT_BIT_64(16)         /**< Config status field is available                */
-#define VIRTIONET_F_CTRL_VQ              RT_BIT_64(17)         /**< Control channel is available                    */
-#define VIRTIONET_F_CTRL_RX              RT_BIT_64(18)         /**< Control channel RX mode + MAC addr filtering    */
-#define VIRTIONET_F_CTRL_VLAN            RT_BIT_64(19)         /**< Control channel VLAN filtering                  */
-#define VIRTIONET_F_CTRL_RX_EXTRA        RT_BIT_64(20)         /**< Control channel RX mode extra functions         */
-#define VIRTIONET_F_GUEST_ANNOUNCE       RT_BIT_64(21)         /**< Driver can send gratuitous packets              */
-#define VIRTIONET_F_MQ                   RT_BIT_64(22)         /**< Support ultiqueue with auto receive steering    */
-#define VIRTIONET_F_CTRL_MAC_ADDR        RT_BIT_64(23)         /**< Set MAC address through control channel         */
-/** @} */
-
-#ifdef IN_RING3
-static const VIRTIO_FEATURES_LIST s_aDevSpecificFeatures[] =
-{
-    { VIRTIONET_F_STATUS,              "   STATUS               Configuration status field is available.\n" },
-    { VIRTIONET_F_MAC,                 "   MAC                  Host has given MAC address.\n" },
-    { VIRTIONET_F_CTRL_VQ,             "   CTRL_VQ              Control channel is available.\n" },
-    { VIRTIONET_F_CTRL_MAC_ADDR,       "   CTRL_MAC_ADDR        Set MAC address through control channel.\n" },
-    { VIRTIONET_F_CTRL_RX,             "   CTRL_RX              Control channel RX mode support.\n" },
-    { VIRTIONET_F_CTRL_VLAN,           "   CTRL_VLAN            Control channel VLAN filtering.\n" },
-    { VIRTIONET_F_CTRL_GUEST_OFFLOADS, "   CTRL_GUEST_OFFLOADS  Control channel offloads reconfiguration support.\n" },
-    { VIRTIONET_F_GUEST_CSUM,          "   GUEST_CSUM           Guest handles packets with partial checksum.\n" },
-    { VIRTIONET_F_GUEST_ANNOUNCE,      "   GUEST_ANNOUNCE       Guest can send gratuitous packets.\n" },
-    { VIRTIONET_F_GUEST_TSO4,          "   GUEST_TSO4           Guest can receive TSOv4.\n" },
-    { VIRTIONET_F_GUEST_TSO6,          "   GUEST_TSO6           Guest can receive TSOv6.\n" },
-    { VIRTIONET_F_GUEST_ECN,           "   GUEST_ECN            Guest can receive TSO with ECN.\n" },
-    { VIRTIONET_F_GUEST_UFO,           "   GUEST_UFO            Guest can receive UFO.\n" },
-    { VIRTIONET_F_HOST_TSO4,           "   HOST_TSO4            Host can receive TSOv4.\n" },
-    { VIRTIONET_F_HOST_TSO6,           "   HOST_TSO6            Host can receive TSOv6.\n" },
-    { VIRTIONET_F_HOST_ECN,            "   HOST_ECN             Host can receive TSO with ECN.\n" },
-    { VIRTIONET_F_HOST_UFO,            "   HOST_UFO             Host can receive UFO.\n" },
-    { VIRTIONET_F_MQ,                  "   MQ                   Host supports multiqueue with automatic receive steering.\n" },
-    { VIRTIONET_F_CSUM,                "   CSUM                 Host handles packets with partial checksum.\n" },
-    { VIRTIONET_F_MRG_RXBUF,           "   MRG_RXBUF            Guest can merge receive buffers.\n" },
-};
-#endif
-
-#ifdef VIRTIONET_WITH_GSO
-# define VIRTIONET_HOST_FEATURES_GSO    \
-      VIRTIONET_F_CSUM                  \
-    | VIRTIONET_F_HOST_TSO4             \
-    | VIRTIONET_F_HOST_TSO6             \
-    | VIRTIONET_F_HOST_UFO              \
-    | VIRTIONET_F_GUEST_TSO4            \
-    | VIRTIONET_F_GUEST_TSO6            \
-    | VIRTIONET_F_GUEST_UFO             \
-    | VIRTIONET_F_GUEST_CSUM                                   /* @bugref(4796) Guest must handle partial chksums   */
-#else
-# define VIRTIONET_HOST_FEATURES_GSO
-#endif
-
-#define VIRTIONET_HOST_FEATURES_OFFERED \
-      VIRTIONET_F_STATUS                \
-    | VIRTIONET_F_GUEST_ANNOUNCE        \
-    | VIRTIONET_F_MAC                   \
-    | VIRTIONET_F_CTRL_VQ               \
-    | VIRTIONET_F_CTRL_RX               \
-    | VIRTIONET_F_CTRL_VLAN             \
-    | VIRTIONET_HOST_FEATURES_GSO       \
-    | VIRTIONET_F_MRG_RXBUF
-
-#define FEATURE_ENABLED(feature)        RT_BOOL(!!(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature))
-#define FEATURE_DISABLED(feature)       (!FEATURE_ENABLED(feature))
-#define FEATURE_OFFERED(feature)        VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
-
-#if FEATURE_OFFERED(MQ)
-/* Instance data doesn't allow an array large enough to contain VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX entries */
-#   define VIRTIONET_MAX_QPAIRS         1  /* This should be increased at some point and made to work */
-#else
-#   define VIRTIONET_MAX_QPAIRS         VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN /* default, VirtIO 1.0, 5.1.6.5.5 */
-#endif
-
-#define VIRTIONET_CTRL_MQ_VQ_PAIRS      64
-#define VIRTIONET_MAX_WORKERS           VIRTIONET_MAX_QPAIRS + 1
-#define VIRTIONET_MAX_VIRTQS            (VIRTIONET_MAX_QPAIRS * 2 + 1)
-#define VIRTIONET_MAX_FRAME_SIZE        65535 + 18  /**< Max IP pkt size + Eth. header w/VLAN tag  */
-#define VIRTIONET_MAC_FILTER_LEN        64
-#define VIRTIONET_MAX_VLAN_ID           4096
-#define VIRTIONET_RX_SEG_COUNT          32
-
-#define VIRTQNAME(uVirtqNbr)            (pThis->aVirtqs[uVirtqNbr]->szName)
-#define CBVIRTQNAME(uVirtqNbr)          RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
-
-#define IS_TX_VIRTQ(n)                  ((n) != CTRLQIDX && ((n) & 1))
-#define IS_RX_VIRTQ(n)                  ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
-#define IS_CTRL_VIRTQ(n)                ((n) == CTRLQIDX)
-
-/*
- * Macros to calculate queue type-pecific index number regardless of scale. VirtIO 1.0, 5.1.2
- */
-#define RXQIDX(qPairIdx)                (qPairIdx * 2)
-#define TXQIDX(qPairIdx)                (RXQIDX(qPairIdx) + 1)
-#define CTRLQIDX                        (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
-
-#define IS_LINK_UP(pState)              !!(pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
-#define IS_LINK_DOWN(pState)            !IS_LINK_UP(pState)
-
-#define SET_LINK_UP(pState) \
-            LogFunc(("SET_LINK_UP\n")); \
-            pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
-            virtioCoreNotifyConfigChanged(&pThis->Virtio)
-
-#define SET_LINK_DOWN(pState) \
-            LogFunc(("SET_LINK_DOWN\n")); \
-            pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
-            virtioCoreNotifyConfigChanged(&pThis->Virtio)
-
-#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
-            (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
-
-#define PCI_DEVICE_ID_VIRTIONET_HOST               0x1000      /**< VirtIO transitional device ID for network card  */
-#define PCI_CLASS_BASE_NETWORK_CONTROLLER          0x0200      /**< PCI Network device class                        */
-#define PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER      0x00        /**< PCI NET Controller subclass                     */
-#define PCI_CLASS_PROG_UNSPECIFIED                 0x00        /**< Programming interface. N/A.                     */
-#define VIRTIONET_PCI_CLASS                        0x01        /**< Base class Mass Storage?                        */
-
-/**
- * VirtIO Network (virtio-net) device-specific configuration subregion (VirtIO 1.0, 5.1.4)
- * Guest MMIO is processed through callback to VirtIO core which forwards references to network configuration
- * fields to this device-specific code through a callback.
- */
-#pragma pack(1)
-
-    typedef struct virtio_net_config
-    {
-        RTMAC  uMacAddress;                                     /**< mac                                            */
-
-#if     FEATURE_OFFERED(STATUS)
-               uint16_t uStatus;                                /**< status                                         */
-#endif
-
-#if     FEATURE_OFFERED(MQ)
-               uint16_t uMaxVirtqPairs;                         /**< max_virtq_pairs                                */
-#endif
-
-    } VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
-
-#pragma pack()
-
-#define VIRTIONET_F_LINK_UP                          1          /**< config status: Link is up                      */
-#define VIRTIONET_F_ANNOUNCE                         2          /**< config status: Announce                        */
-
-/** @name VirtIO 1.0 NET Host Device device specific control types
- * @{  */
-#define VIRTIONET_HDR_F_NEEDS_CSUM                   1          /**< flags: Packet needs checksum                   */
-#define VIRTIONET_HDR_GSO_NONE                       0          /**< gso_type: No Global Segmentation Offset        */
-#define VIRTIONET_HDR_GSO_TCPV4                      1          /**< gso_type: Global Segment Offset for TCPV4      */
-#define VIRTIONET_HDR_GSO_UDP                        3          /**< gso_type: Global Segment Offset for UDP        */
-#define VIRTIONET_HDR_GSO_TCPV6                      4          /**< gso_type: Global Segment Offset for TCPV6      */
-#define VIRTIONET_HDR_GSO_ECN                     0x80          /**< gso_type: Explicit Congestion Notification     */
-/** @} */
-
-/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
-#pragma pack(1)
-struct virtio_net_pkt_hdr {
-    uint8_t  uFlags;                                            /**< flags                                          */
-    uint8_t  uGsoType;                                          /**< gso_type                                       */
-    uint16_t uHdrLen;                                           /**< hdr_len                                        */
-    uint16_t uGsoSize;                                          /**< gso_size                                       */
-    uint16_t uChksumStart;                                      /**< Chksum_start                                   */
-    uint16_t uChksumOffset;                                     /**< Chksum_offset                                  */
-    uint16_t uNumBuffers;                                       /**< num_buffers                                    */
-};
-#pragma pack()
-typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
-AssertCompileSize(VIRTIONETPKTHDR, 12);
-
-/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
-#pragma pack(1)
-struct virtio_net_ctrl_hdr {
-    uint8_t uClass;                                             /**< class                                          */
-    uint8_t uCmd;                                               /**< command                                        */
-};
-#pragma pack()
-typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
-
-typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
-
-/* Command entry fAck values */
-#define VIRTIONET_OK                               0            /**< Internal success status                        */
-#define VIRTIONET_ERROR                            1            /**< Internal failure status                        */
-
-/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
- * @{  */
-#define VIRTIONET_CTRL_RX                           0           /**< Control class: Receive filtering               */
-#define VIRTIONET_CTRL_RX_PROMISC                   0           /**< Promiscuous mode                               */
-#define VIRTIONET_CTRL_RX_ALLMULTI                  1           /**< All-multicast receive                          */
-#define VIRTIONET_CTRL_RX_ALLUNI                    2           /**< All-unicast receive                            */
-#define VIRTIONET_CTRL_RX_NOMULTI                   3           /**< No multicast receive                           */
-#define VIRTIONET_CTRL_RX_NOUNI                     4           /**< No unicast receive                             */
-#define VIRTIONET_CTRL_RX_NOBCAST                   5           /**< No broadcast receive                           */
-/** @} */
-
-typedef uint8_t  VIRTIONET_MAC_ADDRESS[6];
-typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
-typedef uint8_t  VIRTIONET_CTRL_MAC_ENTRIES[][6];
-
-/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
- * @{  */
-#define VIRTIONET_CTRL_MAC                          1           /**< Control class: MAC address filtering           */
-#define VIRTIONET_CTRL_MAC_TABLE_SET                0           /**< Set MAC table                                  */
-#define VIRTIONET_CTRL_MAC_ADDR_SET                 1           /**< Set default MAC address                        */
-/** @} */
-
-/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
- * @{  */
-#define VIRTIONET_CTRL_VLAN                         2           /**< Control class: VLAN filtering                  */
-#define VIRTIONET_CTRL_VLAN_ADD                     0           /**< Add VLAN to filter table                       */
-#define VIRTIONET_CTRL_VLAN_DEL                     1           /**< Delete VLAN from filter table                  */
-/** @} */
-
-/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
- * @{  */
-#define VIRTIONET_CTRL_ANNOUNCE                     3           /**< Control class: Gratuitous Packet Sending       */
-#define VIRTIONET_CTRL_ANNOUNCE_ACK                 0           /**< Gratuitous Packet Sending ACK                  */
-/** @} */
-
-struct virtio_net_ctrl_mq {
-    uint16_t    uVirtqueuePairs;                                /**<  virtqueue_pairs                               */
-};
-
-/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
- * @{  */
-#define VIRTIONET_CTRL_MQ                           4           /**< Control class: Receive steering                */
-#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET              0           /**< Set number of TX/RX queues                     */
-#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN              1           /**< Minimum number of TX/RX queues                 */
-#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX         0x8000           /**< Maximum number of TX/RX queues                 */
-/** @} */
-
-uint64_t    uOffloads;                                          /**< offloads                                       */
-
-/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
- * @{  */
-#define VIRTIONET_CTRL_GUEST_OFFLOADS             5            /**< Control class: Offloads state configuration    */
-#define VIRTIONET_CTRL_GUEST_OFFLOADS_SET         0            /**< Apply new offloads configuration               */
-/** @} */
-
-typedef enum VIRTIONETPKTHDRTYPE
-{
-    kVirtioNetUninitializedPktHdrType   = 0,                   /**< Uninitialized (default) packet header type      */
-    kVirtioNetModernPktHdrWithoutMrgRx  = 1,                   /**< Packets should not be merged (modern driver)    */
-    kVirtioNetModernPktHdrWithMrgRx     = 2,                   /**< Packets should be merged (modern driver)        */
-    kVirtioNetLegacyPktHdrWithoutMrgRx  = 3,                   /**< Packets should not be merged (legacy driver)    */
-    kVirtioNetLegacyPktHdrWithMrgRx     = 4,                   /**< Packets should be merged (legacy driver)        */
-    kVirtioNetFor32BitHack              = 0x7fffffff
-} VIRTIONETPKTHDRTYPE;
-
-/**
- * device-specific queue info
- */
-struct VIRTIONETWORKER;
-struct VIRTIONETWORKERR3;
-
-typedef struct VIRTIONETVIRTQ
-{
-    uint16_t                       uIdx;                        /**< Index of this queue                            */
-    uint16_t                       align;
-    bool                           fCtlVirtq;                   /**< If set this queue is the control queue         */
-    bool                           fHasWorker;                  /**< If set this queue has an associated worker     */
-    bool                           fAttachedToVirtioCore;       /**< Set if queue attached to virtio core           */
-    char                           szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name                              */
-} VIRTIONETVIRTQ, *PVIRTIONETVIRTQ;
-
-/**
- * Worker thread context, shared state.
- */
-typedef struct VIRTIONETWORKER
-{
-    SUPSEMEVENT                     hEvtProcess;                /**< handle of associated sleep/wake-up semaphore   */
-    uint16_t                        uIdx;                       /**< Index of this worker                           */
-    bool volatile                   fSleeping;                  /**< Flags whether worker thread is sleeping or not */
-    bool volatile                   fNotified;                  /**< Flags whether worker thread notified           */
-    bool                            fAssigned;                  /**< Flags whether worker thread has been set up    */
-    uint8_t                         pad;
-} VIRTIONETWORKER;
-/** Pointer to a virtio net worker. */
-typedef VIRTIONETWORKER *PVIRTIONETWORKER;
-
-/**
- * Worker thread context, ring-3 state.
- */
-typedef struct VIRTIONETWORKERR3
-{
-    R3PTRTYPE(PPDMTHREAD)           pThread;                    /**< pointer to worker thread's handle              */
-    uint16_t                        uIdx;                       /**< Index of this worker                           */
-    uint16_t                        pad;
-} VIRTIONETWORKERR3;
-/** Pointer to a virtio net worker. */
-typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
-
-/**
- * VirtIO Host NET device state, shared edition.
- *
- * @extends     VIRTIOCORE
- */
-typedef struct VIRTIONET
-{
-    /** The core virtio state.   */
-    VIRTIOCORE              Virtio;
-
-    /** Virtio device-specific configuration */
-    VIRTIONET_CONFIG_T      virtioNetConfig;
-
-    /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
-    VIRTIONETWORKER         aWorkers[VIRTIONET_MAX_VIRTQS];
-
-    /** Track which VirtIO queues we've attached to */
-    VIRTIONETVIRTQ          aVirtqs[VIRTIONET_MAX_VIRTQS];
-
-    /** PDM device Instance name */
-    char                    szInst[16];
-
-    /** VirtIO features negotiated with the guest, including generic core and device specific */
-    uint64_t                fNegotiatedFeatures;
-
-    /** Number of Rx/Tx queue pairs (only one if MQ feature not negotiated */
-    uint16_t                cVirtqPairs;
-
-    /** Number of Rx/Tx queue pairs that have already been initialized */
-    uint16_t                cInitializedVirtqPairs;
-
-    /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
-    uint16_t                cVirtqs;
-
-    /** Number of worker threads (one for the control queue and one for each Tx queue) */
-    uint16_t                cWorkers;
-
-    /** Alignment */
-    uint16_t                alignment;
-
-    /** Indicates transmission in progress -- only one thread is allowed. */
-    uint32_t                uIsTransmitting;
-
-    /** Link up delay (in milliseconds). */
-    uint32_t                cMsLinkUpDelay;
-
-    /** The number of actually used slots in aMacMulticastFilter. */
-    uint32_t                cMulticastFilterMacs;
-
-    /** The number of actually used slots in aMacUniicastFilter. */
-    uint32_t                cUnicastFilterMacs;
-
-    /** Semaphore leaf device's thread waits on until guest driver sends empty Rx bufs */
-    SUPSEMEVENT             hEventRxDescAvail;
-
-    /** Array of MAC multicast addresses accepted by RX filter. */
-    RTMAC                   aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
-
-    /** Array of MAC unicast addresses accepted by RX filter. */
-    RTMAC                   aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
-
-    /** Default MAC address which rx filtering accepts */
-    RTMAC                   rxFilterMacDefault;
-
-    /** MAC address obtained from the configuration. */
-    RTMAC                   macConfigured;
-
-    /** Bit array of VLAN filter, one bit per VLAN ID. */
-    uint8_t                 aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
-
-    /** Set if PDM leaf device at the network interface is starved for Rx buffers */
-    bool volatile           fLeafWantsEmptyRxBufs;
-
-    /** Number of packet being sent/received to show in debug log. */
-    uint32_t                uPktNo;
-
-    /** Flags whether VirtIO core is in ready state */
-    uint8_t                 fVirtioReady;
-
-    /** Resetting flag */
-    uint8_t                 fResetting;
-
-    /** Promiscuous mode -- RX filter accepts all packets. */
-    uint8_t                 fPromiscuous;
-
-    /** All multicast mode -- RX filter accepts all multicast packets. */
-    uint8_t                 fAllMulticast;
-
-    /** All unicast mode -- RX filter accepts all unicast packets. */
-    uint8_t                 fAllUnicast;
-
-    /** No multicast mode - Supresses multicast receive */
-    uint8_t                 fNoMulticast;
-
-    /** No unicast mode - Suppresses unicast receive */
-    uint8_t                 fNoUnicast;
-
-    /** No broadcast mode - Supresses broadcast receive */
-    uint8_t                 fNoBroadcast;
-
-    /** Type of network pkt header based on guest driver version/features */
-    VIRTIONETPKTHDRTYPE     ePktHdrType;
-
-    /** Size of network pkt header based on guest driver version/features */
-    uint16_t                cbPktHdr;
-
-    /** True if physical cable is attached in configuration. */
-    bool                    fCableConnected;
-
-    /** True if this device should offer legacy virtio support to the guest */
-    bool                    fOfferLegacy;
-
-    /** @name Statistic
-     * @{ */
-    STAMCOUNTER             StatReceiveBytes;
-    STAMCOUNTER             StatTransmitBytes;
-    STAMCOUNTER             StatReceiveGSO;
-    STAMCOUNTER             StatTransmitPackets;
-    STAMCOUNTER             StatTransmitGSO;
-    STAMCOUNTER             StatTransmitCSum;
-#ifdef VBOX_WITH_STATISTICS
-    STAMPROFILE             StatReceive;
-    STAMPROFILE             StatReceiveStore;
-    STAMPROFILEADV          StatTransmit;
-    STAMPROFILE             StatTransmitSend;
-    STAMPROFILE             StatRxOverflow;
-    STAMCOUNTER             StatRxOverflowWakeup;
-    STAMCOUNTER             StatTransmitByNetwork;
-    STAMCOUNTER             StatTransmitByThread;
-    /** @}  */
-#endif
-} VIRTIONET;
-/** Pointer to the shared state of the VirtIO Host NET device. */
-typedef VIRTIONET *PVIRTIONET;
-
-/**
- * VirtIO Host NET device state, ring-3 edition.
- *
- * @extends     VIRTIOCORER3
- */
-typedef struct VIRTIONETR3
-{
-    /** The core virtio ring-3 state. */
-    VIRTIOCORER3                    Virtio;
-
-    /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
-    VIRTIONETWORKERR3               aWorkers[VIRTIONET_MAX_VIRTQS];
-
-    /** The device instance.
-     * @note This is _only_ for use whxen dealing with interface callbacks. */
-    PPDMDEVINSR3                    pDevIns;
-
-    /** Status LUN: Base interface. */
-    PDMIBASE                        IBase;
-
-    /** Status LUN: LED port interface. */
-    PDMILEDPORTS                    ILeds;
-
-    /** Status LUN: LED connector (peer). */
-    R3PTRTYPE(PPDMILEDCONNECTORS)   pLedsConnector;
-
-    /** Status: LED */
-    PDMLED                          led;
-
-    /** Attached network driver. */
-    R3PTRTYPE(PPDMIBASE)            pDrvBase;
-
-    /** Network port interface (down) */
-    PDMINETWORKDOWN                 INetworkDown;
-
-    /** Network config port interface (main). */
-    PDMINETWORKCONFIG               INetworkConfig;
-
-    /** Connector of attached network driver. */
-    R3PTRTYPE(PPDMINETWORKUP)       pDrv;
-
-    /** Link Up(/Restore) Timer. */
-    TMTIMERHANDLE                   hLinkUpTimer;
-
-} VIRTIONETR3;
-
-/** Pointer to the ring-3 state of the VirtIO Host NET device. */
-typedef VIRTIONETR3 *PVIRTIONETR3;
-
-/**
- * VirtIO Host NET device state, ring-0 edition.
- */
-typedef struct VIRTIONETR0
-{
-    /** The core virtio ring-0 state. */
-    VIRTIOCORER0                    Virtio;
-} VIRTIONETR0;
-/** Pointer to the ring-0 state of the VirtIO Host NET device. */
-typedef VIRTIONETR0 *PVIRTIONETR0;
-
-/**
- * VirtIO Host NET device state, raw-mode edition.
- */
-typedef struct VIRTIONETRC
-{
-    /** The core virtio raw-mode state. */
-    VIRTIOCORERC                    Virtio;
-} VIRTIONETRC;
-/** Pointer to the ring-0 state of the VirtIO Host NET device. */
-typedef VIRTIONETRC *PVIRTIONETRC;
-
-/** @typedef VIRTIONETCC
- * The instance data for the current context. */
-typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
-
-/** @typedef PVIRTIONETCC
- * Pointer to the instance data for the current context. */
-typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
-
-#ifdef IN_RING3
-static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
-static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
-
-/**
- * Helper function used when logging state of a VM thread.
- *
- * @param   Thread
- *
- * @return  Associated name of thread as a pointer to a zero-terminated string.
- */
-DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
-{
-    if (!pThread)
-        return "<null>";
-
-    switch(pThread->enmState)
-    {
-        case PDMTHREADSTATE_INVALID:
-            return "invalid state";
-        case PDMTHREADSTATE_INITIALIZING:
-            return "initializing";
-        case PDMTHREADSTATE_SUSPENDING:
-            return "suspending";
-        case PDMTHREADSTATE_SUSPENDED:
-            return "suspended";
-        case PDMTHREADSTATE_RESUMING:
-            return "resuming";
-        case PDMTHREADSTATE_RUNNING:
-            return "running";
-        case PDMTHREADSTATE_TERMINATING:
-            return "terminating";
-        case PDMTHREADSTATE_TERMINATED:
-            return "terminated";
-        default:
-            return "unknown state";
-    }
-}
-#endif
-
-/**
- * Wakeup PDM managed downstream (e.g. hierarchically inferior device's) RX thread
- */
-static DECLCALLBACK(void) virtioNetWakeupRxBufWaiter(PPDMDEVINS pDevIns)
-{
-    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-
-    AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
-
-    STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
-    if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
-    {
-        Log10Func(("[%s] Waking downstream device's Rx buf waiter thread\n", pThis->szInst));
-        int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
-        AssertRC(rc);
-    }
-}
-
-/**
- * Guest notifying us of its activity with a queue. Figure out which queue and respond accordingly.
- *
- * @callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
- */
-static DECLCALLBACK(void) virtioNetVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
-{
-    RT_NOREF(pVirtio);
-    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-
-    PVIRTIONETVIRTQ  pVirtq  = &pThis->aVirtqs[uVirtqNbr];
-    PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
-
-#if defined (IN_RING3) && defined (LOG_ENABLED)
-    RTLogFlush(NULL);
-#endif
-    if (IS_RX_VIRTQ(uVirtqNbr))
-    {
-        uint16_t cBufsAvailable = virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr);
-
-        if (cBufsAvailable)
-        {
-            Log10Func(("%s %u empty bufs added to %s by guest (notifying leaf device)\n",
-                       pThis->szInst, cBufsAvailable, pVirtq->szName));
-            virtioNetWakeupRxBufWaiter(pDevIns);
-        }
-        else
-            Log10Func(("%s \n\n***WARNING: %s notified but no empty bufs added by guest! (skip leaf dev. notification)\n\n",
-                    pThis->szInst, pVirtq->szName));
-    }
-    else if (IS_TX_VIRTQ(uVirtqNbr) || IS_CTRL_VIRTQ(uVirtqNbr))
-    {
-        /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
-        if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
-        {
-            if (ASMAtomicReadBool(&pWorker->fSleeping))
-            {
-                Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
-
-                int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
-                AssertRC(rc);
-            }
-            else
-                Log10Func(("[%s] %s has available buffers - worker already awake\n", pThis->szInst, pVirtq->szName));
-        }
-        else
-            Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
-    }
-    else
-        LogRelFunc(("[%s] unrecognized queue %s (idx=%d) notified\n", pThis->szInst, pVirtq->szName, uVirtqNbr));
-}
-
-#ifdef IN_RING3 /* spans most of the file, at the moment. */
-
-/**
- * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
- */
-static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
-{
-    PVIRTIONET       pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
-
-    Log10Func(("[%s]\n", pThis->szInst));
-    RT_NOREF(pThis);
-    return PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
-}
-
-/**
- * Set queue names, distinguishing between modern or legacy mode.
- *
- * @note This makes it obvious during logging which mode this transitional device is
- *       operating in, legacy or modern.
- *
- * @param  pThis        Device specific device state
- * @param  fLegacy      (input) true if running in legacy mode
- *                              false if running in modern mode
- */
-DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis, uint32_t fLegacy)
-{
-    RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, fLegacy ? "legacy-ctrlq" : " modern-ctrlq");
-    for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
-    {
-        RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-recvq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
-        RTStrPrintf(pThis->aVirtqs[TXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-xmitq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
-    }
-}
-
-/**
- * Dump a packet to debug log.
- *
- * @param   pThis       The virtio-net shared instance data.
- * @param   pbPacket    The packet.
- * @param   cb          The size of the packet.
- * @param   pszText     A string denoting direction of packet transfer.
- */
-DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
-{
-#ifdef LOG_ENABLED
-    if (!LogIs12Enabled())
-        return;
-#endif
-    vboxEthPacketDump(pThis->szInst, pszText, pbPacket, (uint32_t)cb);
-}
-
-#ifdef LOG_ENABLED
-void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONETPKTHDR pRxPktHdr,
-                     uint16_t cVirtqBufs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS GCPhysRxBuf, uint8_t cbRxBuf)
-{
-    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    pRxPktHdr->uNumBuffers = cVirtqBufs;
-    if (pRxPktHdr)
-    {
-        LogFunc(("%*c\nrxPktHdr\n"
-                 "    uFlags ......... %2.2x\n    uGsoType ....... %2.2x\n    uHdrLen ........ %4.4x\n"
-                 "    uGsoSize ....... %4.4x\n    uChksumStart ... %4.4x\n    uChksumOffset .. %4.4x\n",
-                        60, ' ', pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
-                        pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset));
-        if (!virtioCoreIsLegacyMode(&pThis->Virtio) || FEATURE_ENABLED(MRG_RXBUF))
-            LogFunc(("    uNumBuffers .... %4.4x\n", pRxPktHdr->uNumBuffers));
-        virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONETPKTHDR), 0, "Dump of virtual rPktHdr");
-    }
-    virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
-    LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
-    virtioCoreGCPhysHexDump(pDevIns, GCPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
-    LogFunc(("%*c", 60, '-'));
-}
-
-#endif /* LOG_ENABLED */
-
-/**
- * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
- */
-static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
-{
-    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns,  PVIRTIONET);
-    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-
-    bool fNone     = pszArgs && *pszArgs == '\0';
-    bool fAll      = pszArgs && (*pszArgs == 'a' || *pszArgs == 'A'); /* "all"      */
-    bool fNetwork  = pszArgs && (*pszArgs == 'n' || *pszArgs == 'N'); /* "network"  */
-    bool fFeatures = pszArgs && (*pszArgs == 'f' || *pszArgs == 'F'); /* "features" */
-    bool fState    = pszArgs && (*pszArgs == 's' || *pszArgs == 'S'); /* "state"    */
-    bool fPointers = pszArgs && (*pszArgs == 'p' || *pszArgs == 'P'); /* "pointers" */
-    bool fVirtqs   = pszArgs && (*pszArgs == 'q' || *pszArgs == 'Q'); /* "queues    */
-
-    /* Show basic information. */
-    pHlp->pfnPrintf(pHlp,
-        "\n"
-        "---------------------------------------------------------------------------\n"
-        "Debug Info: %s\n"
-        "        (options: [a]ll, [n]et, [f]eatures, [s]tate, [p]ointers, [q]ueues)\n"
-        "---------------------------------------------------------------------------\n\n",
-        pThis->szInst);
-
-    if (fNone)
-        return;
-
-    /* Show offered/unoffered, accepted/rejected features */
-    if (fAll || fFeatures)
-    {
-        virtioCorePrintDeviceFeatures(&pThis->Virtio, pHlp, s_aDevSpecificFeatures,
-            RT_ELEMENTS(s_aDevSpecificFeatures));
-        pHlp->pfnPrintf(pHlp, "\n");
-    }
-
-    /* Show queues (and associate worker info if applicable) */
-    if (fAll || fVirtqs)
-    {
-        pHlp->pfnPrintf(pHlp, "Virtq information:\n\n");
-        for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-        {
-            PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
-
-            if (pVirtq->fHasWorker)
-            {
-                PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[uVirtqNbr];
-                PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
-
-                Assert((pWorker->uIdx == pVirtq->uIdx));
-                Assert((pWorkerR3->uIdx == pVirtq->uIdx));
-
-                if (pWorker->fAssigned)
-                {
-                    pHlp->pfnPrintf(pHlp, "    %-15s (pThread: %p %s) ",
-                        pVirtq->szName,
-                        pWorkerR3->pThread,
-                        virtioNetThreadStateName(pWorkerR3->pThread));
-                    if (pVirtq->fAttachedToVirtioCore)
-                    {
-                        pHlp->pfnPrintf(pHlp, "worker: ");
-                        pHlp->pfnPrintf(pHlp, "%s", pWorker->fSleeping ? "blocking" : "unblocked");
-                        pHlp->pfnPrintf(pHlp, "%s", pWorker->fNotified ? ", notified" : "");
-                    }
-                    else
-                    if (pWorker->fNotified)
-                        pHlp->pfnPrintf(pHlp, "not attached to virtio core");
-                }
-            }
-            else
-            {
-                pHlp->pfnPrintf(pHlp, "    %-15s (INetworkDown's thread) %s", pVirtq->szName,
-                    pVirtq->fAttachedToVirtioCore  ? "" : "not attached to virtio core");
-            }
-            pHlp->pfnPrintf(pHlp, "\n");
-            virtioCoreR3VirtqInfo(pDevIns, pHlp, pszArgs, uVirtqNbr);
-            pHlp->pfnPrintf(pHlp, "    ---------------------------------------------------------------------\n");
-            pHlp->pfnPrintf(pHlp, "\n");
-        }
-        pHlp->pfnPrintf(pHlp, "\n");
-    }
-
-    /* Show various pointers */
-    if (fAll || fPointers)
-    {
-        pHlp->pfnPrintf(pHlp, "Internal Pointers (for instance \"%s\"):\n\n", pThis->szInst);
-        pHlp->pfnPrintf(pHlp, "    pDevIns ................... %p\n",   pDevIns);
-        pHlp->pfnPrintf(pHlp, "    PVIRTIOCORE ............... %p\n",   &pThis->Virtio);
-        pHlp->pfnPrintf(pHlp, "    PVIRTIONET ................ %p\n",   pThis);
-        pHlp->pfnPrintf(pHlp, "    PVIRTIONETCC .............. %p\n",   pThisCC);
-        pHlp->pfnPrintf(pHlp, "    VIRTIONETVIRTQ[] .......... %p\n",   pThis->aVirtqs);
-        pHlp->pfnPrintf(pHlp, "    pDrvBase .................. %p\n",   pThisCC->pDrvBase);
-        pHlp->pfnPrintf(pHlp, "    pDrv ...................... %p\n",   pThisCC->pDrv);
-        pHlp->pfnPrintf(pHlp, "    pDrv ...................... %p\n",   pThisCC->pDrv);
-        pHlp->pfnPrintf(pHlp, "\n");
-    }
-
-    /* Show device state info */
-    if (fAll || fState)
-    {
-        pHlp->pfnPrintf(pHlp, "Device state:\n\n");
-        uint32_t fTransmitting = ASMAtomicReadU32(&pThis->uIsTransmitting);
-
-        pHlp->pfnPrintf(pHlp, "    Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
-        pHlp->pfnPrintf(pHlp, "\n");
-        pHlp->pfnPrintf(pHlp, "Misc state\n");
-        pHlp->pfnPrintf(pHlp, "\n");
-        pHlp->pfnPrintf(pHlp, "    fOfferLegacy .............. %d\n",   pThis->fOfferLegacy);
-        pHlp->pfnPrintf(pHlp, "    fVirtioReady .............. %d\n",   pThis->fVirtioReady);
-        pHlp->pfnPrintf(pHlp, "    fResetting ................ %d\n",   pThis->fResetting);
-        pHlp->pfnPrintf(pHlp, "    fGenUpdatePending ......... %d\n",   pThis->Virtio.fGenUpdatePending);
-        pHlp->pfnPrintf(pHlp, "    fMsiSupport ............... %d\n",   pThis->Virtio.fMsiSupport);
-        pHlp->pfnPrintf(pHlp, "    uConfigGeneration ......... %d\n",   pThis->Virtio.uConfigGeneration);
-        pHlp->pfnPrintf(pHlp, "    uDeviceStatus ............. 0x%x\n", pThis->Virtio.fDeviceStatus);
-        pHlp->pfnPrintf(pHlp, "    cVirtqPairs .,............. %d\n",   pThis->cVirtqPairs);
-        pHlp->pfnPrintf(pHlp, "    cVirtqs .,................. %d\n",   pThis->cVirtqs);
-        pHlp->pfnPrintf(pHlp, "    cWorkers .................. %d\n",   pThis->cWorkers);
-        pHlp->pfnPrintf(pHlp, "    MMIO mapping name ......... %d\n",   pThisCC->Virtio.szMmioName);
-        pHlp->pfnPrintf(pHlp, "\n");
-    }
-
-    /* Show network related information */
-    if (fAll || fNetwork)
-    {
-        pHlp->pfnPrintf(pHlp, "Network configuration:\n\n");
-        pHlp->pfnPrintf(pHlp, "    MAC: ...................... %RTmac\n", &pThis->macConfigured);
-        pHlp->pfnPrintf(pHlp, "\n");
-        pHlp->pfnPrintf(pHlp, "    Cable: .................... %s\n",      pThis->fCableConnected ? "connected" : "disconnected");
-        pHlp->pfnPrintf(pHlp, "    Link-up delay: ............ %d ms\n",   pThis->cMsLinkUpDelay);
-        pHlp->pfnPrintf(pHlp, "\n");
-        pHlp->pfnPrintf(pHlp, "    Accept all multicast: ..... %s\n",      pThis->fAllMulticast  ? "true" : "false");
-        pHlp->pfnPrintf(pHlp, "    Suppress broadcast: ....... %s\n",      pThis->fNoBroadcast   ? "true" : "false");
-        pHlp->pfnPrintf(pHlp, "    Suppress unicast: ......... %s\n",      pThis->fNoUnicast     ? "true" : "false");
-        pHlp->pfnPrintf(pHlp, "    Suppress multicast: ....... %s\n",      pThis->fNoMulticast   ? "true" : "false");
-        pHlp->pfnPrintf(pHlp, "    Promiscuous: .............. %s\n",      pThis->fPromiscuous   ? "true" : "false");
-        pHlp->pfnPrintf(pHlp, "\n");
-        pHlp->pfnPrintf(pHlp, "    Default Rx MAC filter: .... %RTmac\n", pThis->rxFilterMacDefault);
-        pHlp->pfnPrintf(pHlp, "\n");
-
-        pHlp->pfnPrintf(pHlp, "    Unicast filter MACs:\n");
-
-        if (!pThis->cUnicastFilterMacs)
-            pHlp->pfnPrintf(pHlp, "        <none>\n");
-
-        for (uint32_t i = 0; i < pThis->cUnicastFilterMacs; i++)
-            pHlp->pfnPrintf(pHlp, "        %RTmac\n", &pThis->aMacUnicastFilter[i]);
-
-        pHlp->pfnPrintf(pHlp, "\n    Multicast filter MACs:\n");
-
-        if (!pThis->cMulticastFilterMacs)
-            pHlp->pfnPrintf(pHlp, "        <none>\n");
-
-        for (uint32_t i = 0; i < pThis->cMulticastFilterMacs; i++)
-            pHlp->pfnPrintf(pHlp, "        %RTmac\n", &pThis->aMacMulticastFilter[i]);
-
-        pHlp->pfnPrintf(pHlp, "\n\n");
-        pHlp->pfnPrintf(pHlp, "    Leaf starved: ............. %s\n",      pThis->fLeafWantsEmptyRxBufs ? "true" : "false");
-        pHlp->pfnPrintf(pHlp, "\n");
-    }
-    /** @todo implement this
-      * pHlp->pfnPrintf(pHlp, "\n");
-      * virtioCoreR3Info(pDevIns, pHlp, pszArgs);
-      */
-    pHlp->pfnPrintf(pHlp, "\n");
-}
-
-/**
- * Checks whether certain mutually dependent negotiated features are clustered in required combinations.
- *
- * @note See VirtIO 1.0 spec, Section 5.1.3.1
- *
- * @param fFeatures     Bitmask of negotiated features to evaluate
- *
- * @returns             true if valid feature combination(s) found.
- *                      false if non-valid feature set.
- */
-DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
-{
-    uint32_t fGuestChksumRequired =   fFeatures & VIRTIONET_F_GUEST_TSO4
-                                   || fFeatures & VIRTIONET_F_GUEST_TSO6
-                                   || fFeatures & VIRTIONET_F_GUEST_UFO;
-
-    uint32_t fHostChksumRequired =    fFeatures & VIRTIONET_F_HOST_TSO4
-                                   || fFeatures & VIRTIONET_F_HOST_TSO6
-                                   || fFeatures & VIRTIONET_F_HOST_UFO;
-
-    uint32_t fCtrlVqRequired =        fFeatures & VIRTIONET_F_CTRL_RX
-                                   || fFeatures & VIRTIONET_F_CTRL_VLAN
-                                   || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
-                                   || fFeatures & VIRTIONET_F_MQ
-                                   || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
-
-    if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
-        return false;
-
-    if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
-        return false;
-
-    if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
-        return false;
-
-    if (   fFeatures & VIRTIONET_F_GUEST_ECN
-        && !(   fFeatures & VIRTIONET_F_GUEST_TSO4
-             || fFeatures & VIRTIONET_F_GUEST_TSO6))
-                    return false;
-
-    if (   fFeatures & VIRTIONET_F_HOST_ECN
-        && !(   fFeatures & VIRTIONET_F_HOST_TSO4
-             || fFeatures & VIRTIONET_F_HOST_TSO6))
-                    return false;
-    return true;
-}
-
-/**
- * Read or write device-specific configuration parameters.
- * This is called by VirtIO core code a guest-initiated MMIO access is made to access device-specific
- * configuration
- *
- * @note  See VirtIO 1.0 spec, 2.3 Device Configuration Space
- *
- * @param pThis             Pointer to device-specific state
- * @param uOffsetOfAccess   Offset (within VIRTIONET_CONFIG_T)
- * @param pv                Pointer to data to read or write
- * @param cb                Number of bytes to read or write
- * @param fWrite            True if writing, false if reading
- *
- * @returns VINF_SUCCESS if successful, or VINF_IOM_MMIO_UNUSED if fails (bad offset or size)
- */
-static int virtioNetR3DevCfgAccess(PVIRTIONET pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
-{
-    AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
-
-    if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uMacAddress,      VIRTIONET_CONFIG_T, uOffsetOfAccess))
-        VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMacAddress,      VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
-#if FEATURE_OFFERED(STATUS)
-    else
-    if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uStatus,          VIRTIONET_CONFIG_T, uOffsetOfAccess))
-        VIRTIO_DEV_CONFIG_ACCESS_READONLY( uStatus,          VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
-#endif
-#if FEATURE_OFFERED(MQ)
-    else
-    if (VIRTIO_DEV_CONFIG_MATCH_MEMBER(    uMaxVirtqPairs,   VIRTIONET_CONFIG_T, uOffsetOfAccess))
-        VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxVirtqPairs,   VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
-#endif
-    else
-    {
-        LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n",
-                pThis->szInst, uOffsetOfAccess, uOffsetOfAccess, cb));
-        return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
-    }
-    return VINF_SUCCESS;
-}
-
-/**
- * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
- */
-static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
-{
-    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-
-    RT_NOREF(pThis);
-    return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
-}
-
-/**
- * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
- */
-static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
-{
-    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-
-    Log10Func(("[%s] uOffset: %u, cb: %u: %.*Rhxs\n", pThis->szInst, uOffset, cb, cb, pv));
-    RT_NOREF(pThis);
-    return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
-}
-
-static int virtioNetR3VirtqDestroy(PVIRTIOCORE pVirtio, PVIRTIONETVIRTQ pVirtq)
-{
-    PVIRTIONET         pThis     = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
-    PVIRTIONETCC       pThisCC   = PDMDEVINS_2_DATA_CC(pVirtio->pDevInsR3, PVIRTIONETCC);
-    PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[pVirtq->uIdx];
-    PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[pVirtq->uIdx];
-
-    int rc = VINF_SUCCESS, rcThread;
-    Log10Func(("[%s] Destroying \"%s\"", pThis->szInst, pVirtq->szName));
-    if (pVirtq->fHasWorker)
-    {
-        Log10((" and its worker"));
-        rc = PDMDevHlpSUPSemEventClose(pVirtio->pDevInsR3, pWorker->hEvtProcess);
-        AssertRCReturn(rc, rc);
-        pWorker->hEvtProcess = 0;
-        rc = PDMDevHlpThreadDestroy(pVirtio->pDevInsR3,  pWorkerR3->pThread, &rcThread);
-        AssertRCReturn(rc, rc);
-        pWorkerR3->pThread = 0;
-        pVirtq->fHasWorker = false;
-    }
-    pWorker->fAssigned = false;
-    pVirtq->fCtlVirtq  = false;
-    Log10(("\n"));
-    return rc;
-}
-
-/**
- * Takes down the link temporarily if its current status is up.
- *
- * This is used during restore and when replumbing the network link.
- *
- * The temporary link outage is supposed to indicate to the OS that all network
- * connections have been lost and that it for instance is appropriate to
- * renegotiate any DHCP lease.
- *
- * @param   pDevIns     The device instance.
- * @param   pThis       The virtio-net shared instance data.
- * @param   pThisCC     The virtio-net ring-3 instance data.
- */
-static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
-{
-    if (IS_LINK_UP(pThis))
-    {
-        SET_LINK_DOWN(pThis);
-
-        /* Re-establish link in 5 seconds. */
-        int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
-        AssertRC(rc);
-
-        LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
-    }
-}
-
-
-static void virtioNetConfigurePktHdr(PVIRTIONET pThis, uint32_t fLegacy)
-{
-    /* Calculate network packet header type and size based on what we know now */
-    pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
-    if (!fLegacy)
-        /* Modern (e.g. >= VirtIO 1.0) device specification's pkt size rules */
-        if (FEATURE_ENABLED(MRG_RXBUF))
-            pThis->ePktHdrType = kVirtioNetModernPktHdrWithMrgRx;
-        else /* Modern guest driver with MRG_RX feature disabled */
-            pThis->ePktHdrType = kVirtioNetModernPktHdrWithoutMrgRx;
-    else
-    {
-        /* Legacy (e.g. < VirtIO 1.0) device specification's pkt size rules */
-        if (FEATURE_ENABLED(MRG_RXBUF))
-            pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithMrgRx;
-        else /* Legacy guest with MRG_RX feature disabled */
-        {
-            pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
-            pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
-        }
-    }
-}
-
-
-/*********************************************************************************************************************************
-*   Saved state                                                                                                                  *
-*********************************************************************************************************************************/
-
-/**
- * @callback_method_impl{FNSSMDEVLOADEXEC}
- *
- * @note: This is included to accept and migrate VMs that had used the original VirtualBox legacy-only virtio-net (network card)
- *        controller device emulator ("DevVirtioNet.cpp") to work with this superset of VirtIO compatibility known
- *        as a transitional device (see PDM-invoked device constructor comments for more information)
- */
-static DECLCALLBACK(int) virtioNetR3LegacyDeviceLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass,
-                                                    RTMAC uMacLoaded)
-{
-    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-    PCPDMDEVHLPR3  pHlp    = pDevIns->pHlpR3;
-    int rc;
-
-    Log7Func(("[%s] LOAD EXEC (LEGACY)!!\n", pThis->szInst));
-
-    if (   memcmp(&uMacLoaded.au8, &pThis->macConfigured.au8, sizeof(uMacLoaded))
-        && (   uPass == 0
-            || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
-        LogRelFunc(("[%s]: The mac address differs: config=%RTmac saved=%RTmac\n",
-            pThis->szInst, &pThis->macConfigured, &uMacLoaded));
-
-    if (uPass == SSM_PASS_FINAL)
-    {
-        /* Call the virtio core to have it load legacy device state */
-        rc = virtioCoreR3LegacyDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion, VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY);
-        AssertRCReturn(rc, rc);
-        /*
-         * Scan constructor-determined virtqs to determine if they are all valid-as-restored.
-         * If so, nudge them with a signal, otherwise destroy the unusable queue(s)
-         * to avoid tripping up the other queue processing logic.
-         */
-        int cVirtqsToRemove = 0;
-        for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-        {
-            PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
-            if (pVirtq->fHasWorker)
-            {
-                if (!virtioCoreR3VirtqIsEnabled(&pThis->Virtio, uVirtqNbr))
-                {
-                    virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
-                    ++cVirtqsToRemove;
-                }
-                else
-                {
-                    if (virtioCoreR3VirtqIsAttached(&pThis->Virtio, uVirtqNbr))
-                    {
-                        Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
-                        rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[pVirtq->uIdx].hEvtProcess);
-                        AssertRCReturn(rc, rc);
-                    }
-                }
-            }
-        }
-        AssertMsg(cVirtqsToRemove < 2, ("Multiple unusable queues in saved state unexpected\n"));
-        pThis->cVirtqs -= cVirtqsToRemove;
-
-        pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus;
-        pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
-
-        rc = pHlp->pfnSSMGetMem(pSSM, pThis->virtioNetConfig.uMacAddress.au8, sizeof(pThis->virtioNetConfig.uMacAddress));
-        AssertRCReturn(rc, rc);
-
-        if (uVersion > VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY)
-        {
-            /* Zero-out the the Unicast/Multicast filter table */
-            memset(&pThis->aMacUnicastFilter[0], 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
-
-            rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
-            AssertRCReturn(rc, rc);
-            rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
-            AssertRCReturn(rc, rc);
-            /*
-             * The 0.95 legacy virtio spec defines a control queue command VIRTIO_NET_CTRL_MAC_TABLE_SET,
-             * wherein guest driver configures two variable length mac filter tables: A unicast filter,
-             * and a multicast filter. However original VBox virtio-net saved both sets of filter entries
-             * in a single table, abandoning the distinction between unicast and multicast filters. It preserved
-             * only *one* filter's table length, leaving no way to separate table back out into respective unicast
-             * and multicast tables this device implementation preserves. Deduced from legacy code, the original
-             * assumption was that the both MAC filters are whitelists that can be processed identically
-             * (from the standpoint of a *single* host receiver), such that the distinction between unicast and
-             * multicast doesn't matter in any one VM's context. Little choice here but to save the undifferentiated
-             * unicast & multicast MACs to the unicast filter table and leave multicast table empty/unused.
-             */
-            uint32_t cCombinedUnicastMulticastEntries;
-            rc = pHlp->pfnSSMGetU32(pSSM, &cCombinedUnicastMulticastEntries);
-            AssertRCReturn(rc, rc);
-            AssertReturn(cCombinedUnicastMulticastEntries <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
-            pThis->cUnicastFilterMacs = cCombinedUnicastMulticastEntries;
-            rc = pHlp->pfnSSMGetMem(pSSM, pThis->aMacUnicastFilter, cCombinedUnicastMulticastEntries * sizeof(RTMAC));
-            AssertRCReturn(rc, rc);
-            rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
-            AssertRCReturn(rc, rc);
-        }
-        else
-        {
-            pThis->fAllMulticast = false;
-            pThis->cUnicastFilterMacs = 0;
-            memset(&pThis->aMacUnicastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
-
-            memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
-
-            pThis->fPromiscuous = true;
-            if (pThisCC->pDrv)
-                pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
-        }
-
-        /*
-         * Log the restored VirtIO feature selection.
-         */
-        pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
-        /** @todo shouldn't we update the virtio header size here? it depends on the negotiated features. */
-        virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
-
-        /*
-         * Configure remaining transitional device parameters presumably or deductively
-         * as these weren't part of the legacy device code thus it didn't save them to SSM
-         */
-        pThis->fCableConnected = 1;
-        pThis->fAllUnicast     = 0;
-        pThis->fNoMulticast    = 0;
-        pThis->fNoUnicast      = 0;
-        pThis->fNoBroadcast    = 0;
-
-        /* Zero out the multicast table and count, all MAC filters, if any, are in the unicast filter table */
-        pThis->cMulticastFilterMacs = 0;
-        memset(&pThis->aMacMulticastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
-    }
-    return VINF_SUCCESS;
-}
-
-/**
- * @callback_method_impl{FNSSMDEVLOADEXEC}
- *
- * @note: This loads state saved by a Modern (VirtIO 1.0+) device, of which this transitional device is one,
- *        and thus supports both legacy and modern guest virtio drivers.
- */
-static DECLCALLBACK(int) virtioNetR3ModernLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
-{
-    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-    PCPDMDEVHLPR3  pHlp    = pDevIns->pHlpR3;
-    int rc;
-
-    RT_NOREF(pThisCC);
-
-    RTMAC uMacLoaded, uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
-    rc = pHlp->pfnSSMGetMem(pSSM, &uMacLoaded.au8, sizeof(uMacLoaded.au8));
-    AssertRCReturn(rc, rc);
-    if (memcmp(&uMacLoaded.au8, uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8)))
-    {
-        rc = virtioNetR3LegacyDeviceLoadExec(pDevIns, pSSM, uVersion, uPass, uMacLoaded);
-        return rc;
-    }
-
-    Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
-
-    AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
-    AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVEDSTATE_VERSION,
-                          ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
-
-    virtioNetR3SetVirtqNames(pThis, false /* fLegacy */);
-
-    pHlp->pfnSSMGetU64(     pSSM, &pThis->fNegotiatedFeatures);
-
-    pHlp->pfnSSMGetU16(     pSSM, &pThis->cVirtqs);
-    AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2) + 1, VERR_OUT_OF_RANGE);
-    pHlp->pfnSSMGetU16(     pSSM, &pThis->cWorkers);
-    AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS , VERR_OUT_OF_RANGE);
-
-    for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-            pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
-
-    /* Config checks */
-    RTMAC macConfigured;
-    rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
-    AssertRCReturn(rc, rc);
-    if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
-        && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
-            LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
-                    pThis->szInst, &pThis->macConfigured, &macConfigured));
-    memcpy(pThis->virtioNetConfig.uMacAddress.au8, macConfigured.au8, sizeof(macConfigured.au8));
-
-#if FEATURE_OFFERED(STATUS)
-    uint16_t fChkStatus;
-    pHlp->pfnSSMGetU16(     pSSM, &fChkStatus);
-    if (fChkStatus == 0xffff)
-    {
-        /* Dummy value in saved state because status feature wasn't enabled at the time */
-        pThis->virtioNetConfig.uStatus = 0; /* VIRTIO_NET_S_ANNOUNCE disabled */
-        pThis->virtioNetConfig.uStatus = !!IS_LINK_UP(pThis); /* VIRTIO_NET_IS_LINK_UP (bit 0) */
-    }
-    else
-        pThis->virtioNetConfig.uStatus = fChkStatus;
-#else
-    uint16_t fDiscard;
-    pHlp->pfnSSMGetU16(     pSSM, &fDiscard);
-#endif
-
-#if FEATURE_OFFERED(MQ)
-    uint16_t uCheckMaxVirtqPairs;
-    pHlp->pfnSSMGetU16(     pSSM, &uCheckMaxVirtqPairs);
-    if (uCheckMaxVirtqPairs)
-        pThis->virtioNetConfig.uMaxVirtqPairs = uCheckMaxVirtqPairs;
-    else
-        pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_CTRL_MQ_VQ_PAIRS;
-#else
-    uint16_t fDiscard;
-    pHlp->pfnSSMGetU16(     pSSM, &fDiscard);
-#endif
-
-    /* Save device-specific part */
-    pHlp->pfnSSMGetBool(    pSSM, &pThis->fCableConnected);
-    pHlp->pfnSSMGetU8(      pSSM, &pThis->fPromiscuous);
-    pHlp->pfnSSMGetU8(      pSSM, &pThis->fAllMulticast);
-    pHlp->pfnSSMGetU8(      pSSM, &pThis->fAllUnicast);
-    pHlp->pfnSSMGetU8(      pSSM, &pThis->fNoMulticast);
-    pHlp->pfnSSMGetU8(      pSSM, &pThis->fNoUnicast);
-    pHlp->pfnSSMGetU8(      pSSM, &pThis->fNoBroadcast);
-
-    pHlp->pfnSSMGetU32(     pSSM, &pThis->cMulticastFilterMacs);
-    AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
-    pHlp->pfnSSMGetMem(     pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
-
-    if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
-        memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
-               (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
-
-    pHlp->pfnSSMGetU32(     pSSM, &pThis->cUnicastFilterMacs);
-    AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
-    pHlp->pfnSSMGetMem(     pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
-
-    if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
-        memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
-               (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
-
-    rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
-    AssertRCReturn(rc, rc);
-    /*
-     * Call the virtio core to let it load its state.
-     */
-    rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion,
-                                          VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
-    AssertRCReturn(rc, rc);
-    /*
-     * Since the control queue is created proactively in the constructor to accomodate worst-case
-     * legacy guests, even though the queue may have been deducted from queue count while saving state,
-     * we must explicitly remove queue and associated worker thread and context at this point,
-     * or presence of bogus control queue will confuse operations.
-     */
-    PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[CTRLQIDX];
-    if (FEATURE_DISABLED(CTRL_VQ) || !virtioCoreIsVirtqEnabled(&pThis->Virtio, CTRLQIDX))
-    {
-        virtioCoreR3VirtqDetach(&pThis->Virtio, CTRLQIDX);
-        virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
-        pVirtq->fAttachedToVirtioCore = false;
-        --pThis->cWorkers;
-    }
-    /*
-     * Nudge queue workers
-     */
-    for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-    {
-        pVirtq  = &pThis->aVirtqs[uVirtqNbr];
-        if (pVirtq->fAttachedToVirtioCore)
-        {
-            if (pVirtq->fHasWorker)
-            {
-                PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
-                Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
-                rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
-                AssertRCReturn(rc, rc);
-            }
-        }
-    }
-    pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus; /* reflects state to guest driver */
-    pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
-    virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
-    return rc;
-}
-
-/**
- * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
- *                      loading.}
- */
-static DECLCALLBACK(int) virtioNetR3ModernLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
-{
-    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-    RT_NOREF(pSSM);
-
-    if (pThisCC->pDrv)
-        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulticast));
-
-    /*
-     * Indicate link down to the guest OS that all network connections have
-     * been lost, unless we've been teleported here.
-     */
-    if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
-        virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
-
-    return VINF_SUCCESS;
-}
-
-/**
- * @callback_method_impl{FNSSMDEVSAVEEXEC}
- */
-static DECLCALLBACK(int) virtioNetR3ModernSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
-{
-    PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-    PCPDMDEVHLPR3  pHlp    = pDevIns->pHlpR3;
-
-    RT_NOREF(pThisCC);
-    Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
-
-    /* Store a dummy MAC address that would never be actually assigned to a NIC
-     * so that when load exec handler is called it can be easily determined
-     * whether saved state is modern or legacy. This works because original
-     * legacy code stored assigned NIC address as the first item of SSM state
-     */
-    RTMAC uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
-    pHlp->pfnSSMPutMem(pSSM, &uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8));
-
-    pHlp->pfnSSMPutU64(     pSSM, pThis->fNegotiatedFeatures);
-
-    pHlp->pfnSSMPutU16(     pSSM, pThis->cVirtqs);
-    pHlp->pfnSSMPutU16(     pSSM, pThis->cWorkers);
-
-    for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-        pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
-    /*
-
-     * Save device config area (accessed via MMIO)
-     */
-    pHlp->pfnSSMPutMem(     pSSM, pThis->virtioNetConfig.uMacAddress.au8,
-                            sizeof(pThis->virtioNetConfig.uMacAddress.au8));
-#if FEATURE_OFFERED(STATUS)
-    pHlp->pfnSSMPutU16(     pSSM, pThis->virtioNetConfig.uStatus);
-#else
-    /*
-     * Relevant values are lower bits. Forcing this to 0xffff let's loadExec know this
-     * feature was not enabled in saved state. VirtIO 1.0, 5.1.4
-     */
-    pHlp->pfnSSMPutU16(     pSSM, 0xffff);
-
-#endif
-#if FEATURE_OFFERED(MQ)
-    pHlp->pfnSSMPutU16(     pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
-#else
-    /*
-     * Legal values for max_virtqueue_pairs are 0x1 -> 0x8000 *. Forcing zero let's loadExec know this
-     * feature was not enabled in saved state. VirtIO 1.0, 5.1.4.1
-     */
-    pHlp->pfnSSMPutU16(     pSSM, 0);
-#endif
-
-    /* Save device-specific part */
-    pHlp->pfnSSMPutBool(    pSSM, pThis->fCableConnected);
-    pHlp->pfnSSMPutU8(      pSSM, pThis->fPromiscuous);
-    pHlp->pfnSSMPutU8(      pSSM, pThis->fAllMulticast);
-    pHlp->pfnSSMPutU8(      pSSM, pThis->fAllUnicast);
-    pHlp->pfnSSMPutU8(      pSSM, pThis->fNoMulticast);
-    pHlp->pfnSSMPutU8(      pSSM, pThis->fNoUnicast);
-    pHlp->pfnSSMPutU8(      pSSM, pThis->fNoBroadcast);
-
-    pHlp->pfnSSMPutU32(     pSSM, pThis->cMulticastFilterMacs);
-    pHlp->pfnSSMPutMem(     pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
-
-    pHlp->pfnSSMPutU32(     pSSM, pThis->cUnicastFilterMacs);
-    pHlp->pfnSSMPutMem(     pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
-
-    int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
-    AssertRCReturn(rc, rc);
-
-    /*
-     * Call the virtio core to let it save its state.
-     */
-    return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
-}
-
-
-/*********************************************************************************************************************************
-*   Device interface.                                                                                                            *
-*********************************************************************************************************************************/
-
-#ifdef IN_RING3
-
-/**
- * Perform 16-bit 1's compliment checksum on provided packet in accordance with VirtIO specification,
- * pertinent to VIRTIO_NET_F_CSUM feature, which 'offloads' the Checksum feature from the driver
- * to save processor cycles, which is ironic in our case, where the controller device ('network card')
- * is emulated on the virtualization host.
- *
- * @note See VirtIO 1.0 spec, 5.1.6.2 Packet Transmission
- *
- * @param pBuf          Pointer to r/w buffer with any portion to calculate checksum for
- * @param cbSize        Number of bytes to checksum
- * @param uStart        Where to start the checksum within the buffer
- * @param uOffset       Offset past uStart point in the buffer to store checksum result
- *
- */
-DECLINLINE(void) virtioNetR3Calc16BitChecksum(uint8_t *pBuf, size_t cb, uint16_t uStart, uint16_t uOffset)
-{
-    AssertReturnVoid(uStart < cb);
-    AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cb);
-
-    uint32_t  chksum = 0;
-    uint16_t *pu = (uint16_t *)(pBuf + uStart);
-
-    cb -= uStart;
-    while (cb > 1)
-    {
-        chksum += *pu++;
-        cb -= 2;
-    }
-    if (cb)
-        chksum += *(uint8_t *)pu;
-    while (chksum >> 16)
-        chksum = (chksum >> 16) + (chksum & 0xFFFF);
-
-    /* Store 1's compliment of calculated sum */
-    *(uint16_t *)(pBuf + uStart + uOffset) = ~chksum;
-}
-
-/**
- * Turns on/off the read status LED.
- *
- * @returns VBox status code.
- * @param   pThis          Pointer to the device state structure.
- * @param   fOn             New LED state.
- */
-void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
-{
-    if (fOn)
-        pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
-    else
-        pThisR3->led.Actual.s.fReading = fOn;
-}
-
-/**
- * Turns on/off the write status LED.
- *
- * @returns VBox status code.
- * @param   pThis          Pointer to the device state structure.
- * @param   fOn            New LED state.
- */
-void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
-{
-    if (fOn)
-        pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
-    else
-        pThisR3->led.Actual.s.fWriting = fOn;
-}
-
-/**
- * Check that the core is setup and ready and co-configured with guest virtio driver,
- * and verifies that the VM is running.
- *
- * @returns true if VirtIO core and device are in a running and operational state
- */
-DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
-{
-    if (RT_LIKELY(pThis->fVirtioReady))
-    {
-        VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
-        if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
-            return true;
-    }
-    return false;
-}
-
-/**
- * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
- * available. This must be called before the pfnRecieve() method is called.
- *
- * @remarks As a side effect this function enables queue notification
- *          if it cannot receive because the queue is empty.
- *          It disables notification if it can receive.
- *
- * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
- * @thread  RX
- */
-static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
-{
-    int rc = VERR_INVALID_STATE;
-    Log8Func(("[%s] ", pThis->szInst));
-    if (!virtioNetIsOperational(pThis, pDevIns))
-        Log8(("No Rx bufs available. (VirtIO core not ready)\n"));
-
-    else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
-        Log8(("[No Rx bufs available. (%s not enabled)\n", pRxVirtq->szName));
-
-    else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio,  pRxVirtq->uIdx))
-        Log8(("No Rx bufs available. (%s empty)\n", pRxVirtq->szName));
-
-    else
-    {
-        Log8(("%s has %d empty guest bufs in avail ring\n", pRxVirtq->szName,
-                virtioCoreVirtqAvailBufCount(pDevIns,  &pThis->Virtio, pRxVirtq->uIdx)));
-        rc = VINF_SUCCESS;
-    }
-    virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
-    return rc;
-}
-
-/**
- * Find an Rx queue that has Rx packets in it, if *any* do.
- *
- * @todo When multiqueue (MQ) mode is fully supported and tested, some kind of round-robin
- *       or randomization scheme should probably be incorporated here.
- *
- * @returns true if Rx pkts avail on queue and sets pRxVirtq to point to queue w/pkts found
- * @thread  RX
- *
- */
-static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
-{
-    for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
-    {
-        PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
-        if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
-        {
-            if (pRxVirtq)
-                *pRxVirtq = pThisRxVirtq;
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
- */
-static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
-{
-    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
-    PPDMDEVINS   pDevIns = pThisCC->pDevIns;
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-
-    if (!virtioNetIsOperational(pThis, pDevIns))
-        return VERR_INTERRUPTED;
-
-    if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
-    {
-        Log10Func(("[%s] Rx bufs available, releasing waiter...\n", pThis->szInst));
-        return VINF_SUCCESS;
-    }
-    if (!timeoutMs)
-        return VERR_NET_NO_BUFFER_SPACE;
-
-    LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
-
-    ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
-    STAM_PROFILE_START(&pThis->StatRxOverflow, a);
-
-    do {
-        if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
-        {
-            Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
-            ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
-            return VINF_SUCCESS;
-        }
-        Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
-
-        int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
-
-        if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
-        {
-            LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
-
-            if (!virtioNetIsOperational(pThis, pDevIns))
-                break;
-
-            continue;
-        }
-        if (RT_FAILURE(rc)) {
-            LogFunc(("Waken due to failure %Rrc\n", rc));
-            RTThreadSleep(1);
-        }
-    } while (virtioNetIsOperational(pThis, pDevIns));
-
-    STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
-    ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
-
-    Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
-    return VERR_INTERRUPTED;
-}
-
-/**
- * Sets up the GSO context according to the Virtio header.
- *
- * @param   pGso                The GSO context to setup.
- * @param   pCtx                The context descriptor.
- */
-DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
-{
-    pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
-
-    if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
-    {
-        AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
-        return NULL;
-    }
-    switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
-    {
-        case VIRTIONET_HDR_GSO_TCPV4:
-            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
-            pGso->cbHdrsSeg = pPktHdr->uHdrLen;
-            break;
-        case VIRTIONET_HDR_GSO_TCPV6:
-            pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
-            pGso->cbHdrsSeg = pPktHdr->uHdrLen;
-            break;
-        case VIRTIONET_HDR_GSO_UDP:
-            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
-            pGso->cbHdrsSeg = pPktHdr->uChksumStart;
-            break;
-        default:
-            return NULL;
-    }
-    if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
-        pGso->offHdr2  = pPktHdr->uChksumStart;
-    else
-    {
-        AssertMsgFailed(("GSO without checksum offloading!\n"));
-        return NULL;
-    }
-    pGso->offHdr1     = sizeof(RTNETETHERHDR);
-    pGso->cbHdrsTotal = pPktHdr->uHdrLen;
-    pGso->cbMaxSeg    = pPktHdr->uGsoSize;
-    /* Mark GSO frames with zero MSS as PDMNETWORKGSOTYPE_INVALID, so they will be ignored by send. */
-    if (pPktHdr->uGsoType != VIRTIONET_HDR_GSO_NONE && pPktHdr->uGsoSize == 0)
-        pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
-    return pGso;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
- */
-static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
-{
-    PVIRTIONETCC    pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
-    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
-    memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
-    return VINF_SUCCESS;
-}
-
-/**
- * Returns true if it is a broadcast packet.
- *
- * @returns true if destination address indicates broadcast.
- * @param   pvBuf           The ethernet packet.
- */
-DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
-{
-    static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-    return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
-}
-
-/**
- * Returns true if it is a multicast packet.
- *
- * @remarks returns true for broadcast packets as well.
- * @returns true if destination address indicates multicast.
- * @param   pvBuf           The ethernet packet.
- */
-DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
-{
-    return (*(char*)pvBuf) & 1;
-}
-
-/**
- * Determines if the packet is to be delivered to upper layer.
- *
- * @returns true if packet is intended for this node.
- * @param   pThis          Pointer to the state structure.
- * @param   pvBuf          The ethernet packet.
- * @param   cb             Number of bytes available in the packet.
- */
-static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
-{
-
-RT_NOREF(cb);
-
-#ifdef LOG_ENABLED
-    if (LogIs11Enabled())
-    {
-        char *pszType;
-        if (virtioNetR3IsMulticast(pvBuf))
-            pszType = (char *)"mcast";
-        else if (virtioNetR3IsBroadcast(pvBuf))
-            pszType = (char *)"bcast";
-        else
-            pszType = (char *)"ucast";
-
-        LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
-                 pThis->virtioNetConfig.uMacAddress.au8,
-                 pThis->fPromiscuous ? " promisc" : "",
-                 pThis->fAllMulticast ? " all-mcast" : "",
-                 pvBuf,  pszType));
-    }
-#endif
-
-    if (pThis->fPromiscuous) {
-        Log11(("\n"));
-        return true;
-    }
-
-    /* Ignore everything outside of our VLANs */
-    uint16_t *uPtr = (uint16_t *)pvBuf;
-
-    /* Compare TPID with VLAN Ether Type */
-    if (   uPtr[6] == RT_H2BE_U16(0x8100)
-        && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
-    {
-        Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
-        return false;
-    }
-
-    if (virtioNetR3IsBroadcast(pvBuf))
-    {
-        Log11(("acpt (bcast)\n"));
-#ifdef LOG_ENABLED
-        if (LogIs12Enabled())
-            virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
-#endif
-        return true;
-    }
-    if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
-    {
-        Log11(("acpt (all-mcast)\n"));
-#ifdef LOG_ENABLED
-        if (LogIs12Enabled())
-            virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
-#endif
-        return true;
-    }
-
-    if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
-    {
-        Log11(("acpt (to-node)\n"));
-#ifdef LOG_ENABLED
-        if (LogIs12Enabled())
-            virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
-#endif
-        return true;
-    }
-
-    for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
-    {
-        if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
-        {
-            Log11(("acpt (mcast whitelist)\n"));
-#ifdef LOG_ENABLED
-            if (LogIs12Enabled())
-                virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
-#endif
-            return true;
-        }
-    }
-
-    for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
-        if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
-        {
-            Log11(("acpt (ucast whitelist)\n"));
-            return true;
-        }
-#ifdef LOG_ENABLED
-    if (LogIs11Enabled())
-        Log(("... reject\n"));
-#endif
-
-    return false;
-}
-
-
-/**
- * This handles the case where Rx packet must be transfered to guest driver via multiple buffers using
- * copy tactics slower than preferred method using a single virtq buf. Yet this is an available option
- * for guests. Although cited in the spec it's to accomodate guest that perhaps have memory constraints
- * wherein guest may benefit from smaller buffers (see MRG_RXBUF feature), in practice it is seen
- * that without MRG_RXBUF the linux guest enqueues 'huge' multi-segment buffers so that the largest
- * conceivable Rx packet can be contained in a single buffer, where for most transactions most of that
- * memory will be unfilled, so it is typically both wasteful and *slower* to avoid MRG_RXBUF.
- *
- * As an optimization, this multi-buffer copy is only used when:
- *
- *     A. Guest has negotiated MRG_RXBUF
- *     B. Next packet in the Rx avail queue isn't big enough to contain Rx pkt hdr+data.
- *
- * Architecture is defined in VirtIO 1.1 5.1.6 (Device Operations), which has improved
- * wording over the VirtIO 1.0 specification, but, as an implementation note, there is one
- * ambiguity that needs clarification:
- *
- *    The VirtIO 1.1, 5.1.6.4 explains something in a potentially misleading way. And note,
- *    the VirtIO spec makes a document-wide assertion that the distinction between
- *    "SHOULD" and "MUST" is to be taken quite literally.
- *
- *    The confusion is that VirtIO 1.1, 5.1.6.3.1 essentially says guest driver "SHOULD" populate
- *    Rx queue with buffers large enough to accomodate full pkt hdr + data. That's a grammatical
- *    error (dangling participle).
- *
- *    In practice we MUST assume "SHOULD" strictly applies to the word *populate*, -not- to buffer
- *    size, because ultimately buffer minimum size is predicated on configuration parameters,
- *    specifically, when MRG_RXBUF feature is disabled, the driver *MUST* provide Rx bufs
- *    (if and when it can provide them), that are *large enough* to hold pkt hdr + payload.
- *
- *    Therefore, proper interpretation of 5.1.6.3.1 is, the guest *should* (ideally) keep Rx virtq
- *    populated with appropriately sized buffers to *prevent starvation* (i.e. starvation may be
- *    unavoidable thus can't be prohibited). As it would be a ludicrous to presume 5.1.6.3.1 is
- *    giving guests leeway to violate MRG_RXBUF feature buf size constraints.
- *
- * @param pDevIns               PDM instance
- * @param pThis                 Device instance
- * @param pvBuf                 Pointer to incoming GSO Rx data from downstream device
- * @param cb                    Amount of data given
- * @param rxPktHdr              Rx pkt Header that's been massaged into VirtIO semantics
- * @param pRxVirtq              Pointer to Rx virtq
- * @param pVirtqBuf             Initial virtq buffer to start copying Rx hdr/pkt to guest into
- *
- */
-static int virtioNetR3RxPktMultibufXfer(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint8_t *pvPktBuf, size_t cb,
-                                       PVIRTIONETPKTHDR pRxPktHdr, PVIRTIONETVIRTQ pRxVirtq, PVIRTQBUF pVirtqBuf)
-{
-
-    size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
-    size_t cbPktHdr = pThis->cbPktHdr;
-
-    AssertMsgReturn(cbBufRemaining >= pThis->cbPktHdr,
-                    ("guest-provided Rx buf not large enough to store pkt hdr"), VERR_INTERNAL_ERROR);
-
-    Log7Func(("  Sending packet header to guest...\n"));
-
-    /* Copy packet header to rx buf provided by caller. */
-    size_t cbHdrEnqueued = pVirtqBuf->cbPhysReturn == cbPktHdr ? cbPktHdr : 0;
-    virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, cbHdrEnqueued);
-
-    /* Cache address of uNumBuffers field of pkthdr to update ex post facto */
-    RTGCPHYS GCPhysNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
-    uint16_t cVirtqBufsUsed = 0;
-    cbBufRemaining -= cbPktHdr;
-    /*
-     * Copy packet to guest using as many buffers as necessary, tracking and handling whether
-     * the buf containing the packet header was already written to the Rx queue's used buffer ring.
-     */
-    uint64_t uPktOffset = 0;
-    while(uPktOffset < cb)
-    {
-        Log7Func(("  Sending packet data (in buffer #%d) to guest...\n", cVirtqBufsUsed));
-        size_t cbBounded = RT_MIN(cbBufRemaining, cb - uPktOffset);
-        (void) virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbBounded,
-                                    pvPktBuf + uPktOffset, pVirtqBuf, cbBounded + (cbPktHdr - cbHdrEnqueued) /* cbEnqueue */);
-        ++cVirtqBufsUsed;
-        cbBufRemaining -= cbBounded;
-        uPktOffset += cbBounded;
-        if (uPktOffset < cb)
-        {
-            cbHdrEnqueued = cbPktHdr;
-#ifdef VIRTIO_VBUF_ON_STACK
-            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
-#else /* !VIRTIO_VBUF_ON_STACK */
-            virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
-            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-
-            AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
-
-#ifdef VIRTIO_VBUF_ON_STACK
-            AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
-                            ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
-                            VERR_INTERNAL_ERROR);
-#else /* !VIRTIO_VBUF_ON_STACK */
-            AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
-                                ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
-                                virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
-                                VERR_INTERNAL_ERROR);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-            cbBufRemaining = pVirtqBuf->cbPhysReturn;
-        }
-    }
-
-    /* Fix-up pkthdr (in guest phys. memory) with number of buffers (descriptors) that were processed */
-    int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysNumBuffers, &cVirtqBufsUsed, sizeof(cVirtqBufsUsed));
-    AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
-
-#ifndef VIRTIO_VBUF_ON_STACK
-    virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-    virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
-    Log7(("\n"));
-    return rc;
-}
-
-/**
- * Pad and store received packet.
- *
- * @remarks Make sure that the packet appears to upper layer as one coming
- *          from real Ethernet: pad it and insert FCS.
- *
- * @returns VBox status code.
- * @param   pDevIns         The device instance.
- * @param   pThis           The virtio-net shared instance data.
- * @param   pvBuf           The available data.
- * @param   cb              Number of bytes available in the buffer.
- * @param   pGso            Pointer to Global Segmentation Offload structure
- * @param   pRxVirtq        Pointer to Rx virtqueue
- * @thread  RX
- */
-
-static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, const void *pvBuf, size_t cb,
-                                       PVIRTIONETPKTHDR pRxPktHdr, uint8_t cbPktHdr, PVIRTIONETVIRTQ pRxVirtq)
-{
-    RT_NOREF(pThisCC);
-#ifdef VIRTIO_VBUF_ON_STACK
-    VIRTQBUF_T VirtqBuf;
-
-    VirtqBuf.u32Magic  = VIRTQBUF_MAGIC;
-    VirtqBuf.cRefs     = 1;
-
-    PVIRTQBUF pVirtqBuf = &VirtqBuf;
-    int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
-#else /* !VIRTIO_VBUF_ON_STACK */
-    PVIRTQBUF pVirtqBuf;
-    int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-
-    AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
-
-#ifdef VIRTIO_VBUF_ON_STACK
-    AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
-                    ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
-                    VERR_INTERNAL_ERROR);
-#else /* !VIRTIO_VBUF_ON_STACK */
-    AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
-                        ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
-                        virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
-                        VERR_INTERNAL_ERROR);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-    /*
-     * Try to do fast (e.g. single-buffer) copy to guest, even if MRG_RXBUF feature is enabled
-     */
-    STAM_PROFILE_START(&pThis->StatReceiveStore, a);
-    if (RT_LIKELY(FEATURE_DISABLED(MRG_RXBUF))
-        || RT_LIKELY(pVirtqBuf->cbPhysReturn > cb + cbPktHdr))
-    {
-        Log7Func(("Send Rx packet header and data to guest (single-buffer copy)...\n"));
-        pRxPktHdr->uNumBuffers = 1;
-        rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr,  pRxPktHdr, pVirtqBuf, 0  /* cbEnqueue */);
-        if (rc == VINF_SUCCESS)
-            rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cb, pvBuf, pVirtqBuf, cbPktHdr + cb /* cbEnqueue */);
-#ifndef VIRTIO_VBUF_ON_STACK
-        virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-        virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
-        AssertMsgReturn(rc == VINF_SUCCESS, ("%Rrc\n", rc), rc);
-    }
-    else
-    {
-        Log7Func(("Send Rx pkt to guest (merged-buffer copy [MRG_RXBUF feature])...\n"));
-        rc = virtioNetR3RxPktMultibufXfer(pDevIns, pThis, (uint8_t *)pvBuf, cb, pRxPktHdr, pRxVirtq, pVirtqBuf);
-        return rc;
-    }
-    STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
-    return VINF_SUCCESS;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
- */
-static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(
-                                 PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso)
-{
-    PVIRTIONETCC    pThisCC  = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
-    PPDMDEVINS      pDevIns  = pThisCC->pDevIns;
-    PVIRTIONET      pThis    = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    VIRTIONETPKTHDR rxPktHdr = { 0, VIRTIONET_HDR_GSO_NONE, 0, 0, 0, 0, 0 };
-
-    if (!pThis->fVirtioReady)
-    {
-        LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
-        return VERR_INTERRUPTED;
-    }
-    /*
-     * If GSO (Global Segment Offloading) was received from downstream PDM network device, massage the
-     * PDM-provided GSO parameters into VirtIO semantics, which get passed to guest virtio-net via
-     * Rx pkt header.  See VirtIO 1.1, 5.1.6 Device Operation for more information.
-     */
-    if (pGso)
-    {
-        LogFunc(("[%s] (%RTmac) \n", pThis->szInst, pvBuf));
-
-        rxPktHdr.uFlags        = VIRTIONET_HDR_F_NEEDS_CSUM;
-        rxPktHdr.uHdrLen       = pGso->cbHdrsTotal;
-        rxPktHdr.uGsoSize      = pGso->cbMaxSeg;
-        rxPktHdr.uChksumStart  = pGso->offHdr2;
-
-        switch (pGso->u8Type)
-        {
-            case PDMNETWORKGSOTYPE_IPV4_TCP:
-                rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
-                rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
-                break;
-            case PDMNETWORKGSOTYPE_IPV6_TCP:
-                rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
-                rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
-                break;
-            case PDMNETWORKGSOTYPE_IPV4_UDP:
-                rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
-                rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
-                break;
-            default:
-                LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
-                return VERR_NOT_SUPPORTED;
-        }
-        STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
-        Log2Func(("[%s] gso type=%#x, cbHdrsTotal=%u cbHdrsSeg=%u mss=%u offHdr1=%#x offHdr2=%#x\n",
-                        pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
-                        pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
-    }
-
-    /*
-     * Find a virtq with Rx bufs on avail ring, if any, and copy the packet to the guest's Rx buffer.
-     * @todo pk: PROBABLY NOT A SOPHISTICATED ENOUGH QUEUE SELECTION ALGORTITH FOR OPTIMAL MQ (FEATURE) SUPPORT
-     */
-    for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
-    {
-        PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
-        if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
-        {
-            int rc = VINF_SUCCESS;
-            STAM_PROFILE_START(&pThis->StatReceive, a);
-            virtioNetR3SetReadLed(pThisCC, true);
-            if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
-            {
-                /* rxPktHdr is local stack variable that should not go out of scope in this use */
-                rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pThisCC, pvBuf, cb, &rxPktHdr, pThis->cbPktHdr, pRxVirtq);
-                STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
-            }
-            virtioNetR3SetReadLed(pThisCC, false);
-            STAM_PROFILE_STOP(&pThis->StatReceive, a);
-            return rc;
-        }
-    }
-    return VERR_INTERRUPTED;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
- */
-static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
-{
-
-#ifdef LOG_ENABLED
-    PVIRTIONETCC    pThisCC  = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
-    PPDMDEVINS      pDevIns  = pThisCC->pDevIns;
-    PVIRTIONET      pThis    = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    LogFunc(("[%s] (%RTmac)\n", pThis->szInst, pvBuf));
-#endif
-
-    return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
-}
-
-/*
- * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's Rx packet receive filtering.
- * See VirtIO 1.0, 5.1.6.5.1
- *
- * @param pThis         virtio-net instance
- * @param pCtrlPktHdr   Control packet header (which includes command parameters)
- * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
- */
-static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
-                                 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
-{
-
-#define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
-
-    LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
-    switch(pCtrlPktHdr->uCmd)
-    {
-      case VIRTIONET_CTRL_RX_PROMISC:
-        break;
-      case VIRTIONET_CTRL_RX_ALLMULTI:
-        break;
-      case VIRTIONET_CTRL_RX_ALLUNI:
-        /* fallthrough */
-      case VIRTIONET_CTRL_RX_NOMULTI:
-        /* fallthrough */
-      case VIRTIONET_CTRL_RX_NOUNI:
-        /* fallthrough */
-      case VIRTIONET_CTRL_RX_NOBCAST:
-        AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
-                        ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
-                        VIRTIONET_ERROR);
-        /* fall out */
-    }
-
-    uint8_t fOn, fPromiscChanged = false;
-    virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
-
-    switch(pCtrlPktHdr->uCmd)
-    {
-      case VIRTIONET_CTRL_RX_PROMISC:
-        pThis->fPromiscuous = RT_BOOL(fOn);
-        fPromiscChanged = true;
-        LOG_VIRTIONET_FLAG(fPromiscuous);
-        break;
-      case VIRTIONET_CTRL_RX_ALLMULTI:
-        pThis->fAllMulticast = RT_BOOL(fOn);
-        fPromiscChanged = true;
-        LOG_VIRTIONET_FLAG(fAllMulticast);
-        break;
-      case VIRTIONET_CTRL_RX_ALLUNI:
-        pThis->fAllUnicast = RT_BOOL(fOn);
-        LOG_VIRTIONET_FLAG(fAllUnicast);
-        break;
-      case VIRTIONET_CTRL_RX_NOMULTI:
-        pThis->fNoMulticast = RT_BOOL(fOn);
-        LOG_VIRTIONET_FLAG(fNoMulticast);
-        break;
-      case VIRTIONET_CTRL_RX_NOUNI:
-        pThis->fNoUnicast = RT_BOOL(fOn);
-        LOG_VIRTIONET_FLAG(fNoUnicast);
-        break;
-      case VIRTIONET_CTRL_RX_NOBCAST:
-        pThis->fNoBroadcast = RT_BOOL(fOn);
-        LOG_VIRTIONET_FLAG(fNoBroadcast);
-        break;
-    }
-
-    if (pThisCC->pDrv && fPromiscChanged)
-        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
-
-    return VIRTIONET_OK;
-}
-
-/*
- * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MAC filter tables
- * See VirtIO 1.0, 5.1.6.5.2
- *
- * @param pThis         virtio-net instance
- * @param pCtrlPktHdr   Control packet header (which includes command parameters)
- * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
- */
-static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
-{
-    LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
-
-
-    AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
-                   ("insufficient descriptor space for ctrl pkt hdr"),
-                   VIRTIONET_ERROR);
-
-    size_t cbRemaining = pVirtqBuf->cbPhysSend;
-    switch(pCtrlPktHdr->uCmd)
-    {
-        case VIRTIONET_CTRL_MAC_ADDR_SET:
-        {
-            /* Set default Rx filter MAC */
-            AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
-                            ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
-
-            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(RTMAC));
-            break;
-        }
-        case VIRTIONET_CTRL_MAC_TABLE_SET:
-        {
-            VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
-
-            /* Load unicast MAC filter table */
-            AssertMsgReturn(cbRemaining >= sizeof(cMacs),
-                           ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
-
-            /* Fetch count of unicast filter MACs from guest buffer */
-            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
-            cbRemaining -= sizeof(cMacs);
-
-            Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
-
-            if (cMacs)
-            {
-                uint32_t cbMacs = cMacs * sizeof(RTMAC);
-
-                AssertMsgReturn(cbMacs <= sizeof(pThis->aMacUnicastFilter)  / sizeof(RTMAC),
-                                ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
-
-                AssertMsgReturn(cbRemaining >= cbMacs,
-                                ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
-
-
-                /* Fetch unicast table contents from guest buffer */
-                virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
-                cbRemaining -= cbMacs;
-            }
-            pThis->cUnicastFilterMacs = cMacs;
-
-            /* Load multicast MAC filter table */
-            AssertMsgReturn(cbRemaining >= sizeof(cMacs),
-                            ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
-
-            /* Fetch count of multicast filter MACs from guest buffer */
-            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
-            cbRemaining -= sizeof(cMacs);
-
-            Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
-
-            if (cMacs)
-            {
-                uint32_t cbMacs = cMacs * sizeof(RTMAC);
-
-                AssertMsgReturn(cbMacs <= sizeof(pThis->aMacMulticastFilter)  / sizeof(RTMAC),
-                                ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
-
-                AssertMsgReturn(cbRemaining >= cbMacs,
-                                ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
-
-                /* Fetch multicast table contents from guest buffer */
-                virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
-                cbRemaining -= cbMacs;
-            }
-            pThis->cMulticastFilterMacs = cMacs;
-
-#ifdef LOG_ENABLED
-            LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
-            for(unsigned i = 0; i < pThis->cUnicastFilterMacs; i++)
-                LogFunc(("         %RTmac\n", &pThis->aMacUnicastFilter[i]));
-
-            LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
-            for(unsigned i = 0; i < pThis->cMulticastFilterMacs; i++)
-                LogFunc(("         %RTmac\n", &pThis->aMacMulticastFilter[i]));
-#endif
-            break;
-        }
-        default:
-            LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
-            return VIRTIONET_ERROR;
-    }
-    return VIRTIONET_OK;
-}
-
-/*
- * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MQ (multiqueue) operations.
- * See VirtIO 1.0, 5.1.6.5.5
- *
- * @param pThis         virtio-net instance
- * @param pCtrlPktHdr   Control packet header (which includes command parameters)
- * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
- */
-static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
-{
-    LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
-
-    uint16_t cVirtqPairs;
-    switch(pCtrlPktHdr->uCmd)
-    {
-        case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
-        {
-            size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
-
-            AssertMsgReturn(cbRemaining > sizeof(cVirtqPairs),
-                ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
-
-            /* Fetch number of virtq pairs from guest buffer */
-            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
-
-            AssertMsgReturn(cVirtqPairs > VIRTIONET_MAX_QPAIRS,
-                ("[%s] Guest CTRL MQ virtq pair count out of range [%d])\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
-
-            LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
-            pThis->cVirtqPairs = cVirtqPairs;
-            break;
-        }
-        default:
-            LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
-            return VIRTIONET_ERROR;
-    }
-
-    /*
-     * The MQ control function is invoked by the guest in an RPC like manner to change
-     * the Rx/Tx queue pair count. If the new value exceeds the number of queues
-     * (and associated workers) already initialized initialize only the new queues and
-     * respective workers.
-     */
-    if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
-    {
-        virtioNetR3SetVirtqNames(pThis, virtioCoreIsLegacyMode(&pThis->Virtio));
-        int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
-        if (RT_FAILURE(rc))
-        {
-            LogRelFunc(("Failed to create worker threads\n"));
-            return VIRTIONET_ERROR;
-        }
-    }
-    return VIRTIONET_OK;
-}
-
-/*
- * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's VLAN filtering.
- * See VirtIO 1.0, 5.1.6.5.3
- *
- * @param pThis         virtio-net instance
- * @param pCtrlPktHdr   Control packet header (which includes command parameters)
- * @param pVirtqBuf     Buffer from ctrlq buffer (contains command data)
- */
-static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
-{
-    LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
-
-    uint16_t uVlanId;
-    size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
-
-    AssertMsgReturn(cbRemaining > sizeof(uVlanId),
-        ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
-
-    /* Fetch VLAN ID from guest buffer */
-    virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
-
-    AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
-        ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
-
-    LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
-
-    switch (pCtrlPktHdr->uCmd)
-    {
-        case VIRTIONET_CTRL_VLAN_ADD:
-            ASMBitSet(pThis->aVlanFilter, uVlanId);
-            break;
-        case VIRTIONET_CTRL_VLAN_DEL:
-            ASMBitClear(pThis->aVlanFilter, uVlanId);
-            break;
-        default:
-            LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
-            return VIRTIONET_ERROR;
-    }
-    return VIRTIONET_OK;
-}
-
-/**
- * Processes control command from guest.
- * See VirtIO 1.0 spec, 5.1.6 "Device Operation" and 5.1.6.5 "Control Virtqueue".
- *
- * The control command is contained in a virtio buffer pulled from the virtio-net defined control queue (ctrlq).
- * Command type is parsed is dispatched to a command-specific device-configuration handler function (e.g. RX, MAC, VLAN, MQ
- * and ANNOUNCE).
- *
- * This function handles all parts of the host-side of the ctrlq round-trip buffer processing.
- *
- * Invoked by worker for virtio-net control queue to process a queued control command buffer.
- *
- * @param pDevIns       PDM device instance
- * @param pThis         virtio-net device instance
- * @param pThisCC       virtio-net device instance
- * @param pVirtqBuf     pointer to buffer pulled from virtq (input to this function)
- */
-static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
-                            PVIRTQBUF pVirtqBuf)
-{
-    if (!(pThis->fNegotiatedFeatures & VIRTIONET_F_CTRL_VQ))
-        LogFunc(("[%s] WARNING: Guest using CTRL queue w/o negotiating VIRTIONET_F_CTRL_VQ feature\n", pThis->szInst));
-
-    LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
-
-    if (pVirtqBuf->cbPhysSend < 2)
-    {
-        LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
-        return;
-    }
-    else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
-    {
-        LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
-        return;
-    }
-
-    /*
-     * Allocate buffer and read in the control command
-     */
-    AssertMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(VIRTIONET_CTRL_HDR_T),
-                        ("DESC chain too small for CTRL pkt header"));
-
-    VIRTIONET_CTRL_HDR_T CtrlPktHdr; RT_ZERO(CtrlPktHdr);
-    virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &CtrlPktHdr,
-                              RT_MIN(pVirtqBuf->cbPhysSend, sizeof(CtrlPktHdr)));
-
-    Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, CtrlPktHdr.uClass, CtrlPktHdr.uCmd));
-
-    uint8_t uAck;
-    switch (CtrlPktHdr.uClass)
-    {
-        case VIRTIONET_CTRL_RX:
-            uAck = virtioNetR3CtrlRx(pThis, pThisCC, &CtrlPktHdr, pVirtqBuf);
-            break;
-        case VIRTIONET_CTRL_MAC:
-            uAck = virtioNetR3CtrlMac(pThis, &CtrlPktHdr, pVirtqBuf);
-            break;
-        case VIRTIONET_CTRL_VLAN:
-            uAck = virtioNetR3CtrlVlan(pThis, &CtrlPktHdr, pVirtqBuf);
-            break;
-        case VIRTIONET_CTRL_MQ:
-            uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, &CtrlPktHdr, pVirtqBuf);
-            break;
-        case VIRTIONET_CTRL_ANNOUNCE:
-            uAck = VIRTIONET_OK;
-            if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
-            {
-                LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
-                         "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
-                break;
-            }
-            if (CtrlPktHdr.uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
-            {
-                LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
-                break;
-            }
-#if FEATURE_OFFERED(STATUS)
-            pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
-#endif
-            Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
-            break;
-        default:
-            LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", CtrlPktHdr.uClass));
-            uAck = VIRTIONET_ERROR;
-    }
-
-    /* Return CTRL packet Ack byte (result code) to guest driver */
-    RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
-    RTSGBUF SgBuf;
-
-    RTSgBufInit(&SgBuf, aStaticSegs, RT_ELEMENTS(aStaticSegs));
-    virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, &SgBuf, pVirtqBuf, true /* fFence */);
-    virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
-
-    LogFunc(("%s Finished processing CTRL command with status %s\n",
-             pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
-}
-
-/**
- * Reads virtio-net pkt header from provided Phy. addr of virtio descriptor chain
- * (e.g. S/G segment from guest-driver provided buffer pulled from Tx virtq)
- * Verifies state and supported modes, sets TCP header size.
- *
- * @param pVirtio      VirtIO core instance data
- * @param pThis        virtio-net instance
- * @param pDevIns      PDM device instance
- * @param GCPhys       Phys. Address from where to read virtio-net pkt header
- * @param pPktHdr      Where to store read Tx pkt hdr (virtio pkt hdr size is determined from instance configuration)
- * @param cbFrame      Total pkt frame size to inform bounds check
- */
-static int virtioNetR3ReadVirtioTxPktHdr(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
-{
-    int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
-          pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
-          pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
-
-    if (pPktHdr->uGsoType)
-    {
-        /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
-        AssertMsgReturn(    RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
-                         && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
-                         ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
-
-        uint32_t uTcpHdrSize;
-        switch (pPktHdr->uGsoType)
-        {
-            case VIRTIONET_HDR_GSO_TCPV4:
-            case VIRTIONET_HDR_GSO_TCPV6:
-                uTcpHdrSize = sizeof(RTNETTCP);
-                break;
-            case VIRTIONET_HDR_GSO_UDP:
-                uTcpHdrSize = 0;
-                break;
-            default:
-                LogFunc(("Bad GSO type in packet header\n"));
-                return VERR_INVALID_PARAMETER;
-        }
-        /* Header + MSS must not exceed the packet size. */
-        AssertMsgReturn(RT_LIKELY(uTcpHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
-                    ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
-    }
-
-    AssertMsgReturn( !(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
-                    || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
-                 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
-                 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
-                 VERR_BUFFER_OVERFLOW);
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Transmits single GSO frame via PDM framework to downstream PDM device, to emit from virtual NIC.
- *
- * This does final prep of GSO parameters including checksum calculation if configured
- * (e.g. if VIRTIONET_HDR_F_NEEDS_CSUM flag is set).
- *
- * @param pThis         virtio-net instance
- * @param pThisCC       virtio-net instance
- * @param pSgBuf        PDM S/G buffer containing pkt and hdr to transmit
- * @param pGso          GSO parameters used for the packet
- * @param pPktHdr       virtio-net pkt header to adapt to PDM semantics
- */
-static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
-                               PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
-{
-
-    virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
-    if (pGso)
-    {
-        /* Some guests (RHEL) may report HdrLen excluding transport layer header!
-         * Thus cannot use cdHdrs provided by the guest because of different ways
-         * it gets filled out by different versions of kernels. */
-        Log4Func(("%s HdrLen before adjustment %d.\n", pThis->szInst, pGso->cbHdrsTotal));
-        switch (pGso->u8Type)
-        {
-            case PDMNETWORKGSOTYPE_IPV4_TCP:
-            case PDMNETWORKGSOTYPE_IPV6_TCP:
-                pGso->cbHdrsTotal = pPktHdr->uChksumStart +
-                    ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
-                AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
-                    ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
-                pGso->cbHdrsSeg   = pGso->cbHdrsTotal;
-                break;
-            case PDMNETWORKGSOTYPE_IPV4_UDP:
-                pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
-                pGso->cbHdrsSeg = pPktHdr->uChksumStart;
-                break;
-            case PDMNETWORKGSOTYPE_INVALID:
-                LogFunc(("%s ignoring invalid GSO frame\n", pThis->szInst));
-                return VERR_INVALID_PARAMETER;
-        }
-        /* Update GSO structure embedded into the frame */
-        ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
-        ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg   = pGso->cbHdrsSeg;
-        Log4Func(("%s adjusted HdrLen to %d.\n",
-              pThis->szInst, pGso->cbHdrsTotal));
-        Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
-                  pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
-                  pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
-        STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
-    }
-    else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
-    {
-        STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
-        /*
-         * This is not GSO frame but checksum offloading is requested.
-         */
-        virtioNetR3Calc16BitChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
-                             pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
-    }
-
-    return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
-}
-
-/**
- * Non-reentrant function transmits all available packets from specified Tx virtq to downstream
- * PDM device (if cable is connected). For each Tx pkt, virtio-net pkt header is converted
- * to required GSO information (VBox host network stack semantics)
- *
- * @param pDevIns           PDM device instance
- * @param pThis             virtio-net device instance
- * @param pThisCC           virtio-net device instance
- * @param pTxVirtq          Address of transmit virtq
- * @param fOnWorkerThread   Flag to PDM whether to use caller's or or PDM transmit worker's thread.
- */
-static int virtioNetR3TransmitPkts(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
-                                         PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
-{
-    PVIRTIOCORE pVirtio = &pThis->Virtio;
-
-
-    if (!pThis->fVirtioReady)
-    {
-        LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
-                pThis->szInst, pThis->virtioNetConfig.uStatus));
-        return VERR_IGNORED;
-    }
-
-    if (!pThis->fCableConnected)
-    {
-        Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
-        return VERR_IGNORED;
-    }
-
-    /*
-     * Only one thread is allowed to transmit at a time, others should skip transmission as the packets
-     * will be picked up by the transmitting thread.
-     */
-    if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
-        return VERR_IGNORED;
-
-    PPDMINETWORKUP pDrv = pThisCC->pDrv;
-    if (pDrv)
-    {
-        int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
-        Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
-        if (rc == VERR_TRY_AGAIN)
-        {
-            ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
-            return VERR_TRY_AGAIN;
-        }
-    }
-    int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
-    if (!cPkts)
-    {
-        LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
-
-        if (pDrv)
-            pDrv->pfnEndXmit(pDrv);
-
-        ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
-        return VERR_MISSING;
-    }
-    LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
-
-    virtioNetR3SetWriteLed(pThisCC, true);
-
-    /* Disable notifications until all available descriptors have been processed */
-    if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX))
-        virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, false /* fEnable */);
-
-    int rc;
-#ifdef VIRTIO_VBUF_ON_STACK
-    VIRTQBUF_T VirtqBuf;
-
-    VirtqBuf.u32Magic  = VIRTQBUF_MAGIC;
-    VirtqBuf.cRefs     = 1;
-
-    PVIRTQBUF pVirtqBuf = &VirtqBuf;
-    while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, pVirtqBuf)) == VINF_SUCCESS)
-#else /* !VIRTIO_VBUF_ON_STACK */
-    PVIRTQBUF pVirtqBuf = NULL;
-    while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, &pVirtqBuf)) == VINF_SUCCESS)
-#endif /* !VIRTIO_VBUF_ON_STACK */
-    {
-        Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
-
-        PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
-        PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
-        uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
-        size_t uFrameSize = 0;
-
-        AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
-                        ("Desc chain's first seg has insufficient space for pkt header!\n"),
-                        VERR_INTERNAL_ERROR);
-
-#ifdef VIRTIO_VBUF_ON_STACK
-        VIRTIONETPKTHDR PktHdr;
-        PVIRTIONETPKTHDR pPktHdr = &PktHdr;
-#else /* !VIRTIO_VBUF_ON_STACK */
-        PVIRTIONETPKTHDR pPktHdr = (PVIRTIONETPKTHDR)RTMemAllocZ(pThis->cbPktHdr);
-        AssertMsgReturn(pPktHdr, ("Out of Memory\n"), VERR_NO_MEMORY);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-
-        /* Compute total frame size from guest (including virtio-net pkt hdr) */
-        for (unsigned i = 0; i < cSegsFromGuest && uFrameSize < VIRTIONET_MAX_FRAME_SIZE; i++)
-            uFrameSize +=  paSegsFromGuest[i].cbSeg;
-
-        Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uFrameSize));
-        Assert(uFrameSize <= VIRTIONET_MAX_FRAME_SIZE);
-
-        /* Truncate oversized frames. */
-        if (uFrameSize > VIRTIONET_MAX_FRAME_SIZE)
-            uFrameSize = VIRTIONET_MAX_FRAME_SIZE;
-
-        if (pThisCC->pDrv)
-        {
-            uFrameSize -= pThis->cbPktHdr;
-            /*
-             * Peel off pkt header and convert to PDM/GSO semantics.
-             */
-            rc = virtioNetR3ReadVirtioTxPktHdr(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uFrameSize /* cbFrame */);
-            if (RT_FAILURE(rc))
-                return rc;
-            virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
-
-            PDMNETWORKGSO  Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
-
-            /* Allocate PDM transmit buffer to send guest provided network frame from to VBox network leaf device */
-            PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
-            rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uFrameSize, pGso, &pSgBufToPdmLeafDevice);
-
-            /*
-             * Copy virtio-net guest S/G buffer to PDM leaf driver S/G buffer
-             * converting from GCphys to virt memory at the same time
-             */
-            if (RT_SUCCESS(rc))
-            {
-                STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
-                STAM_PROFILE_START(&pThis->StatTransmitSend, a);
-
-                size_t cbCopied = 0;
-                size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uFrameSize;
-                uint64_t uOffset = 0;
-                while (cbRemain)
-                {
-                    PVIRTIOSGSEG paSeg  = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
-                    uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
-                    uint64_t srcSgLen   = (uint64_t)paSeg->cbSeg;
-                    uint64_t srcSgCur   = (uint64_t)pSgPhysSend->GCPhysCur;
-                    cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
-                    virtioCoreGCPhysRead(pVirtio, pDevIns,
-                                         (RTGCPHYS)pSgPhysSend->GCPhysCur,
-                                         ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
-                    virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
-                    cbRemain -= cbCopied;
-                    uOffset += cbCopied;
-                }
-
-                LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
-                     uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
-
-                rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
-                if (RT_FAILURE(rc))
-                {
-                    LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
-                    STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
-                    STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
-                    pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
-                }
-                STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
-                STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
-            }
-            else
-            {
-                Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
-                /* Stop trying to fetch TX descriptors until we get more bandwidth. */
-#ifndef VIRTIO_VBUF_ON_STACK
-                virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-                break;
-            }
-
-            virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
-
-            /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
-            virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
-            virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
-        }
-
-#ifndef VIRTIO_VBUF_ON_STACK
-        virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
-        pVirtqBuf = NULL;
-#endif /* !VIRTIO_VBUF_ON_STACK */
-        /* Before we break the loop we need to check if the queue is empty,
-         * re-enable notifications, and then re-check again to avoid missing
-         * a notification for the descriptor that is added to the queue
-         * after we have checked it on being empty, but before we re-enabled
-         * notifications.
-         */
-        if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
-            && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
-            virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
-    }
-    virtioNetR3SetWriteLed(pThisCC, false);
-
-    if (pDrv)
-        pDrv->pfnEndXmit(pDrv);
-
-    ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
-    return VINF_SUCCESS;
-}
-
-/**
- * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
- */
-static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
-{
-    LogFunc(("\n"));
-    PVIRTIONETCC    pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
-    PPDMDEVINS      pDevIns = pThisCC->pDevIns;
-    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
-    PVIRTIONETVIRTQ pTxVirtq  = &pThis->aVirtqs[TXQIDX(0)];
-    STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
-
-    (void)virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
-}
-
-/**
- * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
- */
-static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
-{
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-
-    SET_LINK_UP(pThis);
-    virtioNetWakeupRxBufWaiter(pDevIns);
-
-    if (pThisCC->pDrv)
-        pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
-
-    LogFunc(("[%s] Link is up\n", pThis->szInst));
-    RT_NOREF(hTimer, pvUser);
-}
-
-/**
- * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
- */
-static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
-{
-    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
-    PPDMDEVINS   pDevIns = pThisCC->pDevIns;
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-
-    bool fRequestedLinkStateIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
-
-#ifdef LOG_ENABLED
-    if (LogIs7Enabled())
-    {
-        LogFunc(("[%s]", pThis->szInst));
-        switch(enmState)
-        {
-        case PDMNETWORKLINKSTATE_UP:
-            Log(("UP\n"));
-            break;
-        case PDMNETWORKLINKSTATE_DOWN:
-            Log(("DOWN\n"));
-            break;
-        case PDMNETWORKLINKSTATE_DOWN_RESUME:
-            Log(("DOWN (RESUME)\n"));
-            break;
-        default:
-            Log(("UNKNOWN)\n"));
-        }
-    }
-#endif
-
-    if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
-    {
-        if (IS_LINK_UP(pThis))
-        {
-            /*
-             * We bother to bring the link down only if it was up previously. The UP link state
-             * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
-             */
-            virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
-            if (pThisCC->pDrv)
-                pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
-        }
-    }
-    else if (fRequestedLinkStateIsUp != IS_LINK_UP(pThis))
-    {
-        if (fRequestedLinkStateIsUp)
-        {
-            Log(("[%s] Link is up\n", pThis->szInst));
-            pThis->fCableConnected = true;
-            SET_LINK_UP(pThis);
-        }
-        else /* Link requested to be brought down */
-        {
-            /* The link was brought down explicitly, make sure it won't come up by timer.  */
-            PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
-            Log(("[%s] Link is down\n", pThis->szInst));
-            pThis->fCableConnected = false;
-            SET_LINK_DOWN(pThis);
-        }
-        if (pThisCC->pDrv)
-            pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
-    }
-    return VINF_SUCCESS;
-}
-/**
- * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
- */
-static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
-{
-    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
-    PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
-
-    return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
-}
-
-static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
-{
-    Log10Func(("[%s]\n", pThis->szInst));
-    int rc = VINF_SUCCESS;
-    for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
-    {
-        PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[uIdxWorker];
-        PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
-
-        if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
-        {
-            PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
-            pWorker->hEvtProcess = NIL_SUPSEMEVENT;
-        }
-        if (pWorkerR3->pThread)
-        {
-            int rcThread;
-            rc = PDMDevHlpThreadDestroy(pDevIns, pWorkerR3->pThread, &rcThread);
-            if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
-                AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
-            pWorkerR3->pThread = NULL;
-        }
-    }
-    return rc;
-}
-
-/**
- * Creates a worker for specified queue, along with semaphore to throttle the worker.
- *
- * @param pDevIns       - PDM device instance
- * @param pThis         - virtio-net instance
- * @param pWorker       - Pointer to worker state
- * @param pWorkerR3     - Pointer to worker state
- * @param pVirtq        - Pointer to virtq
- */
-static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis,
-                                            PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
-                                            PVIRTIONETVIRTQ pVirtq)
-{
-    Log10Func(("[%s]\n", pThis->szInst));
-    RT_NOREF(pThis);
-
-    int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess);
-
-    if (RT_FAILURE(rc))
-        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
-                                   N_("DevVirtioNET: Failed to create SUP event semaphore"));
-
-    LogFunc(("creating thread for queue %s\n", pVirtq->szName));
-
-    rc = PDMDevHlpThreadCreate(pDevIns, &pWorkerR3->pThread,
-                               (void *)pWorker, virtioNetR3WorkerThread,
-                               virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, pVirtq->szName);
-    if (RT_FAILURE(rc))
-        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
-                                   N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
-
-    pWorker->fAssigned = true;  /* Because worker's state in fixed-size array initialized w/empty slots */
-
-    LogFunc(("%s pThread: %p\n", pVirtq->szName, pWorkerR3->pThread));
-
-    return rc;
-}
-
-static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
-{
-    Log10Func(("[%s]\n", pThis->szInst));
-    int rc;
-
-    /* Create the Control Queue worker anyway whether or not it is feature-negotiated or utilized by the guest.
-     * See related comment for queue construction in the device constructor function for more context.
-     */
-
-    PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
-    rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis,
-                                          &pThis->aWorkers[CTRLQIDX], &pThisCC->aWorkers[CTRLQIDX], pCtlVirtq);
-    AssertRCReturn(rc, rc);
-
-    pCtlVirtq->fHasWorker = true;
-
-    for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
-    {
-        PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
-        PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
-
-        rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, &pThis->aWorkers[TXQIDX(uVirtqPair)],
-                                              &pThisCC->aWorkers[TXQIDX(uVirtqPair)], pTxVirtq);
-        AssertRCReturn(rc, rc);
-
-        pTxVirtq->fHasWorker = true;
-        pRxVirtq->fHasWorker = false;
-    }
-
-    if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
-        pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
-
-    pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
-
-    return rc;
-}
-
-
-/**
- * @callback_method_impl{FNPDMTHREADDEV}
- */
-static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
-{
-    PVIRTIONET         pThis     = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC       pThisCC   = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-    PVIRTIONETWORKER   pWorker   = (PVIRTIONETWORKER)pThread->pvUser;
-    PVIRTIONETVIRTQ    pVirtq    = &pThis->aVirtqs[pWorker->uIdx];
-    uint16_t           uIdx      = pWorker->uIdx;
-
-    ASMAtomicWriteBool(&pWorker->fSleeping, false);
-
-    Assert(pWorker->uIdx == pVirtq->uIdx);
-
-    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
-        return VINF_SUCCESS;
-
-    LogFunc(("[%s] worker thread idx=%d started for %s (virtq idx=%d)\n", pThis->szInst,  pWorker->uIdx, pVirtq->szName, pVirtq->uIdx));
-
-    /** @todo Race w/guest enabling/disabling guest notifications cyclically.
-              See BugRef #8651, Comment #82 */
-    virtioCoreVirtqEnableNotify(&pThis->Virtio, uIdx, true /* fEnable */);
-
-    while (   pThread->enmState != PDMTHREADSTATE_TERMINATING
-           && pThread->enmState != PDMTHREADSTATE_TERMINATED)
-    {
-        if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio,  pVirtq->uIdx))
-        {
-            /*  Precisely coordinated atomic interlocks avoid a race condition that results in hung thread
-             *  wherein a sloppily coordinated wake-up notification during a transition into or out
-             * of sleep leaves notifier and target mutually confused about actual & intended state.
-             */
-            ASMAtomicWriteBool(&pWorker->fSleeping, true);
-            bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
-            if (!fNotificationSent)
-            {
-                Log10Func(("[%s] %s worker sleeping...\n\n", pThis->szInst, pVirtq->szName));
-                Assert(ASMAtomicReadBool(&pWorker->fSleeping));
-
-                int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
-                STAM_COUNTER_INC(&pThis->StatTransmitByThread);
-                AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
-                if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
-                    return VINF_SUCCESS;
-                if (rc == VERR_INTERRUPTED)
-                    continue;
-               ASMAtomicWriteBool(&pWorker->fNotified, false);
-            }
-            ASMAtomicWriteBool(&pWorker->fSleeping, false);
-        }
-        /*
-         * Dispatch to the handler for the queue this worker is set up to drive
-         */
-        if (pVirtq->fCtlVirtq)
-        {
-            Log10Func(("[%s] %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
-#ifdef VIRTIO_VBUF_ON_STACK
-            VIRTQBUF_T VirtqBuf;
-            PVIRTQBUF pVirtqBuf = &VirtqBuf;
-            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, pVirtqBuf, true);
-#else /* !VIRTIO_VBUF_ON_STACK */
-            PVIRTQBUF pVirtqBuf = NULL;
-            int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, &pVirtqBuf, true);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-            if (rc == VERR_NOT_AVAILABLE)
-            {
-                Log10Func(("[%s] %s worker woken. Nothing found in queue\n", pThis->szInst, pVirtq->szName));
-                continue;
-            }
-            virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
-#ifndef VIRTIO_VBUF_ON_STACK
-            virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
-#endif /* !VIRTIO_VBUF_ON_STACK */
-        }
-        else /* Must be Tx queue */
-        {
-            Log10Func(("[%s] %s worker woken. Virtq has data to transmit\n",  pThis->szInst, pVirtq->szName));
-            virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
-        }
-        /* Note: Surprise! Rx queues aren't handled by local worker threads. Instead, the PDM network leaf driver
-         * invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback, which waits until woken by virtioNetVirtqNotified()
-         * indicating that guest IN buffers have been added to Rx virt queue.
-         */
-    }
-    Log10(("[%s] %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
-    return VINF_SUCCESS;
-}
-
-/**
- * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
- *
- * Called back by the core code when VirtIO's ready state has changed.
- */
-static DECLCALLBACK(void) virtioNetR3StatusChg(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
-{
-    PVIRTIONET     pThis     = RT_FROM_MEMBER(pVirtio,  VIRTIONET, Virtio);
-    PVIRTIONETCC   pThisCC   = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
-
-    pThis->fVirtioReady = fVirtioReady;
-
-    if (fVirtioReady)
-    {
-#ifdef LOG_ENABLED
-        Log(("\n%-23s: %s *** VirtIO Ready ***\n\n", __FUNCTION__, pThis->szInst));
-        virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
-#endif
-        pThis->fResetting = false;
-        pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(pVirtio);
-        /* Now we can properly figure out the size of virtio header! */
-        virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
-        pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
-
-        for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-        {
-            PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
-            PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
-
-            Assert(pWorker->uIdx == uVirtqNbr);
-            RT_NOREF(pWorker);
-
-            Assert(pVirtq->uIdx == pWorker->uIdx);
-
-            (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
-            pVirtq->fAttachedToVirtioCore = true;
-            if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
-                virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
-        }
-
-        virtioNetWakeupRxBufWaiter(pThisCC->pDevIns);
-    }
-    else
-    {
-        Log(("\n%-23s: %s VirtIO is resetting ***\n", __FUNCTION__, pThis->szInst));
-
-        pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
-        Log7(("%-23s: %s Link is %s\n", __FUNCTION__, pThis->szInst, pThis->fCableConnected ? "up" : "down"));
-
-        pThis->fPromiscuous         = true;
-        pThis->fAllMulticast        = false;
-        pThis->fAllUnicast          = false;
-        pThis->fNoMulticast         = false;
-        pThis->fNoUnicast           = false;
-        pThis->fNoBroadcast         = false;
-        pThis->uIsTransmitting      = 0;
-        pThis->cUnicastFilterMacs   = 0;
-        pThis->cMulticastFilterMacs = 0;
-
-        memset(pThis->aMacMulticastFilter,  0, sizeof(pThis->aMacMulticastFilter));
-        memset(pThis->aMacUnicastFilter,    0, sizeof(pThis->aMacUnicastFilter));
-        memset(pThis->aVlanFilter,          0, sizeof(pThis->aVlanFilter));
-
-        pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
-
-        for (uint16_t uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-        {
-            virtioCoreR3VirtqDetach(&pThis->Virtio, uVirtqNbr);
-            pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore = false;
-        }
-    }
-}
-
-/**
- * @callback_method_impl{VIRTIOCORER3,pfnFeatureNegotiationComplete}
- */
-static DECLCALLBACK(void) pfnFeatureNegotiationComplete(PVIRTIOCORE pVirtio, uint64_t fDriverFeatures, uint32_t fLegacy)
-{
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pVirtio->pDevInsR3, PVIRTIONET);
-
-    LogFunc(("[Feature Negotiation Complete] Guest Driver version is: %s\n", fLegacy ? "legacy" : "modern"));
-    virtioNetConfigurePktHdr(pThis, fLegacy);
-    virtioNetR3SetVirtqNames(pThis, fLegacy);
-
-    /* Senseless for modern guest to use control queue in this case. (See Note 1 in PDM-invoked device constructor) */
-    if (!fLegacy && !(fDriverFeatures & VIRTIONET_F_CTRL_VQ))
-        virtioNetR3VirtqDestroy(pVirtio, &pThis->aVirtqs[CTRLQIDX]);
-}
-
-#endif /* IN_RING3 */
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnDetach}
- *
- * The VM is suspended at this point.
- */
-static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
-{
-    RT_NOREF(fFlags);
-
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-
-    Log7Func(("[%s]\n", pThis->szInst));
-    RT_NOREF(pThis);
-
-    AssertLogRelReturnVoid(iLUN == 0);
-
-    pThisCC->pDrvBase = NULL;
-    pThisCC->pDrv     = NULL;
-}
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnAttach}
- *
- * This is called when we change block driver.
- */
-static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
-{
-    PVIRTIONET       pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC     pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-
-    Log7Func(("[%s]", pThis->szInst));
-    AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
-
-    int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
-    if (RT_SUCCESS(rc))
-    {
-        pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
-        AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
-                      rc = VERR_PDM_MISSING_INTERFACE_BELOW);
-    }
-    else if (   rc == VERR_PDM_NO_ATTACHED_DRIVER
-             || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
-                    Log(("[%s] No attached driver!\n", pThis->szInst));
-
-    RT_NOREF2(pThis, fFlags);
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
- */
-static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
-{
-    PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
-    if (iLUN)
-        return VERR_PDM_LUN_NOT_FOUND;
-    *ppLed = &pThisR3->led;
-    return VINF_SUCCESS;
-}
-
-/**
- * @interface_method_impl{PDMIBASE,pfnQueryInterface}
- */
-static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
-{
-    PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN,   &pThisCC->INetworkDown);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE,          &pThisCC->IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS,      &pThisCC->ILeds);
-    return NULL;
-}
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnReset}
- */
-static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
-{
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-
-    virtioCoreR3ResetDevice(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
-}
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
- */
-static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
-{
-    PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
-
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-
-    Log(("[%s] Destroying instance\n", pThis->szInst));
-    if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
-    {
-        PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
-        PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
-        pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
-    }
-
-    virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
-    virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
-    return VINF_SUCCESS;
-}
-
-/**
- * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
- *
- * Notes about revising originally VirtIO 1.0+ only virtio-net device emulator to be "transitional",
- * a VirtIO term meaning this now interoperates with both "legacy" (e.g. pre-1.0) and "modern" (1.0+)
- * guest virtio-net drivers. The changes include migrating VMs saved using prior DevVirtioNet.cpp (0.95)
- * saveExec/loadExec semantics to use 1.0 save/load semantics.
- *
- * Regardless of the 1.0 spec's overall helpful guidance for implementing transitional devices,
- * A bit is left to the imagination, e.g. some things have to be determined deductively
- * (AKA "the hard way").
- *
- * Case in point: According to VirtIO 0.95 ("legacy") specification, section 2.2.1, "historically"
- * drivers may start driving prior to feature negotiation and prior to drivers setting DRIVER_OK
- * status, "provided driver doesn't use features that alter early use of this device".  Interpreted
- * here to mean a virtio-net driver must respect default settings (such as implicit pkt header default
- * size, as determined per Note 1 below).
- *
- * ----------------------------------------------------------------------------------------------
- * Transitional device initialization Note 1:  Identifying default value for network Rx pkt hdr size.
- * (VirtIO 1.0 specification section 5.1.6.1)
- *
- * Guest virtio legacy drivers may begin operations prematurely, regardless of early spec's
- * initialization sequence (see note 2 below). Legacy drivers implicitly default to using the
- * (historically) shortest-length network packet header *unless* VIRTIONET_F_MRG_RXBUF feature is
- * negotiated. If feature negotiation phase is [optionally] enacted by a legacy guest (i.e. we strictly
- * enforce full initialization protocol for modern guests), virtioNetConfigurePktHdr() is invoked again to
- * finalize device's network packet header size. Best-guess at default packet header size is deduced, e.g.
- * isn't documented, as follows: A legacy guest with VIRTIONET_F_MRG_RXBUF not-yet-negotiated is the only
- * case where network I/O could possibly occur with any reasonable assumption about packet type/size,
- * because logically other permutations couldn't possibly be inferred until feature negotiation
- * is complete. Specifically, those cases are:
- *
- * 1. A modern driver (detected only when VIRTIONET_F_VERSION_1 feature is ack'd by guest, and,
- * simultaneously, VIRTIONET_F_MRG_RXBUF feature is accepted or declined (determining network receive-packet
- * processing behavior).
- *
- * 2. A legacy driver that has agreed to use VIRTIONET_F_MRG_RXBUF feature, resulting in a two-byte larger pkt hdr,
- * (as well as deciding Rx packet processing behavior).
- *
- * ----------------------------------------------------------------------------------------------
- * Transitional device initialization Note 2:  Creating unnegotiated control queue.
- * (VirtIO 1.0 spec, sections 5.1.5 and 5.1.6.5)
- *
- * Create all queues immediately, prior to feature negotiation, including control queue (irrespective
- * of the fact it's too early in initialization for control feature to be approved by guest). This
- * transitional device must deal with legacy guests which *can* (and on linux have been seen to) use
- * the control queue prior to feature negotiation.
- *
- * The initial assumption is *modern" guest virtio-net drivers out in the wild could never reasonably
- * attempt something as obviously risky as using ctrlq without first acking VIRTIO_NET_F_CTRL_VQ
- * feature to establish it. For now, we create the control queue proactively to accomodate a potentially
- * badly behaved but officially sanctioned legacy virtio-net driver, but *destroy* that same queue
- * if a driver announces as 'modern' during feature finalization yet leaves VIRTIO_NET_F_CTRL_VQ un-ack'd.
- */
-static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
-{
-    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-    PCPDMDEVHLPR3 pHlp   = pDevIns->pHlpR3;
-
-    /*
-     * Quickly initialize state data to ensure destructor always works.
-     */
-    Log7Func(("PDM device instance: %d\n", iInstance));
-    RTStrPrintf(pThis->szInst, sizeof(pThis->szInst), "virtio-net #%d", iInstance);
-
-    pThisCC->pDevIns                          = pDevIns;
-    pThisCC->IBase.pfnQueryInterface          = virtioNetR3QueryInterface;
-    pThisCC->ILeds.pfnQueryStatusLed          = virtioNetR3QueryStatusLed;
-    pThisCC->led.u32Magic = PDMLED_MAGIC;
-
-    /* Interfaces */
-    pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
-    pThisCC->INetworkDown.pfnReceive          = virtioNetR3NetworkDown_Receive;
-    pThisCC->INetworkDown.pfnReceiveGso       = virtioNetR3NetworkDown_ReceiveGso;
-    pThisCC->INetworkDown.pfnXmitPending      = virtioNetR3NetworkDown_XmitPending;
-    pThisCC->INetworkConfig.pfnGetMac         = virtioNetR3NetworkConfig_GetMac;
-    pThisCC->INetworkConfig.pfnGetLinkState   = virtioNetR3NetworkConfig_GetLinkState;
-    pThisCC->INetworkConfig.pfnSetLinkState   = virtioNetR3NetworkConfig_SetLinkState;
-
-    pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
-
-    /*
-     * Validate configuration.
-     */
-    PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo|Legacy", "");
-
-    /* Get config params */
-    int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
-
-    rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
-
-    uint32_t uStatNo = iInstance;
-    rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
-
-    rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
-
-    Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
-
-    if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
-        LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
-                pThis->szInst, pThis->cMsLinkUpDelay / 1000));
-
-    Log(("[%s] Link up delay is set to %u seconds\n", pThis->szInst, pThis->cMsLinkUpDelay / 1000));
-
-    /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
-    memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
-
-    Log(("Using MAC address for %s: %2x:%2x:%2x:%2x:%2x:%2x\n", pThis->szInst,
-            pThis->macConfigured.au8[0], pThis->macConfigured.au8[1], pThis->macConfigured.au8[2],
-            pThis->macConfigured.au8[3], pThis->macConfigured.au8[4], pThis->macConfigured.au8[5]));
-
-    LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
-
-    /*
-     * Configure Virtio core (generic Virtio queue and infrastructure management) parameters.
-     */
-#   if FEATURE_OFFERED(STATUS)
-        pThis->virtioNetConfig.uStatus = 0;
-#   endif
-
-    pThis->virtioNetConfig.uMaxVirtqPairs          = VIRTIONET_MAX_QPAIRS;
-    pThisCC->Virtio.pfnFeatureNegotiationComplete  = pfnFeatureNegotiationComplete;
-    pThisCC->Virtio.pfnVirtqNotified               = virtioNetVirtqNotified;
-    pThisCC->Virtio.pfnStatusChanged               = virtioNetR3StatusChg;
-    pThisCC->Virtio.pfnDevCapRead                  = virtioNetR3DevCapRead;
-    pThisCC->Virtio.pfnDevCapWrite                 = virtioNetR3DevCapWrite;
-
-    VIRTIOPCIPARAMS VirtioPciParams;
-    VirtioPciParams.uDeviceId                      = PCI_DEVICE_ID_VIRTIONET_HOST;
-    VirtioPciParams.uClassBase                     = PCI_CLASS_BASE_NETWORK_CONTROLLER;
-    VirtioPciParams.uClassSub                      = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
-    VirtioPciParams.uClassProg                     = PCI_CLASS_PROG_UNSPECIFIED;
-    VirtioPciParams.uSubsystemId                   = DEVICE_PCI_NETWORK_SUBSYSTEM;  /* VirtIO 1.0 allows PCI Device ID here */
-    VirtioPciParams.uInterruptLine                 = 0x00;
-    VirtioPciParams.uInterruptPin                  = 0x01;
-
-    /* Create semaphore used to synchronize/throttle the downstream LUN's Rx waiter thread. */
-    rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
-
-    pThis->fOfferLegacy = VIRTIONET_TRANSITIONAL_ENABLE_FLAG;
-    virtioNetConfigurePktHdr(pThis, pThis->fOfferLegacy); /* set defaults */
-
-    /* Initialize VirtIO core. (*pfnStatusChanged)() callback occurs when both host VirtIO core & guest driver are ready) */
-    rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
-                          VIRTIONET_HOST_FEATURES_OFFERED, pThis->fOfferLegacy,
-                          &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
-
-    pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
-    /** @todo validating features at this point is most probably pointless, as the negotiation hasn't started yet. */
-    if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
-    pThis->cVirtqPairs = pThis->virtioNetConfig.uMaxVirtqPairs;
-    pThis->cVirtqs += pThis->cVirtqPairs * 2 + 1;
-    pThis->aVirtqs[CTRLQIDX].fCtlVirtq = true;
-
-    virtioNetR3SetVirtqNames(pThis, pThis->fOfferLegacy);
-    for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
-    {
-        PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
-        PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
-        PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
-        pVirtq->uIdx = pWorker->uIdx = pWorkerR3->uIdx = uVirtqNbr;
-    }
-    /*
-     * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
-     */
-    rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
-
-    /* Create Link Up Timer */
-    rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL,
-                              TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
-                              "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
-    /*
-     * Attach network driver instance
-     */
-    rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
-    if (RT_SUCCESS(rc))
-    {
-        pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
-        AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
-                      rc = VERR_PDM_MISSING_INTERFACE_BELOW);
-    }
-    else if (   rc == VERR_PDM_NO_ATTACHED_DRIVER
-             || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
-    {
-                    Log(("[%s] No attached driver!\n", pThis->szInst));
-                    AssertRCReturn(rc, rc);
-    }
-    /*
-     * Status driver
-     */
-    PPDMIBASE pUpBase;
-    rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
-    if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
-
-    pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
-    /*
-     * Register saved state.
-     */
-    rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIONET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
-                                NULL, NULL, NULL, /** @todo r=aeichner Teleportation? */
-                                NULL, virtioNetR3ModernSaveExec, NULL,
-                                NULL, virtioNetR3ModernLoadExec, virtioNetR3ModernLoadDone);
-    AssertRCReturn(rc, rc);
-    /*
-     * Statistics and debug stuff.
-     * The /Public/ bits are official and used by session info in the GUI.
-     */
-    PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes,  STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
-                           "Amount of data received",    "/Public/NetAdapter/%u/BytesReceived", uStatNo);
-    PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
-                           "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
-    PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance,       STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
-                           "Device instance number",     "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
-
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes,        STAMTYPE_COUNTER, "ReceiveBytes",           STAMUNIT_BYTES,          "Amount of data received");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes,       STAMTYPE_COUNTER, "TransmitBytes",          STAMUNIT_BYTES,          "Amount of data transmitted");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO,          STAMTYPE_COUNTER, "Packets/ReceiveGSO",     STAMUNIT_COUNT,          "Number of received GSO packets");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets,     STAMTYPE_COUNTER, "Packets/Transmit",       STAMUNIT_COUNT,          "Number of sent packets");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO,         STAMTYPE_COUNTER, "Packets/Transmit-Gso",   STAMUNIT_COUNT,          "Number of sent GSO packets");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum,        STAMTYPE_COUNTER, "Packets/Transmit-Csum",  STAMUNIT_COUNT,          "Number of completed TX checksums");
-# ifdef VBOX_WITH_STATISTICS
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive,             STAMTYPE_PROFILE, "Receive/Total",          STAMUNIT_TICKS_PER_CALL, "Profiling receive");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore,        STAMTYPE_PROFILE, "Receive/Store",          STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow,          STAMTYPE_PROFILE, "RxOverflow",             STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup,    STAMTYPE_COUNTER, "RxOverflowWakeup",       STAMUNIT_OCCURENCES,     "Nr of RX overflow wakeups");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit,            STAMTYPE_PROFILE, "Transmit/Total",         STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend,        STAMTYPE_PROFILE, "Transmit/Send",          STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork,   STAMTYPE_COUNTER, "Transmit/ByNetwork",     STAMUNIT_COUNT,          "Network-initiated transmissions");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread,    STAMTYPE_COUNTER, "Transmit/ByThread",      STAMUNIT_COUNT,          "Thread-initiated transmissions");
-# endif
-    /*
-     * Register the debugger info callback (ignore errors).
-     */
-    char szTmp[128];
-    rc = PDMDevHlpDBGFInfoRegister(pDevIns, "virtio-net", "Display virtio-net info (help, net, features, state, pointers, queues, all)", virtioNetR3Info);
-    if (RT_FAILURE(rc))
-        LogRel(("Failed to register DBGF info for device %s\n", szTmp));
-    return rc;
-}
-
-#else  /* !IN_RING3 */
-
-/**
- * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
- */
-static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
-{
-    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
-    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
-    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
-    pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
-    return virtioCoreRZInit(pDevIns, &pThis->Virtio);
-}
-
-#endif /* !IN_RING3 */
-
-/**
- * The device registration structure.
- */
-const PDMDEVREG g_DeviceVirtioNet =
-{
-    /* .uVersion = */               PDM_DEVREG_VERSION,
-    /* .uReserved0 = */             0,
-    /* .szName = */                 "virtio-net",
-    /* .fFlags = */                 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
-    /* .fClass = */                 PDM_DEVREG_CLASS_NETWORK,
-    /* .cMaxInstances = */          ~0U,
-    /* .uSharedVersion = */         42,
-    /* .cbInstanceShared = */       sizeof(VIRTIONET),
-    /* .cbInstanceCC = */           sizeof(VIRTIONETCC),
-    /* .cbInstanceRC = */           sizeof(VIRTIONETRC),
-    /* .cMaxPciDevices = */         1,
-    /* .cMaxMsixVectors = */        VBOX_MSIX_MAX_ENTRIES,
-    /* .pszDescription = */         "Virtio Host NET.\n",
-#if defined(IN_RING3)
-    /* .pszRCMod = */               "VBoxDDRC.rc",
-    /* .pszR0Mod = */               "VBoxDDR0.r0",
-    /* .pfnConstruct = */           virtioNetR3Construct,
-    /* .pfnDestruct = */            virtioNetR3Destruct,
-    /* .pfnRelocate = */            NULL,
-    /* .pfnMemSetup = */            NULL,
-    /* .pfnPowerOn = */             NULL,
-    /* .pfnReset = */               virtioNetR3Reset,
-    /* .pfnSuspend = */             virtioNetWakeupRxBufWaiter,
-    /* .pfnResume = */              NULL,
-    /* .pfnAttach = */              virtioNetR3Attach,
-    /* .pfnDetach = */              virtioNetR3Detach,
-    /* .pfnQueryInterface = */      NULL,
-    /* .pfnInitComplete = */        NULL,
-    /* .pfnPowerOff = */            virtioNetWakeupRxBufWaiter,
-    /* .pfnSoftReset = */           NULL,
-    /* .pfnReserved0 = */           NULL,
-    /* .pfnReserved1 = */           NULL,
-    /* .pfnReserved2 = */           NULL,
-    /* .pfnReserved3 = */           NULL,
-    /* .pfnReserved4 = */           NULL,
-    /* .pfnReserved5 = */           NULL,
-    /* .pfnReserved6 = */           NULL,
-    /* .pfnReserved7 = */           NULL,
-#elif defined(IN_RING0)
-    /* .pfnEarlyConstruct = */      NULL,
-    /* .pfnConstruct = */           virtioNetRZConstruct,
-    /* .pfnDestruct = */            NULL,
-    /* .pfnFinalDestruct = */       NULL,
-    /* .pfnRequest = */             NULL,
-    /* .pfnReserved0 = */           NULL,
-    /* .pfnReserved1 = */           NULL,
-    /* .pfnReserved2 = */           NULL,
-    /* .pfnReserved3 = */           NULL,
-    /* .pfnReserved4 = */           NULL,
-    /* .pfnReserved5 = */           NULL,
-    /* .pfnReserved6 = */           NULL,
-    /* .pfnReserved7 = */           NULL,
-#elif defined(IN_RC)
-    /* .pfnConstruct = */           virtioNetRZConstruct,
-    /* .pfnReserved0 = */           NULL,
-    /* .pfnReserved1 = */           NULL,
-    /* .pfnReserved2 = */           NULL,
-    /* .pfnReserved3 = */           NULL,
-    /* .pfnReserved4 = */           NULL,
-    /* .pfnReserved5 = */           NULL,
-    /* .pfnReserved6 = */           NULL,
-    /* .pfnReserved7 = */           NULL,
-#else
-# error "Not in IN_RING3, IN_RING0 or IN_RC!"
-#endif
-    /* .uVersionEnd = */          PDM_DEVREG_VERSION
-};
-
Index: unk/src/VBox/Devices/VirtIO/Virtio.cpp
===================================================================
--- /trunk/src/VBox/Devices/VirtIO/Virtio.cpp	(revision 98063)
+++ 	(revision )
@@ -1,962 +1,0 @@
-/* $Id$ */
-/** @file
- * Virtio - Virtio Common Functions (VRing, VQueue, Virtio PCI)
- */
-
-/*
- * Copyright (C) 2009-2022 Oracle and/or its affiliates.
- *
- * This file is part of VirtualBox base platform packages, as
- * available from https://www.virtualbox.org.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation, in version 3 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses>.
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-
-/*********************************************************************************************************************************
-*   Header Files                                                                                                                 *
-*********************************************************************************************************************************/
-#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
-
-#include <iprt/param.h>
-#include <iprt/uuid.h>
-#include <VBox/vmm/pdmdev.h>
-#include <VBox/AssertGuest.h>
-#include "Virtio.h"
-
-
-/*********************************************************************************************************************************
-*   Defined Constants And Macros                                                                                                 *
-*********************************************************************************************************************************/
-#define INSTANCE(pThis) (pThis->szInstance)
-
-
-static void vqueueReset(PVQUEUE pQueue)
-{
-    pQueue->VRing.addrDescriptors = 0;
-    pQueue->VRing.addrAvail       = 0;
-    pQueue->VRing.addrUsed        = 0;
-    pQueue->uNextAvailIndex       = 0;
-    pQueue->uNextUsedIndex        = 0;
-    pQueue->uPageNumber           = 0;
-}
-
-static void vqueueInit(PVQUEUE pQueue, uint32_t uPageNumber)
-{
-    pQueue->VRing.addrDescriptors = (uint64_t)uPageNumber << GUEST_PAGE_SHIFT;
-    pQueue->VRing.addrAvail       = pQueue->VRing.addrDescriptors + sizeof(VRINGDESC) * pQueue->VRing.uSize;
-    pQueue->VRing.addrUsed        = RT_ALIGN(pQueue->VRing.addrAvail + RT_UOFFSETOF_DYN(VRINGAVAIL, auRing[pQueue->VRing.uSize]),
-                                             GUEST_PAGE_SIZE); /* The used ring must start from the next page. */
-    pQueue->uNextAvailIndex       = 0;
-    pQueue->uNextUsedIndex        = 0;
-}
-
-// void vqueueElemFree(PVQUEUEELEM pElem)
-// {
-// }
-
-static void vringReadDesc(PPDMDEVINS pDevIns, PVRING pVRing, uint32_t uIndex, PVRINGDESC pDesc)
-{
-    //Log(("%s vringReadDesc: ring=%p idx=%u\n", INSTANCE(pThis), pVRing, uIndex));
-    PDMDevHlpPhysRead(pDevIns,
-                      pVRing->addrDescriptors + sizeof(VRINGDESC) * (uIndex % pVRing->uSize),
-                      pDesc, sizeof(VRINGDESC));
-    /** @todo r=bird: Why exactly are we sometimes using PDMDevHlpPhysRead rather
-     *        than PDMDevHlpPCIPhysRead? */
-}
-
-static uint16_t vringReadAvail(PPDMDEVINS pDevIns, PVRING pVRing, uint32_t uIndex)
-{
-    uint16_t tmp = 0;
-    PDMDevHlpPhysRead(pDevIns, pVRing->addrAvail + RT_UOFFSETOF_DYN(VRINGAVAIL, auRing[uIndex % pVRing->uSize]),
-                      &tmp, sizeof(tmp));
-    return tmp;
-}
-
-static uint16_t vringReadAvailFlags(PPDMDEVINS pDevIns, PVRING pVRing)
-{
-    uint16_t tmp = 0;
-    PDMDevHlpPhysRead(pDevIns, pVRing->addrAvail + RT_UOFFSETOF(VRINGAVAIL, uFlags), &tmp, sizeof(tmp));
-    return tmp;
-}
-
-void vringSetNotification(PPDMDEVINS pDevIns, PVRING pVRing, bool fEnabled)
-{
-    uint16_t fState = 0;
-    PDMDevHlpPhysRead(pDevIns, pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uFlags), &fState, sizeof(fState));
-
-    if (fEnabled)
-        fState &= ~ VRINGUSED_F_NO_NOTIFY;
-    else
-        fState |= VRINGUSED_F_NO_NOTIFY;
-
-    PDMDevHlpPCIPhysWrite(pDevIns, pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uFlags), &fState, sizeof(fState));
-}
-
-bool vqueueSkip(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue)
-{
-    if (vqueueIsEmpty(pDevIns, pQueue))
-        return false;
-
-    Log2(("%s vqueueSkip: %s avail_idx=%u\n", INSTANCE(pThis), pQueue->szName, pQueue->uNextAvailIndex));
-    RT_NOREF(pThis);
-    pQueue->uNextAvailIndex++;
-    return true;
-}
-
-bool vqueueGet(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, bool fRemove)
-{
-    if (vqueueIsEmpty(pDevIns, pQueue))
-        return false;
-
-    pElem->cIn = pElem->cOut = 0;
-
-    Log2(("%s vqueueGet: %s avail_idx=%u\n", INSTANCE(pThis), pQueue->szName, pQueue->uNextAvailIndex));
-
-    VRINGDESC desc;
-    uint16_t  idx = vringReadAvail(pDevIns, &pQueue->VRing, pQueue->uNextAvailIndex);
-    if (fRemove)
-        pQueue->uNextAvailIndex++;
-    pElem->uIndex = idx;
-    do
-    {
-        VQUEUESEG *pSeg;
-
-        /*
-         * Malicious guests may try to trick us into writing beyond aSegsIn or
-         * aSegsOut boundaries by linking several descriptors into a loop. We
-         * cannot possibly get a sequence of linked descriptors exceeding the
-         * total number of descriptors in the ring (see @bugref{8620}).
-         */
-        if (pElem->cIn + pElem->cOut >= VRING_MAX_SIZE)
-        {
-            static volatile uint32_t s_cMessages  = 0;
-            static volatile uint32_t s_cThreshold = 1;
-            if (ASMAtomicIncU32(&s_cMessages) == ASMAtomicReadU32(&s_cThreshold))
-            {
-                LogRel(("%s: too many linked descriptors; check if the guest arranges descriptors in a loop.\n",
-                        INSTANCE(pThis)));
-                if (ASMAtomicReadU32(&s_cMessages) != 1)
-                    LogRel(("%s: (the above error has occured %u times so far)\n",
-                            INSTANCE(pThis), ASMAtomicReadU32(&s_cMessages)));
-                ASMAtomicWriteU32(&s_cThreshold, ASMAtomicReadU32(&s_cThreshold) * 10);
-            }
-            break;
-        }
-        RT_UNTRUSTED_VALIDATED_FENCE();
-
-        vringReadDesc(pDevIns, &pQueue->VRing, idx, &desc);
-        if (desc.u16Flags & VRINGDESC_F_WRITE)
-        {
-            Log2(("%s vqueueGet: %s IN  seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pThis),
-                  pQueue->szName, pElem->cIn, idx, desc.u64Addr, desc.uLen));
-            pSeg = &pElem->aSegsIn[pElem->cIn++];
-        }
-        else
-        {
-            Log2(("%s vqueueGet: %s OUT seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pThis),
-                  pQueue->szName, pElem->cOut, idx, desc.u64Addr, desc.uLen));
-            pSeg = &pElem->aSegsOut[pElem->cOut++];
-        }
-
-        pSeg->addr = desc.u64Addr;
-        pSeg->cb   = desc.uLen;
-        pSeg->pv   = NULL;
-
-        idx = desc.u16Next;
-    } while (desc.u16Flags & VRINGDESC_F_NEXT);
-
-    Log2(("%s vqueueGet: %s head_desc_idx=%u nIn=%u nOut=%u\n", INSTANCE(pThis),
-          pQueue->szName, pElem->uIndex, pElem->cIn, pElem->cOut));
-    return true;
-}
-
-#ifdef LOG_ENABLED
-static uint16_t vringReadUsedIndex(PPDMDEVINS pDevIns, PVRING pVRing)
-{
-    uint16_t tmp = 0;
-    PDMDevHlpPhysRead(pDevIns, pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uIndex), &tmp, sizeof(tmp));
-    return tmp;
-}
-#endif
-
-static void vringWriteUsedIndex(PPDMDEVINS pDevIns, PVRING pVRing, uint16_t u16Value)
-{
-    PDMDevHlpPCIPhysWrite(pDevIns,
-                          pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uIndex),
-                          &u16Value, sizeof(u16Value));
-}
-
-static void vringWriteUsedElem(PPDMDEVINS pDevIns, PVRING pVRing, uint32_t uIndex, uint32_t uId, uint32_t uLen)
-{
-    VRINGUSEDELEM elem;
-
-    elem.uId = uId;
-    elem.uLen = uLen;
-    PDMDevHlpPCIPhysWrite(pDevIns,
-                          pVRing->addrUsed + RT_UOFFSETOF_DYN(VRINGUSED, aRing[uIndex % pVRing->uSize]),
-                          &elem, sizeof(elem));
-}
-
-void vqueuePut(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uTotalLen, uint32_t uReserved)
-{
-    Log2(("%s vqueuePut: %s desc_idx=%u acb=%u (%u)\n", INSTANCE(pThis), pQueue->szName, pElem->uIndex, uTotalLen, uReserved));
-    RT_NOREF(pThis);
-
-    Assert(uReserved < uTotalLen);
-
-    uint32_t cbLen = uTotalLen - uReserved;
-    uint32_t cbSkip = uReserved;
-
-    for (unsigned i = 0; i < pElem->cIn && cbLen > 0; ++i)
-    {
-        if (cbSkip >= pElem->aSegsIn[i].cb) /* segment completely skipped? */
-        {
-            cbSkip -= pElem->aSegsIn[i].cb;
-            continue;
-        }
-
-        uint32_t cbSegLen = pElem->aSegsIn[i].cb - cbSkip;
-        if (cbSegLen > cbLen)   /* last segment only partially used? */
-            cbSegLen = cbLen;
-
-        /*
-         * XXX: We should assert pv != NULL, but we need to check and
-         * fix all callers first.
-         */
-        if (pElem->aSegsIn[i].pv != NULL)
-        {
-            Log2(("%s vqueuePut: %s used_idx=%u seg=%u addr=%RGp pv=%p cb=%u acb=%u\n", INSTANCE(pThis), pQueue->szName,
-                  pQueue->uNextUsedIndex, i, pElem->aSegsIn[i].addr, pElem->aSegsIn[i].pv, pElem->aSegsIn[i].cb, cbSegLen));
-
-            PDMDevHlpPCIPhysWrite(pDevIns,
-                                  pElem->aSegsIn[i].addr + cbSkip,
-                                  pElem->aSegsIn[i].pv,
-                                  cbSegLen);
-        }
-
-        cbSkip = 0;
-        cbLen -= cbSegLen;
-    }
-
-    Log2(("%s vqueuePut: %s used_idx=%u guest_used_idx=%u id=%u len=%u\n", INSTANCE(pThis), pQueue->szName,
-          pQueue->uNextUsedIndex, vringReadUsedIndex(pDevIns, &pQueue->VRing), pElem->uIndex, uTotalLen));
-
-    vringWriteUsedElem(pDevIns, &pQueue->VRing,
-                       pQueue->uNextUsedIndex++,
-                       pElem->uIndex, uTotalLen);
-}
-
-static void vqueueNotify(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue)
-{
-    uint16_t const fAvail = vringReadAvailFlags(pDevIns, &pQueue->VRing);
-    LogFlow(("%s vqueueNotify: %s availFlags=%x guestFeatures=%x vqueue is %sempty\n", INSTANCE(pThis), pQueue->szName,
-             fAvail, pThis->uGuestFeatures, vqueueIsEmpty(pDevIns, pQueue)?"":"not "));
-    if (   !(fAvail & VRINGAVAIL_F_NO_INTERRUPT)
-        || ((pThis->uGuestFeatures & VPCI_F_NOTIFY_ON_EMPTY) && vqueueIsEmpty(pDevIns, pQueue)))
-    {
-        int rc = vpciRaiseInterrupt(pDevIns, pThis, VERR_INTERNAL_ERROR, VPCI_ISR_QUEUE);
-        if (RT_FAILURE(rc))
-            Log(("%s vqueueNotify: Failed to raise an interrupt (%Rrc).\n", INSTANCE(pThis), rc));
-    }
-    else
-        STAM_REL_COUNTER_INC(&pThis->StatIntsSkipped);
-
-}
-
-void vqueueSync(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue)
-{
-    Log2(("%s vqueueSync: %s old_used_idx=%u new_used_idx=%u\n", INSTANCE(pThis),
-          pQueue->szName, vringReadUsedIndex(pDevIns, &pQueue->VRing), pQueue->uNextUsedIndex));
-    vringWriteUsedIndex(pDevIns, &pQueue->VRing, pQueue->uNextUsedIndex);
-    vqueueNotify(pDevIns, pThis, pQueue);
-}
-
-
-/**
- * Raise interrupt.
- *
- * @param   pDevIns     The device instance.
- * @param   pThis       The shared virtio core instance data.
- * @param   rcBusy      Status code to return when the critical section is busy.
- * @param   u8IntCause  Interrupt cause bit mask to set in PCI ISR port.
- */
-int vpciRaiseInterrupt(PPDMDEVINS pDevIns, PVPCISTATE pThis, int rcBusy, uint8_t u8IntCause)
-{
-    RT_NOREF_PV(rcBusy);
-    // int rc = vpciCsEnter(pThis, rcBusy);
-    // if (RT_UNLIKELY(rc != VINF_SUCCESS))
-    //     return rc;
-
-    STAM_REL_COUNTER_INC(&pThis->StatIntsRaised);
-    LogFlow(("%s vpciRaiseInterrupt: u8IntCause=%x\n", INSTANCE(pThis), u8IntCause));
-
-    pThis->uISR |= u8IntCause;
-    PDMDevHlpPCISetIrq(pDevIns, 0, 1);
-    // vpciCsLeave(pThis);
-    return VINF_SUCCESS;
-}
-
-/**
- * Lower interrupt.
- *
- * @param   pDevIns     The device instance.
- * @param   pThis       The shared virtio core instance data.
- */
-static void vpciLowerInterrupt(PPDMDEVINS pDevIns, PVPCISTATE pThis)
-{
-    LogFlow(("%s vpciLowerInterrupt\n", INSTANCE(pThis)));
-    RT_NOREF(pThis);
-    PDMDevHlpPCISetIrq(pDevIns, 0, 0);
-}
-
-
-void vpciReset(PPDMDEVINS pDevIns, PVPCISTATE pThis)
-{
-    /* No interrupts should survive device reset, see @bugref(9556). */
-    if (pThis->uISR)
-        vpciLowerInterrupt(pDevIns, pThis);
-
-    pThis->uGuestFeatures = 0;
-    pThis->uQueueSelector = 0;
-    pThis->uStatus        = 0;
-    pThis->uISR           = 0;
-
-    for (unsigned i = 0; i < pThis->cQueues; i++)
-        vqueueReset(&pThis->Queues[i]);
-}
-
-
-DECLINLINE(uint32_t) vpciGetHostFeatures(PVPCISTATE pThis, PCVPCIIOCALLBACKS  pCallbacks)
-{
-    return pCallbacks->pfnGetHostFeatures(pThis) | VPCI_F_NOTIFY_ON_EMPTY;
-}
-
-/**
- * Port I/O Handler for IN operations.
- *
- * @returns VBox status code.
- *
- * @param   pDevIns     The device instance.
- * @param   pThis       The shared virtio core instance data.
- * @param   offPort     The offset into the I/O range of the port being read.
- * @param   pu32        Where to store the result.
- * @param   cb          Number of bytes read.
- * @param   pCallbacks  Pointer to the callbacks.
- * @thread  EMT
- */
-int vpciIOPortIn(PPDMDEVINS         pDevIns,
-                 PVPCISTATE         pThis,
-                 RTIOPORT           offPort,
-                 uint32_t          *pu32,
-                 unsigned           cb,
-                 PCVPCIIOCALLBACKS  pCallbacks)
-{
-    STAM_PROFILE_ADV_START(&pThis->CTX_SUFF(StatIORead), a);
-
-    /*
-     * We probably do not need to enter critical section when reading registers
-     * as the most of them are either constant or being changed during
-     * initialization only, the exception being ISR which can be raced by all
-     * threads but I see no big harm in it. It also happens to be the most read
-     * register as it gets read in interrupt handler. By dropping cs protection
-     * here we gain the ability to deliver RX packets to the guest while TX is
-     * holding cs transmitting queued packets.
-     *
-    int rc = vpciCsEnter(pThis, VINF_IOM_R3_IOPORT_READ);
-    if (RT_UNLIKELY(rc != VINF_SUCCESS))
-    {
-        STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF(StatIORead), a);
-        return rc;
-        }*/
-    int rc = VINF_SUCCESS;
-
-    switch (offPort)
-    {
-        case VPCI_HOST_FEATURES:
-            /* Tell the guest what features we support. */
-            ASSERT_GUEST_MSG(cb == 4, ("%d\n", cb));
-            *pu32 = vpciGetHostFeatures(pThis, pCallbacks) | VPCI_F_BAD_FEATURE;
-            break;
-
-        case VPCI_GUEST_FEATURES:
-            ASSERT_GUEST_MSG(cb == 4, ("%d\n", cb));
-            *pu32 = pThis->uGuestFeatures;
-            break;
-
-        case VPCI_QUEUE_PFN:
-            ASSERT_GUEST_MSG(cb == 4, ("%d\n", cb));
-            *pu32 = pThis->Queues[pThis->uQueueSelector].uPageNumber;
-            break;
-
-        case VPCI_QUEUE_NUM:
-            ASSERT_GUEST_MSG(cb == 2, ("%d\n", cb));
-            *pu32 = pThis->Queues[pThis->uQueueSelector].VRing.uSize;
-            break;
-
-        case VPCI_QUEUE_SEL:
-            ASSERT_GUEST_MSG(cb == 2, ("%d\n", cb));
-            *pu32 = pThis->uQueueSelector;
-            break;
-
-        case VPCI_STATUS:
-            ASSERT_GUEST_MSG(cb == 1, ("%d\n", cb));
-            *pu32 = pThis->uStatus;
-            break;
-
-        case VPCI_ISR:
-            ASSERT_GUEST_MSG(cb == 1, ("%d\n", cb));
-            *pu32 = pThis->uISR;
-            pThis->uISR = 0; /* read clears all interrupts */
-            vpciLowerInterrupt(pDevIns, pThis);
-            break;
-
-        default:
-            if (offPort >= VPCI_CONFIG)
-                rc = pCallbacks->pfnGetConfig(pThis, offPort - VPCI_CONFIG, cb, pu32);
-            else
-            {
-                *pu32 = UINT32_MAX;
-                rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s vpciIOPortIn: no valid port at offset port=%RTiop cb=%08x\n",
-                                       INSTANCE(pThis), offPort, cb);
-            }
-            break;
-    }
-    Log3(("%s vpciIOPortIn:  At %RTiop in  %0*x\n", INSTANCE(pThis), offPort, cb*2, *pu32));
-
-    //vpciCsLeave(pThis);
-
-    STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF(StatIORead), a);
-    return rc;
-}
-
-
-/**
- * Port I/O Handler for OUT operations.
- *
- * @returns VBox status code.
- *
- * @param   pDevIns     The device instance.
- * @param   pThis       The shared virtio core instance data.
- * @param   offPort     The offset into the I/O range of the port being written.
- * @param   u32         The value to output.
- * @param   cb          The value size in bytes.
- * @param   pCallbacks  Pointer to the callbacks.
- * @thread  EMT
- */
-int vpciIOPortOut(PPDMDEVINS         pDevIns,
-                  PVPCISTATE         pThis,
-                  PVPCISTATECC       pThisCC,
-                  RTIOPORT           offPort,
-                  uint32_t           u32,
-                  unsigned           cb,
-                  PCVPCIIOCALLBACKS  pCallbacks)
-{
-    STAM_PROFILE_ADV_START(&pThis->CTX_SUFF(StatIOWrite), a);
-    int  rc = VINF_SUCCESS;
-    bool fHasBecomeReady;
-#ifndef IN_RING3
-    RT_NOREF_PV(pThisCC);
-#endif
-
-    Log3(("%s virtioIOPortOut: At offPort=%RTiop out  %0*x\n", INSTANCE(pThis), offPort, cb*2, u32));
-
-    switch (offPort)
-    {
-        case VPCI_GUEST_FEATURES:
-        {
-            const uint32_t fHostFeatures = vpciGetHostFeatures(pThis, pCallbacks);
-
-            if (RT_LIKELY((u32 & ~fHostFeatures) == 0))
-                pThis->uGuestFeatures = u32;
-            else
-            {
-                /*
-                 * Guest requests features we don't advertise.  Stick
-                 * to the minimum if negotiation looks completely
-                 * botched, otherwise restrict to advertised features.
-                 */
-                if (u32 & VPCI_F_BAD_FEATURE)
-                {
-                    Log(("%s WARNING! Guest failed to negotiate properly (guest=%x)\n",
-                         INSTANCE(pThis), u32));
-                    pThis->uGuestFeatures = pCallbacks->pfnGetHostMinimalFeatures(pThis);
-                }
-                else
-                {
-                    Log(("%s Guest asked for features host does not support! (host=%x guest=%x)\n",
-                         INSTANCE(pThis), fHostFeatures, u32));
-                    pThis->uGuestFeatures = u32 & fHostFeatures;
-                }
-            }
-            pCallbacks->pfnSetHostFeatures(pThis, pThis->uGuestFeatures);
-            break;
-        }
-
-        case VPCI_QUEUE_PFN:
-            /*
-             * The guest is responsible for allocating the pages for queues,
-             * here it provides us with the page number of descriptor table.
-             * Note that we provide the size of the queue to the guest via
-             * VIRTIO_PCI_QUEUE_NUM.
-             */
-            pThis->Queues[pThis->uQueueSelector].uPageNumber = u32;
-            if (u32)
-                vqueueInit(&pThis->Queues[pThis->uQueueSelector], u32);
-            else
-                rc = pCallbacks->pfnReset(pDevIns);
-            break;
-
-        case VPCI_QUEUE_SEL:
-            ASSERT_GUEST_MSG(cb == 2, ("cb=%u\n", cb));
-            u32 &= 0xFFFF;
-            if (u32 < pThis->cQueues)
-                pThis->uQueueSelector = u32;
-            else
-                Log3(("%s vpciIOPortOut: Invalid queue selector %08x\n", INSTANCE(pThis), u32));
-            break;
-
-        case VPCI_QUEUE_NOTIFY:
-#ifdef IN_RING3
-            ASSERT_GUEST_MSG(cb == 2, ("cb=%u\n", cb));
-            u32 &= 0xFFFF;
-            if (u32 < pThis->cQueues)
-            {
-                RT_UNTRUSTED_VALIDATED_FENCE();
-                if (pThis->Queues[u32].VRing.addrDescriptors)
-                {
-
-                    // rc = vpciCsEnter(pThis, VERR_SEM_BUSY);
-                    // if (RT_LIKELY(rc == VINF_SUCCESS))
-                    // {
-                        pThisCC->Queues[u32].pfnCallback(pDevIns, &pThis->Queues[u32]);
-                    //     vpciCsLeave(pThis);
-                    // }
-                }
-                else
-                    Log(("%s The queue (#%d) being notified has not been initialized.\n",
-                         INSTANCE(pThis), u32));
-            }
-            else
-                Log(("%s Invalid queue number (%d)\n", INSTANCE(pThis), u32));
-#else
-            rc = VINF_IOM_R3_IOPORT_WRITE;
-#endif
-            break;
-
-        case VPCI_STATUS:
-            ASSERT_GUEST_MSG(cb == 1, ("cb=%u\n", cb));
-            u32 &= 0xFF;
-            fHasBecomeReady = !(pThis->uStatus & VPCI_STATUS_DRV_OK) && (u32 & VPCI_STATUS_DRV_OK);
-            pThis->uStatus = u32;
-            /* Writing 0 to the status port triggers device reset. */
-            if (u32 == 0)
-                rc = pCallbacks->pfnReset(pDevIns);
-            else if (fHasBecomeReady)
-            {
-                /* Older hypervisors were lax and did not enforce bus mastering. Older guests
-                 * (Linux prior to 2.6.34, NetBSD 6.x) were lazy and did not enable bus mastering.
-                 * We automagically enable bus mastering on driver initialization to make existing
-                 * drivers work.
-                 */
-                PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
-                PDMPciDevSetCommand(pPciDev, PDMPciDevGetCommand(pPciDev) | PCI_COMMAND_BUSMASTER);
-
-                pCallbacks->pfnReady(pDevIns);
-            }
-            break;
-
-        default:
-            if (offPort >= VPCI_CONFIG)
-                rc = pCallbacks->pfnSetConfig(pThis, offPort - VPCI_CONFIG, cb, &u32);
-            else
-                rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s vpciIOPortOut: no valid port at offset offPort=%RTiop cb=%08x\n",
-                                       INSTANCE(pThis), offPort, cb);
-            break;
-    }
-
-    STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF(StatIOWrite), a);
-    return rc;
-}
-
-#ifdef IN_RING3
-
-/**
- * Handles common IBase.pfnQueryInterface requests.
- */
-void *vpciR3QueryInterface(PVPCISTATECC pThisCC, const char *pszIID)
-{
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
-    return NULL;
-}
-
-/**
- * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
- */
-static DECLCALLBACK(int) vpciR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
-{
-    PVPCISTATECC pThisCC = RT_FROM_MEMBER(pInterface, VPCISTATECC, ILeds);
-    if (iLUN == 0)
-    {
-        *ppLed = &pThisCC->pShared->led;
-        return VINF_SUCCESS;
-    }
-    return VERR_PDM_LUN_NOT_FOUND;
-}
-
-/**
- * Turns on/off the write status LED.
- *
- * @returns VBox status code.
- * @param   pThis          Pointer to the device state structure.
- * @param   fOn             New LED state.
- */
-void vpciR3SetWriteLed(PVPCISTATE pThis, bool fOn)
-{
-    LogFlow(("%s vpciR3SetWriteLed: %s\n", INSTANCE(pThis), fOn?"on":"off"));
-    if (fOn)
-        pThis->led.Asserted.s.fWriting = pThis->led.Actual.s.fWriting = 1;
-    else
-        pThis->led.Actual.s.fWriting = fOn;
-}
-
-/**
- * Turns on/off the read status LED.
- *
- * @returns VBox status code.
- * @param   pThis          Pointer to the device state structure.
- * @param   fOn             New LED state.
- */
-void vpciR3SetReadLed(PVPCISTATE pThis, bool fOn)
-{
-    LogFlow(("%s vpciR3SetReadLed: %s\n", INSTANCE(pThis), fOn?"on":"off"));
-    if (fOn)
-        pThis->led.Asserted.s.fReading = pThis->led.Actual.s.fReading = 1;
-    else
-        pThis->led.Actual.s.fReading = fOn;
-}
-
-# if 0 /* unused */
-/**
- * Sets 32-bit register in PCI configuration space.
- * @param   refPciDev   The PCI device.
- * @param   uOffset     The register offset.
- * @param   u32Value    The value to store in the register.
- * @thread  EMT
- */
-DECLINLINE(void) vpciCfgSetU32(PDMPCIDEV& refPciDev, uint32_t uOffset, uint32_t u32Value)
-{
-    Assert(uOffset+sizeof(u32Value) <= sizeof(refPciDev.config));
-    *(uint32_t*)&refPciDev.config[uOffset] = u32Value;
-}
-# endif /* unused */
-
-/**
- * Dumps the state (useful for both logging and info items).
- */
-void vpciR3DumpStateWorker(PVPCISTATE pThis, PCDBGFINFOHLP pHlp)
-{
-
-    pHlp->pfnPrintf(pHlp,
-                    "  uGuestFeatures = 0x%08x\n"
-                    "  uQueueSelector = 0x%04x\n"
-                    "  uStatus        = 0x%02x\n"
-                    "  uISR           = 0x%02x\n",
-                    pThis->uGuestFeatures,
-                    pThis->uQueueSelector,
-                    pThis->uStatus,
-                    pThis->uISR);
-
-    for (unsigned i = 0; i < pThis->cQueues; i++)
-        pHlp->pfnPrintf(pHlp,
-                        " %s queue:\n"
-                        "  VRing.uSize           = %u\n"
-                        "  VRing.addrDescriptors = %p\n"
-                        "  VRing.addrAvail       = %p\n"
-                        "  VRing.addrUsed        = %p\n"
-                        "  uNextAvailIndex       = %u\n"
-                        "  uNextUsedIndex        = %u\n"
-                        "  uPageNumber           = %x\n",
-                        pThis->Queues[i].szName,
-                        pThis->Queues[i].VRing.uSize,
-                        pThis->Queues[i].VRing.addrDescriptors,
-                        pThis->Queues[i].VRing.addrAvail,
-                        pThis->Queues[i].VRing.addrUsed,
-                        pThis->Queues[i].uNextAvailIndex,
-                        pThis->Queues[i].uNextUsedIndex,
-                        pThis->Queues[i].uPageNumber);
-}
-
-# ifdef LOG_ENABLED
-void vpciR3DumpState(PPDMDEVINS pDevIns, PVPCISTATE pThis, const char *pcszCaller)
-{
-    if (LogIs2Enabled())
-    {
-        Log2(("vpciR3DumpState: (called from %s)\n", pcszCaller));
-        vpciR3DumpStateWorker(pThis, PDMDevHlpDBGFInfoLogHlp(pDevIns));
-    }
-}
-# else
-#  define vpciR3DumpState(d, x, s)  do { } while (0)
-# endif
-
-/**
- * Saved the core virtio state.
- *
- * @returns VBox status code.
- * @param   pDevIns     The device insatnce data.
- * @param   pHlp        The device helpers.
- * @param   pThis       The shared virtio core instance data.
- * @param   pSSM        The handle to the saved state.
- */
-int vpciR3SaveExec(PPDMDEVINS pDevIns, PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM)
-{
-    vpciR3DumpState(pDevIns, pThis, "vpciR3SaveExec"); RT_NOREF(pDevIns);
-
-    pHlp->pfnSSMPutU32(pSSM, pThis->uGuestFeatures);
-    pHlp->pfnSSMPutU16(pSSM, pThis->uQueueSelector);
-    pHlp->pfnSSMPutU8( pSSM, pThis->uStatus);
-    pHlp->pfnSSMPutU8( pSSM, pThis->uISR);
-
-    /* Save queue states */
-    int rc = pHlp->pfnSSMPutU32(pSSM, pThis->cQueues);
-    AssertRCReturn(rc, rc);
-    for (unsigned i = 0; i < pThis->cQueues; i++)
-    {
-        pHlp->pfnSSMPutU16(pSSM, pThis->Queues[i].VRing.uSize);
-        pHlp->pfnSSMPutU32(pSSM, pThis->Queues[i].uPageNumber);
-        pHlp->pfnSSMPutU16(pSSM, pThis->Queues[i].uNextAvailIndex);
-        rc = pHlp->pfnSSMPutU16(pSSM, pThis->Queues[i].uNextUsedIndex);
-        AssertRCReturn(rc, rc);
-    }
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Loads a saved device state.
- *
- * @returns VBox status code.
- * @param   pDevIns     The device insatnce data.
- * @param   pHlp        The device helpers.
- * @param   pThis       The shared virtio core instance data.
- * @param   pSSM        The handle to the saved state.
- * @param   uVersion    The data unit version number.
- * @param   uPass       The data pass.
- * @param   cQueues     The default queue count (for old states).
- */
-int vpciR3LoadExec(PPDMDEVINS pDevIns, PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass, uint32_t cQueues)
-{
-    int rc;
-
-    if (uPass == SSM_PASS_FINAL)
-    {
-        /* Restore state data */
-        pHlp->pfnSSMGetU32(pSSM, &pThis->uGuestFeatures);
-        pHlp->pfnSSMGetU16(pSSM, &pThis->uQueueSelector);
-        pHlp->pfnSSMGetU8( pSSM, &pThis->uStatus);
-        pHlp->pfnSSMGetU8( pSSM, &pThis->uISR);
-
-        /* Restore queues */
-        if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
-        {
-            rc = pHlp->pfnSSMGetU32(pSSM, &pThis->cQueues);
-            AssertRCReturn(rc, rc);
-        }
-        else
-            pThis->cQueues = cQueues;
-        AssertLogRelMsgReturn(pThis->cQueues <= VIRTIO_MAX_NQUEUES, ("%#x\n", pThis->cQueues), VERR_SSM_LOAD_CONFIG_MISMATCH);
-        AssertLogRelMsgReturn(pThis->uQueueSelector < pThis->cQueues || (pThis->cQueues == 0 && pThis->uQueueSelector),
-                              ("uQueueSelector=%u cQueues=%u\n", pThis->uQueueSelector, pThis->cQueues),
-                              VERR_SSM_LOAD_CONFIG_MISMATCH);
-
-        for (unsigned i = 0; i < pThis->cQueues; i++)
-        {
-            rc = pHlp->pfnSSMGetU16(pSSM, &pThis->Queues[i].VRing.uSize);
-            AssertRCReturn(rc, rc);
-            rc = pHlp->pfnSSMGetU32(pSSM, &pThis->Queues[i].uPageNumber);
-            AssertRCReturn(rc, rc);
-
-            if (pThis->Queues[i].uPageNumber)
-                vqueueInit(&pThis->Queues[i], pThis->Queues[i].uPageNumber);
-
-            rc = pHlp->pfnSSMGetU16(pSSM, &pThis->Queues[i].uNextAvailIndex);
-            AssertRCReturn(rc, rc);
-            rc = pHlp->pfnSSMGetU16(pSSM, &pThis->Queues[i].uNextUsedIndex);
-            AssertRCReturn(rc, rc);
-        }
-    }
-
-    vpciR3DumpState(pDevIns, pThis, "vpciLoadExec"); RT_NOREF(pDevIns);
-
-    return VINF_SUCCESS;
-}
-
-PVQUEUE vpciR3AddQueue(PVPCISTATE pThis, PVPCISTATECC pThisCC, unsigned uSize,
-                       PFNVPCIQUEUECALLBACK pfnCallback, const char *pcszName)
-{
-    /* Find an empty queue slot */
-    for (unsigned i = 0; i < pThis->cQueues; i++)
-    {
-        if (pThis->Queues[i].VRing.uSize == 0)
-        {
-            PVQUEUE pQueue = &pThis->Queues[i];
-            pQueue->VRing.uSize = uSize;
-            pQueue->VRing.addrDescriptors = 0;
-            pQueue->uPageNumber = 0;
-            int rc = RTStrCopy(pQueue->szName, sizeof(pQueue->szName), pcszName);
-            AssertRC(rc);
-            pThisCC->Queues[i].pfnCallback = pfnCallback;
-            return pQueue;
-        }
-    }
-    AssertMsgFailedReturn(("%s Too many queues being added, no empty slots available!\n", INSTANCE(pThis)), NULL);
-}
-
-/**
- * Destruct PCI-related part of device.
- *
- * We need to free non-VM resources only.
- *
- * @returns VBox status code.
- * @param   pThis      The shared virtio core instance data.
- */
-int vpciR3Term(PPDMDEVINS pDevIns, PVPCISTATE pThis)
-{
-    Log(("%s Destroying PCI instance\n", INSTANCE(pThis)));
-
-    if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->cs))
-        PDMDevHlpCritSectDelete(pDevIns, &pThis->cs);
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Set PCI configuration space registers.
- *
- * @param   pPciDev      Pointer to the PCI device structure.
- * @param   uDeviceId    VirtiO Device Id
- * @param   uClass       Class of PCI device (network, etc)
- * @thread  EMT
- */
-static void vpciConfigure(PPDMPCIDEV pPciDev, uint16_t uDeviceId, uint16_t uClass)
-{
-    /* Configure PCI Device, assume 32-bit mode ******************************/
-    PDMPciDevSetVendorId(pPciDev,                           DEVICE_PCI_VENDOR_ID);
-    PDMPciDevSetDeviceId(pPciDev,                           DEVICE_PCI_BASE_ID + uDeviceId);
-    PDMPciDevSetWord(pPciDev, VBOX_PCI_SUBSYSTEM_VENDOR_ID, DEVICE_PCI_SUBSYSTEM_VENDOR_ID);
-    PDMPciDevSetWord(pPciDev, VBOX_PCI_SUBSYSTEM_ID,        DEVICE_PCI_SUBSYSTEM_BASE_ID + uDeviceId);
-
-    /* ABI version, must be equal 0 as of 2.6.30 kernel. */
-    PDMPciDevSetByte(pPciDev, VBOX_PCI_REVISION_ID,         0x00);
-    /* Ethernet adapter */
-    PDMPciDevSetByte(pPciDev, VBOX_PCI_CLASS_PROG,          0x00);
-    PDMPciDevSetWord(pPciDev, VBOX_PCI_CLASS_DEVICE,        uClass);
-    /* Interrupt Pin: INTA# */
-    PDMPciDevSetByte(pPciDev, VBOX_PCI_INTERRUPT_PIN,       0x01);
-
-# ifdef VBOX_WITH_MSI_DEVICES
-    PDMPciDevSetCapabilityList(pPciDev,                     0x80);
-    PDMPciDevSetStatus(pPciDev,                             VBOX_PCI_STATUS_CAP_LIST);
-# endif
-}
-
-int vpciR3Init(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC, uint16_t uDeviceId, uint16_t uClass, uint32_t cQueues)
-{
-    /* Init data members. */
-    pThis->cQueues                   = cQueues;
-    pThis->led.u32Magic              = PDMLED_MAGIC;
-    pThisCC->pShared                 = pThis;
-    pThisCC->ILeds.pfnQueryStatusLed = vpciR3QueryStatusLed;
-    AssertReturn(pThisCC->IBase.pfnQueryInterface, VERR_INVALID_POINTER);
-    AssertReturn(pThis->szInstance[0], VERR_INVALID_PARAMETER);
-    AssertReturn(strlen(pThis->szInstance) < sizeof(pThis->szInstance), VERR_INVALID_PARAMETER);
-
-    /* Initialize critical section. */
-    int rc = PDMDevHlpCritSectInit(pDevIns, &pThis->cs, RT_SRC_POS, "%s", pThis->szInstance);
-    AssertRCReturn(rc, rc);
-
-    /*
-     * Set up the PCI device.
-     */
-    PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
-    PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
-
-    /* Set PCI config registers */
-    vpciConfigure(pPciDev, uDeviceId, uClass);
-
-    /* Register PCI device */
-    rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
-    AssertRCReturn(rc, rc);
-
-# ifdef VBOX_WITH_MSI_DEVICES
-#  if 0
-    {
-        PDMMSIREG aMsiReg;
-
-        RT_ZERO(aMsiReg);
-        aMsiReg.cMsixVectors = 1;
-        aMsiReg.iMsixCapOffset = 0x80;
-        aMsiReg.iMsixNextOffset = 0x0;
-        aMsiReg.iMsixBar = 0;
-        rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg);
-        if (RT_FAILURE (rc))
-            PCIDevSetCapabilityList(&pThis->pciDevice, 0x0);
-    }
-#  endif
-# endif
-
-    /*
-     * Attach the status driver (optional).
-     */
-    PPDMIBASE pBase;
-    rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
-    if (RT_SUCCESS(rc))
-        pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
-    else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
-
-    /*
-     * Statistics.
-     */
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIntsRaised,  STAMTYPE_COUNTER, "Interrupts/Raised",  STAMUNIT_OCCURENCES,     "Number of raised interrupts");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIntsSkipped, STAMTYPE_COUNTER, "Interrupts/Skipped", STAMUNIT_OCCURENCES,     "Number of skipped interrupts");
-# ifdef VBOX_WITH_STATISTICS
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR3,    STAMTYPE_PROFILE, "IO/ReadR3",          STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR0,    STAMTYPE_PROFILE, "IO/ReadR0",          STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R0");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadRC,    STAMTYPE_PROFILE, "IO/ReadRC",          STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RC");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR3,   STAMTYPE_PROFILE, "IO/WriteR3",         STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR0,   STAMTYPE_PROFILE, "IO/WriteR0",         STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R0");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteRC,   STAMTYPE_PROFILE, "IO/WriteRC",         STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RC");
-# endif /* VBOX_WITH_STATISTICS */
-
-    return VINF_SUCCESS;
-}
-
-#else  /* !IN_RING3 */
-
-/**
- * Does ring-0/raw-mode initialization.
- */
-int vpciRZInit(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC)
-{
-    RT_NOREF(pDevIns, pThis, pThisCC);
-    return VINF_SUCCESS;
-}
-
-#endif /* !IN_RING3 */
-
Index: unk/src/VBox/Devices/VirtIO/Virtio.h
===================================================================
--- /trunk/src/VBox/Devices/VirtIO/Virtio.h	(revision 98063)
+++ 	(revision )
@@ -1,364 +1,0 @@
-/* $Id$ */
-/** @file
- * Virtio.h - Virtio Declarations
- */
-
-/*
- * Copyright (C) 2009-2022 Oracle and/or its affiliates.
- *
- * This file is part of VirtualBox base platform packages, as
- * available from https://www.virtualbox.org.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation, in version 3 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses>.
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#ifndef VBOX_INCLUDED_SRC_VirtIO_Virtio_h
-#define VBOX_INCLUDED_SRC_VirtIO_Virtio_h
-#ifndef RT_WITHOUT_PRAGMA_ONCE
-# pragma once
-#endif
-
-#include <iprt/types.h>
-
-
-/** Pointer to the core shared state of a VirtIO PCI device */
-typedef struct VPCISTATE *PVPCISTATE;
-/** Pointer to the core ring-3 state of a VirtIO PCI device */
-typedef struct VPCISTATER3 *PVPCISTATER3;
-/** Pointer to the core ring-0 state of a VirtIO PCI device */
-typedef struct VPCISTATER0 *PVPCISTATER0;
-/** Pointer to the core raw-mode state of a VirtIO PCI device */
-typedef struct VPCISTATERC *PVPCISTATERC;
-
-/** Pointer to the core current context state of a VirtIO PCI device */
-typedef CTX_SUFF(PVPCISTATE) PVPCISTATECC;
-/** The core current context state of a VirtIO PCI device */
-typedef struct CTX_SUFF(VPCISTATE) VPCISTATECC;
-
-
-/** @name Saved state versions.
- * The saved state version is changed if either common or any of specific
- * parts are changed. That is, it is perfectly possible that the version
- * of saved vnet state will increase as a result of change in vblk structure
- * for example.
- */
-#define VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1 1
-#define VIRTIO_SAVEDSTATE_VERSION           2
-/** @} */
-
-#define DEVICE_PCI_VENDOR_ID                0x1AF4
-#define DEVICE_PCI_BASE_ID                  0x1000
-#define DEVICE_PCI_SUBSYSTEM_VENDOR_ID      0x1AF4
-#define DEVICE_PCI_SUBSYSTEM_BASE_ID        1
-
-#define VIRTIO_MAX_NQUEUES                  3
-
-#define VPCI_HOST_FEATURES                  0x0
-#define VPCI_GUEST_FEATURES                 0x4
-#define VPCI_QUEUE_PFN                      0x8
-#define VPCI_QUEUE_NUM                      0xC
-#define VPCI_QUEUE_SEL                      0xE
-#define VPCI_QUEUE_NOTIFY                   0x10
-#define VPCI_STATUS                         0x12
-#define VPCI_ISR                            0x13
-#define VPCI_CONFIG                         0x14
-
-#define VPCI_ISR_QUEUE                      0x1
-#define VPCI_ISR_CONFIG                     0x3
-
-#define VPCI_STATUS_ACK                     0x01
-#define VPCI_STATUS_DRV                     0x02
-#define VPCI_STATUS_DRV_OK                  0x04
-#define VPCI_STATUS_FAILED                  0x80
-
-#define VPCI_F_NOTIFY_ON_EMPTY              UINT32_C(0x01000000)
-#define VPCI_F_ANY_LAYOUT                   UINT32_C(0x08000000)
-#define VPCI_F_RING_INDIRECT_DESC           UINT32_C(0x10000000)
-#define VPCI_F_RING_EVENT_IDX               UINT32_C(0x20000000)
-#define VPCI_F_BAD_FEATURE                  UINT32_C(0x40000000)
-
-#define VRINGDESC_MAX_SIZE                  (2 * 1024 * 1024)
-#define VRINGDESC_F_NEXT                    0x01
-#define VRINGDESC_F_WRITE                   0x02
-#define VRINGDESC_F_INDIRECT                0x04
-
-typedef struct VRINGDESC
-{
-    uint64_t u64Addr;
-    uint32_t uLen;
-    uint16_t u16Flags;
-    uint16_t u16Next;
-} VRINGDESC;
-typedef VRINGDESC *PVRINGDESC;
-
-#define VRINGAVAIL_F_NO_INTERRUPT           0x01
-
-typedef struct VRINGAVAIL
-{
-    uint16_t uFlags;
-    uint16_t uNextFreeIndex;
-    uint16_t auRing[1];
-} VRINGAVAIL;
-
-typedef struct VRINGUSEDELEM
-{
-    uint32_t uId;
-    uint32_t uLen;
-} VRINGUSEDELEM;
-
-#define VRINGUSED_F_NO_NOTIFY 0x01
-
-typedef struct VRingUsed
-{
-    uint16_t      uFlags;
-    uint16_t      uIndex;
-    VRINGUSEDELEM aRing[1];
-} VRINGUSED;
-typedef VRINGUSED *PVRINGUSED;
-
-#define VRING_MAX_SIZE                      1024
-
-typedef struct VRING
-{
-    uint16_t   uSize;
-    uint16_t   padding[3];
-    RTGCPHYS   addrDescriptors;
-    RTGCPHYS   addrAvail;
-    RTGCPHYS   addrUsed;
-} VRING;
-typedef VRING *PVRING;
-
-typedef struct VQUEUE
-{
-    VRING       VRing;
-    uint16_t    uNextAvailIndex;
-    uint16_t    uNextUsedIndex;
-    uint32_t    uPageNumber;
-    char        szName[16];
-} VQUEUE;
-typedef VQUEUE *PVQUEUE;
-
-/**
- * Queue callback (consumer?).
- *
- * @param   pDevIns         The device instance.
- * @param   pQueue          Pointer to the queue structure.
- */
-typedef DECLCALLBACKTYPE(void, FNVPCIQUEUECALLBACK,(PPDMDEVINS pDevIns, PVQUEUE pQueue));
-/** Pointer to a VQUEUE callback function. */
-typedef FNVPCIQUEUECALLBACK *PFNVPCIQUEUECALLBACK;
-
-typedef struct VQUEUER3
-{
-    R3PTRTYPE(PFNVPCIQUEUECALLBACK) pfnCallback;
-} VQUEUER3;
-typedef VQUEUER3 *PVQUEUER3;
-
-typedef struct VQUEUESEG
-{
-    RTGCPHYS addr;
-    void    *pv;
-    uint32_t cb;
-} VQUEUESEG;
-
-typedef struct VQUEUEELEM
-{
-    uint32_t  uIndex;
-    uint32_t  cIn;
-    uint32_t  cOut;
-    VQUEUESEG aSegsIn[VRING_MAX_SIZE];
-    VQUEUESEG aSegsOut[VRING_MAX_SIZE];
-} VQUEUEELEM;
-typedef VQUEUEELEM *PVQUEUEELEM;
-
-
-enum VirtioDeviceType
-{
-    VIRTIO_NET_ID = 0,
-    VIRTIO_BLK_ID = 1,
-    VIRTIO_32BIT_HACK = 0x7fffffff
-};
-
-
-/**
- * The core shared state of a VirtIO PCI device
- */
-typedef struct VPCISTATE
-{
-    PDMCRITSECT             cs;      /**< Critical section - what is it protecting? */
-    /** Read-only part, never changes after initialization. */
-    char                    szInstance[8];         /**< Instance name, e.g. VNet#1. */
-
-    /* Read/write part, protected with critical section. */
-    /** Status LED. */
-    PDMLED                  led;
-
-    uint32_t                uGuestFeatures;
-    uint16_t                uQueueSelector;         /**< An index in aQueues array. */
-    uint8_t                 uStatus; /**< Device Status (bits are device-specific). */
-    uint8_t                 uISR;                   /**< Interrupt Status Register. */
-
-    /** Number of queues actually used. */
-    uint32_t                cQueues;
-    uint32_t                u32Padding;
-    /** Shared queue data. */
-    VQUEUE                  Queues[VIRTIO_MAX_NQUEUES];
-
-    STAMCOUNTER             StatIntsRaised;
-    STAMCOUNTER             StatIntsSkipped;
-
-#ifdef VBOX_WITH_STATISTICS
-    STAMPROFILEADV          StatIOReadR3;
-    STAMPROFILEADV          StatIOReadR0;
-    STAMPROFILEADV          StatIOReadRC;
-    STAMPROFILEADV          StatIOWriteR3;
-    STAMPROFILEADV          StatIOWriteR0;
-    STAMPROFILEADV          StatIOWriteRC;
-#endif
-} VPCISTATE;
-
-
-/**
- * The core ring-3 state of a VirtIO PCI device
- *
- * @implements  PDMILEDPORTS
- */
-typedef struct VPCISTATER3
-{
-    /** Status LUN: Base interface. */
-    PDMIBASE                        IBase;
-    /** Status LUN: LED port interface. */
-    PDMILEDPORTS                    ILeds;
-    /** Status LUN: LED connector (peer). */
-    R3PTRTYPE(PPDMILEDCONNECTORS)   pLedsConnector;
-    /** Pointer to the shared state. */
-    R3PTRTYPE(PVPCISTATE)           pShared;
-    /** Ring-3 per-queue data. */
-    VQUEUER3                        Queues[VIRTIO_MAX_NQUEUES];
-} VPCISTATER3;
-
-
-/**
- * The core ring-0 state of a VirtIO PCI device
- */
-typedef struct VPCISTATER0
-{
-    uint64_t                uUnused;
-} VPCISTATER0;
-
-
-/**
- * The core raw-mode state of a VirtIO PCI device
- */
-typedef struct VPCISTATERC
-{
-    uint64_t                uUnused;
-} VPCISTATERC;
-
-
-/** @name VirtIO port I/O callbacks.
- * @{ */
-typedef struct VPCIIOCALLBACKS
-{
-     DECLCALLBACKMEMBER(uint32_t, pfnGetHostFeatures,(PVPCISTATE pVPciState));
-     DECLCALLBACKMEMBER(uint32_t, pfnGetHostMinimalFeatures,(PVPCISTATE pVPciState));
-     DECLCALLBACKMEMBER(void, pfnSetHostFeatures,(PVPCISTATE pVPciState, uint32_t fFeatures));
-     DECLCALLBACKMEMBER(int, pfnGetConfig,(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *pvData));
-     DECLCALLBACKMEMBER(int, pfnSetConfig,(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *pvData));
-     DECLCALLBACKMEMBER(int, pfnReset,(PPDMDEVINS pDevIns));
-     DECLCALLBACKMEMBER(void, pfnReady,(PPDMDEVINS pDevIns));
-} VPCIIOCALLBACKS;
-/** Pointer to a const VirtIO port I/O callback structure. */
-typedef const VPCIIOCALLBACKS *PCVPCIIOCALLBACKS;
-/** @} */
-
-int   vpciR3Init(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC, uint16_t uDeviceId, uint16_t uClass, uint32_t cQueues);
-int   vpciRZInit(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC);
-int   vpciR3Term(PPDMDEVINS pDevIns, PVPCISTATE pThis);
-PVQUEUE vpciR3AddQueue(PVPCISTATE pThis, PVPCISTATECC pThisCC, unsigned uSize, PFNVPCIQUEUECALLBACK pfnCallback, const char *pcszName);
-void *vpciR3QueryInterface(PVPCISTATECC pThisCC, const char *pszIID);
-void  vpciR3SetWriteLed(PVPCISTATE pThis, bool fOn);
-void  vpciR3SetReadLed(PVPCISTATE pThis, bool fOn);
-int   vpciR3SaveExec(PPDMDEVINS pDevIns, PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM);
-int   vpciR3LoadExec(PPDMDEVINS pDevIns, PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass, uint32_t cQueues);
-void  vpciR3DumpStateWorker(PVPCISTATE pThis, PCDBGFINFOHLP pHlp);
-
-void  vpciReset(PPDMDEVINS pDevIns, PVPCISTATE pThis);
-int   vpciRaiseInterrupt(PPDMDEVINS pDevIns, PVPCISTATE pThis, int rcBusy, uint8_t u8IntCause);
-int   vpciIOPortIn(PPDMDEVINS pDevIns, PVPCISTATE pThis, RTIOPORT offPort,
-                   uint32_t *pu32, unsigned cb,PCVPCIIOCALLBACKS pCallbacks);
-int   vpciIOPortOut(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC, RTIOPORT offPort,
-                    uint32_t u32, unsigned cb, PCVPCIIOCALLBACKS pCallbacks);
-
-#define VPCI_CS
-
-#ifdef VPCI_CS
-# define VPCI_R3_CS_ENTER_RETURN_VOID(a_pDevIns, a_pThis) do { \
-        int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &(a_pThis)->cs, VERR_IGNORED); \
-        PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &(a_pThis)->cs, rcLock); \
-    } while (0)
-#else
-# define VPCI_R3_CS_ENTER_RETURN_VOID(a_pDevIns, a_pThis) do { } while (0)
-#endif
-
-DECLINLINE(int) vpciCsEnter(PPDMDEVINS pDevIns, PVPCISTATE pThis, int rcBusy)
-{
-#ifdef VPCI_CS
-    return PDMDevHlpCritSectEnter(pDevIns, &pThis->cs, rcBusy);
-#else
-    RT_NOREF(pDevIns, pThis, rcBusy);
-    return VINF_SUCCESS;
-#endif
-}
-
-DECLINLINE(void) vpciCsLeave(PPDMDEVINS pDevIns, PVPCISTATE pThis)
-{
-#ifdef VPCI_CS
-    PDMDevHlpCritSectLeave(pDevIns, &pThis->cs);
-#endif
-}
-
-void vringSetNotification(PPDMDEVINS pDevIns, PVRING pVRing, bool fEnabled);
-
-DECLINLINE(uint16_t) vringReadAvailIndex(PPDMDEVINS pDevIns, PVRING pVRing)
-{
-    uint16_t idx = 0;
-    PDMDevHlpPhysRead(pDevIns, pVRing->addrAvail + RT_UOFFSETOF(VRINGAVAIL, uNextFreeIndex), &idx, sizeof(idx));
-    return idx;
-}
-
-bool vqueueSkip(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue);
-bool vqueueGet(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, bool fRemove = true);
-void vqueuePut(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen, uint32_t uReserved = 0);
-void vqueueSync(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue);
-
-DECLINLINE(bool) vqueuePeek(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem)
-{
-    return vqueueGet(pDevIns, pThis, pQueue, pElem, /* fRemove */ false);
-}
-
-DECLINLINE(bool) vqueueIsReady(PVQUEUE pQueue)
-{
-    return !!pQueue->VRing.addrAvail;
-}
-
-DECLINLINE(bool) vqueueIsEmpty(PPDMDEVINS pDevIns, PVQUEUE pQueue)
-{
-    return vringReadAvailIndex(pDevIns, &pQueue->VRing) == pQueue->uNextAvailIndex;
-}
-
-#endif /* !VBOX_INCLUDED_SRC_VirtIO_Virtio_h */
-
