[82681] | 1 | /* $Id: DevVirtioNet.cpp 101400 2023-10-10 08:39:16Z vboxsync $ $Revision: 101400 $ $Date: 2023-10-10 08:39:16 +0000 (Tue, 10 Oct 2023) $ $Author: vboxsync $ */
|
---|
| 2 |
|
---|
| 3 | /** @file
|
---|
| 4 | * VBox storage devices - Virtio NET Driver
|
---|
| 5 | *
|
---|
| 6 | * Log-levels used:
|
---|
| 7 | * - Level 1: The most important (but usually rare) things to note
|
---|
| 8 | * - Level 2: NET command logging
|
---|
| 9 | * - Level 3: Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
|
---|
| 10 | * - Level 6: Device <-> Guest Driver negotation, traffic, notifications and state handling
|
---|
| 11 | * - Level 12: Brief formatted hex dumps of I/O data
|
---|
| 12 | */
|
---|
| 13 |
|
---|
| 14 | /*
|
---|
[98103] | 15 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[82681] | 16 | *
|
---|
[96407] | 17 | * This file is part of VirtualBox base platform packages, as
|
---|
| 18 | * available from https://www.virtualbox.org.
|
---|
| 19 | *
|
---|
| 20 | * This program is free software; you can redistribute it and/or
|
---|
| 21 | * modify it under the terms of the GNU General Public License
|
---|
| 22 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 23 | * License.
|
---|
| 24 | *
|
---|
| 25 | * This program is distributed in the hope that it will be useful, but
|
---|
| 26 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 28 | * General Public License for more details.
|
---|
| 29 | *
|
---|
| 30 | * You should have received a copy of the GNU General Public License
|
---|
| 31 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 32 | *
|
---|
| 33 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[82681] | 34 | */
|
---|
| 35 |
|
---|
[92939] | 36 | /*******************************************************************************************************************************
|
---|
| 37 | * Header Files *
|
---|
| 38 | ***************************************************************************************************************************** **/
|
---|
[82681] | 39 | #define LOG_GROUP LOG_GROUP_DEV_VIRTIO
|
---|
| 40 | #define VIRTIONET_WITH_GSO
|
---|
[84351] | 41 |
|
---|
[83913] | 42 | #include <iprt/types.h>
|
---|
[84351] | 43 | #include <iprt/errcore.h>
|
---|
| 44 | #include <iprt/assert.h>
|
---|
| 45 | #include <iprt/string.h>
|
---|
[82681] | 46 |
|
---|
[84351] | 47 | #include <VBox/sup.h>
|
---|
[82681] | 48 | #include <VBox/vmm/pdmdev.h>
|
---|
[83913] | 49 | #include <VBox/vmm/stam.h>
|
---|
[82681] | 50 | #include <VBox/vmm/pdmcritsect.h>
|
---|
| 51 | #include <VBox/vmm/pdmnetifs.h>
|
---|
| 52 | #include <VBox/msi.h>
|
---|
| 53 | #include <VBox/version.h>
|
---|
| 54 | #include <VBox/log.h>
|
---|
[100872] | 55 | #include <VBox/pci.h>
|
---|
[84351] | 56 |
|
---|
[92939] | 57 |
|
---|
[82681] | 58 | #ifdef IN_RING3
|
---|
[84351] | 59 | # include <VBox/VBoxPktDmp.h>
|
---|
[82681] | 60 | # include <iprt/alloc.h>
|
---|
| 61 | # include <iprt/memcache.h>
|
---|
| 62 | # include <iprt/semaphore.h>
|
---|
| 63 | # include <iprt/sg.h>
|
---|
| 64 | # include <iprt/param.h>
|
---|
| 65 | # include <iprt/uuid.h>
|
---|
| 66 | #endif
|
---|
[84819] | 67 | #include "../VirtIO/VirtioCore.h"
|
---|
[82681] | 68 |
|
---|
| 69 | #include "VBoxDD.h"
|
---|
| 70 |
|
---|
[92939] | 71 | #define VIRTIONET_TRANSITIONAL_ENABLE_FLAG 1 /** < If set behave as VirtIO "transitional" device */
|
---|
[82681] | 72 |
|
---|
[92939] | 73 | /** The current saved state version for the virtio core. */
|
---|
| 74 | #define VIRTIONET_SAVEDSTATE_VERSION UINT32_C(1)
|
---|
| 75 | #define VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY UINT32_C(1) /**< Grandfathered in from DevVirtioNet.cpp */
|
---|
| 76 | #define VIRTIONET_SAVEDSTATE_VERSION_LEGACY UINT32_C(2) /**< Grandfathered in from DevVirtioNet.cpp */
|
---|
| 77 | #define VIRTIONET_VERSION_MARKER_MAC_ADDR { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /** SSM handling */
|
---|
| 78 |
|
---|
| 79 | /*
|
---|
| 80 | * Glossary of networking acronyms used in feature names below:
|
---|
| 81 | *
|
---|
| 82 | * GSO = Generic Segmentation Offload
|
---|
| 83 | * TSO = TCP Segmentation Offload
|
---|
| 84 | * UFO = UDP Fragmentation Offload
|
---|
| 85 | * ECN = Explicit Congestion Notification
|
---|
| 86 | */
|
---|
| 87 |
|
---|
[82681] | 88 | /** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
|
---|
| 89 | * @{ */
|
---|
| 90 | #define VIRTIONET_F_CSUM RT_BIT_64(0) /**< Handle packets with partial checksum */
|
---|
| 91 | #define VIRTIONET_F_GUEST_CSUM RT_BIT_64(1) /**< Handles packets with partial checksum */
|
---|
| 92 | #define VIRTIONET_F_CTRL_GUEST_OFFLOADS RT_BIT_64(2) /**< Control channel offloads reconfig support */
|
---|
| 93 | #define VIRTIONET_F_MAC RT_BIT_64(5) /**< Device has given MAC address */
|
---|
| 94 | #define VIRTIONET_F_GUEST_TSO4 RT_BIT_64(7) /**< Driver can receive TSOv4 */
|
---|
| 95 | #define VIRTIONET_F_GUEST_TSO6 RT_BIT_64(8) /**< Driver can receive TSOv6 */
|
---|
| 96 | #define VIRTIONET_F_GUEST_ECN RT_BIT_64(9) /**< Driver can receive TSO with ECN */
|
---|
| 97 | #define VIRTIONET_F_GUEST_UFO RT_BIT_64(10) /**< Driver can receive UFO */
|
---|
| 98 | #define VIRTIONET_F_HOST_TSO4 RT_BIT_64(11) /**< Device can receive TSOv4 */
|
---|
| 99 | #define VIRTIONET_F_HOST_TSO6 RT_BIT_64(12) /**< Device can receive TSOv6 */
|
---|
| 100 | #define VIRTIONET_F_HOST_ECN RT_BIT_64(13) /**< Device can receive TSO with ECN */
|
---|
| 101 | #define VIRTIONET_F_HOST_UFO RT_BIT_64(14) /**< Device can receive UFO */
|
---|
| 102 | #define VIRTIONET_F_MRG_RXBUF RT_BIT_64(15) /**< Driver can merge receive buffers */
|
---|
| 103 | #define VIRTIONET_F_STATUS RT_BIT_64(16) /**< Config status field is available */
|
---|
| 104 | #define VIRTIONET_F_CTRL_VQ RT_BIT_64(17) /**< Control channel is available */
|
---|
| 105 | #define VIRTIONET_F_CTRL_RX RT_BIT_64(18) /**< Control channel RX mode + MAC addr filtering */
|
---|
| 106 | #define VIRTIONET_F_CTRL_VLAN RT_BIT_64(19) /**< Control channel VLAN filtering */
|
---|
[82775] | 107 | #define VIRTIONET_F_CTRL_RX_EXTRA RT_BIT_64(20) /**< Control channel RX mode extra functions */
|
---|
[82681] | 108 | #define VIRTIONET_F_GUEST_ANNOUNCE RT_BIT_64(21) /**< Driver can send gratuitous packets */
|
---|
| 109 | #define VIRTIONET_F_MQ RT_BIT_64(22) /**< Support ultiqueue with auto receive steering */
|
---|
| 110 | #define VIRTIONET_F_CTRL_MAC_ADDR RT_BIT_64(23) /**< Set MAC address through control channel */
|
---|
| 111 | /** @} */
|
---|
| 112 |
|
---|
[93013] | 113 | #ifdef IN_RING3
|
---|
[92091] | 114 | static const VIRTIO_FEATURES_LIST s_aDevSpecificFeatures[] =
|
---|
| 115 | {
|
---|
[92939] | 116 | { VIRTIONET_F_STATUS, " STATUS Configuration status field is available.\n" },
|
---|
| 117 | { VIRTIONET_F_MAC, " MAC Host has given MAC address.\n" },
|
---|
| 118 | { VIRTIONET_F_CTRL_VQ, " CTRL_VQ Control channel is available.\n" },
|
---|
| 119 | { VIRTIONET_F_CTRL_MAC_ADDR, " CTRL_MAC_ADDR Set MAC address through control channel.\n" },
|
---|
| 120 | { VIRTIONET_F_CTRL_RX, " CTRL_RX Control channel RX mode support.\n" },
|
---|
| 121 | { VIRTIONET_F_CTRL_VLAN, " CTRL_VLAN Control channel VLAN filtering.\n" },
|
---|
| 122 | { VIRTIONET_F_CTRL_GUEST_OFFLOADS, " CTRL_GUEST_OFFLOADS Control channel offloads reconfiguration support.\n" },
|
---|
[92091] | 123 | { VIRTIONET_F_GUEST_CSUM, " GUEST_CSUM Guest handles packets with partial checksum.\n" },
|
---|
[92939] | 124 | { VIRTIONET_F_GUEST_ANNOUNCE, " GUEST_ANNOUNCE Guest can send gratuitous packets.\n" },
|
---|
[92091] | 125 | { VIRTIONET_F_GUEST_TSO4, " GUEST_TSO4 Guest can receive TSOv4.\n" },
|
---|
| 126 | { VIRTIONET_F_GUEST_TSO6, " GUEST_TSO6 Guest can receive TSOv6.\n" },
|
---|
| 127 | { VIRTIONET_F_GUEST_ECN, " GUEST_ECN Guest can receive TSO with ECN.\n" },
|
---|
| 128 | { VIRTIONET_F_GUEST_UFO, " GUEST_UFO Guest can receive UFO.\n" },
|
---|
| 129 | { VIRTIONET_F_HOST_TSO4, " HOST_TSO4 Host can receive TSOv4.\n" },
|
---|
| 130 | { VIRTIONET_F_HOST_TSO6, " HOST_TSO6 Host can receive TSOv6.\n" },
|
---|
| 131 | { VIRTIONET_F_HOST_ECN, " HOST_ECN Host can receive TSO with ECN.\n" },
|
---|
| 132 | { VIRTIONET_F_HOST_UFO, " HOST_UFO Host can receive UFO.\n" },
|
---|
[92939] | 133 | { VIRTIONET_F_MQ, " MQ Host supports multiqueue with automatic receive steering.\n" },
|
---|
| 134 | { VIRTIONET_F_CSUM, " CSUM Host handles packets with partial checksum.\n" },
|
---|
[92091] | 135 | { VIRTIONET_F_MRG_RXBUF, " MRG_RXBUF Guest can merge receive buffers.\n" },
|
---|
| 136 | };
|
---|
[93012] | 137 | #endif
|
---|
[92091] | 138 |
|
---|
[82681] | 139 | #ifdef VIRTIONET_WITH_GSO
|
---|
| 140 | # define VIRTIONET_HOST_FEATURES_GSO \
|
---|
| 141 | VIRTIONET_F_CSUM \
|
---|
| 142 | | VIRTIONET_F_HOST_TSO4 \
|
---|
| 143 | | VIRTIONET_F_HOST_TSO6 \
|
---|
| 144 | | VIRTIONET_F_HOST_UFO \
|
---|
| 145 | | VIRTIONET_F_GUEST_TSO4 \
|
---|
| 146 | | VIRTIONET_F_GUEST_TSO6 \
|
---|
| 147 | | VIRTIONET_F_GUEST_UFO \
|
---|
| 148 | | VIRTIONET_F_GUEST_CSUM /* @bugref(4796) Guest must handle partial chksums */
|
---|
| 149 | #else
|
---|
| 150 | # define VIRTIONET_HOST_FEATURES_GSO
|
---|
| 151 | #endif
|
---|
| 152 |
|
---|
| 153 | #define VIRTIONET_HOST_FEATURES_OFFERED \
|
---|
[82961] | 154 | VIRTIONET_F_STATUS \
|
---|
| 155 | | VIRTIONET_F_GUEST_ANNOUNCE \
|
---|
| 156 | | VIRTIONET_F_MAC \
|
---|
[82681] | 157 | | VIRTIONET_F_CTRL_VQ \
|
---|
| 158 | | VIRTIONET_F_CTRL_RX \
|
---|
| 159 | | VIRTIONET_F_CTRL_VLAN \
|
---|
[82863] | 160 | | VIRTIONET_HOST_FEATURES_GSO \
|
---|
[84430] | 161 | | VIRTIONET_F_MRG_RXBUF
|
---|
[82681] | 162 |
|
---|
[91703] | 163 | #define FEATURE_ENABLED(feature) RT_BOOL(!!(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature))
|
---|
[85415] | 164 | #define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
|
---|
| 165 | #define FEATURE_OFFERED(feature) VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
|
---|
| 166 |
|
---|
| 167 | #if FEATURE_OFFERED(MQ)
|
---|
[92939] | 168 | /* Instance data doesn't allow an array large enough to contain VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX entries */
|
---|
| 169 | # define VIRTIONET_MAX_QPAIRS 1 /* This should be increased at some point and made to work */
|
---|
[85415] | 170 | #else
|
---|
[92939] | 171 | # define VIRTIONET_MAX_QPAIRS VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN /* default, VirtIO 1.0, 5.1.6.5.5 */
|
---|
[85415] | 172 | #endif
|
---|
| 173 |
|
---|
[92939] | 174 | #define VIRTIONET_CTRL_MQ_VQ_PAIRS 64
|
---|
| 175 | #define VIRTIONET_MAX_WORKERS VIRTIONET_MAX_QPAIRS + 1
|
---|
[85415] | 176 | #define VIRTIONET_MAX_VIRTQS (VIRTIONET_MAX_QPAIRS * 2 + 1)
|
---|
| 177 | #define VIRTIONET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP pkt size + Eth. header w/VLAN tag */
|
---|
[92939] | 178 | #define VIRTIONET_MAC_FILTER_LEN 64
|
---|
| 179 | #define VIRTIONET_MAX_VLAN_ID 4096
|
---|
[85415] | 180 | #define VIRTIONET_RX_SEG_COUNT 32
|
---|
| 181 |
|
---|
| 182 | #define VIRTQNAME(uVirtqNbr) (pThis->aVirtqs[uVirtqNbr]->szName)
|
---|
| 183 | #define CBVIRTQNAME(uVirtqNbr) RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
|
---|
| 184 |
|
---|
| 185 | #define IS_TX_VIRTQ(n) ((n) != CTRLQIDX && ((n) & 1))
|
---|
| 186 | #define IS_RX_VIRTQ(n) ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
|
---|
| 187 | #define IS_CTRL_VIRTQ(n) ((n) == CTRLQIDX)
|
---|
[92939] | 188 |
|
---|
| 189 | /*
|
---|
| 190 | * Macros to calculate queue type-pecific index number regardless of scale. VirtIO 1.0, 5.1.2
|
---|
| 191 | */
|
---|
[85415] | 192 | #define RXQIDX(qPairIdx) (qPairIdx * 2)
|
---|
| 193 | #define TXQIDX(qPairIdx) (RXQIDX(qPairIdx) + 1)
|
---|
| 194 | #define CTRLQIDX (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
|
---|
| 195 |
|
---|
[90931] | 196 | #define IS_LINK_UP(pState) !!(pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
|
---|
[85415] | 197 | #define IS_LINK_DOWN(pState) !IS_LINK_UP(pState)
|
---|
| 198 |
|
---|
| 199 | #define SET_LINK_UP(pState) \
|
---|
| 200 | LogFunc(("SET_LINK_UP\n")); \
|
---|
| 201 | pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
|
---|
| 202 | virtioCoreNotifyConfigChanged(&pThis->Virtio)
|
---|
| 203 |
|
---|
| 204 | #define SET_LINK_DOWN(pState) \
|
---|
| 205 | LogFunc(("SET_LINK_DOWN\n")); \
|
---|
| 206 | pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
|
---|
| 207 | virtioCoreNotifyConfigChanged(&pThis->Virtio)
|
---|
| 208 |
|
---|
| 209 | #define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
|
---|
| 210 | (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
|
---|
| 211 |
|
---|
[91703] | 212 | #define PCI_DEVICE_ID_VIRTIONET_HOST 0x1000 /**< VirtIO transitional device ID for network card */
|
---|
[82681] | 213 | #define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
|
---|
| 214 | #define VIRTIONET_PCI_CLASS 0x01 /**< Base class Mass Storage? */
|
---|
| 215 |
|
---|
| 216 | /**
|
---|
[92939] | 217 | * VirtIO Network (virtio-net) device-specific configuration subregion (VirtIO 1.0, 5.1.4)
|
---|
| 218 | * Guest MMIO is processed through callback to VirtIO core which forwards references to network configuration
|
---|
| 219 | * fields to this device-specific code through a callback.
|
---|
[82681] | 220 | */
|
---|
[85016] | 221 | #pragma pack(1)
|
---|
[82826] | 222 |
|
---|
[85016] | 223 | typedef struct virtio_net_config
|
---|
| 224 | {
|
---|
| 225 | RTMAC uMacAddress; /**< mac */
|
---|
| 226 |
|
---|
| 227 | #if FEATURE_OFFERED(STATUS)
|
---|
| 228 | uint16_t uStatus; /**< status */
|
---|
[82681] | 229 | #endif
|
---|
[85016] | 230 |
|
---|
| 231 | #if FEATURE_OFFERED(MQ)
|
---|
| 232 | uint16_t uMaxVirtqPairs; /**< max_virtq_pairs */
|
---|
[82681] | 233 | #endif
|
---|
[85016] | 234 |
|
---|
| 235 | } VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
|
---|
| 236 |
|
---|
[82826] | 237 | #pragma pack()
|
---|
[82681] | 238 |
|
---|
[83499] | 239 | #define VIRTIONET_F_LINK_UP 1 /**< config status: Link is up */
|
---|
| 240 | #define VIRTIONET_F_ANNOUNCE 2 /**< config status: Announce */
|
---|
[82681] | 241 |
|
---|
| 242 | /** @name VirtIO 1.0 NET Host Device device specific control types
|
---|
| 243 | * @{ */
|
---|
[83664] | 244 | #define VIRTIONET_HDR_F_NEEDS_CSUM 1 /**< flags: Packet needs checksum */
|
---|
| 245 | #define VIRTIONET_HDR_GSO_NONE 0 /**< gso_type: No Global Segmentation Offset */
|
---|
| 246 | #define VIRTIONET_HDR_GSO_TCPV4 1 /**< gso_type: Global Segment Offset for TCPV4 */
|
---|
| 247 | #define VIRTIONET_HDR_GSO_UDP 3 /**< gso_type: Global Segment Offset for UDP */
|
---|
| 248 | #define VIRTIONET_HDR_GSO_TCPV6 4 /**< gso_type: Global Segment Offset for TCPV6 */
|
---|
| 249 | #define VIRTIONET_HDR_GSO_ECN 0x80 /**< gso_type: Explicit Congestion Notification */
|
---|
[82681] | 250 | /** @} */
|
---|
| 251 |
|
---|
| 252 | /* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
|
---|
| 253 | #pragma pack(1)
|
---|
[83165] | 254 | struct virtio_net_pkt_hdr {
|
---|
[84876] | 255 | uint8_t uFlags; /**< flags */
|
---|
| 256 | uint8_t uGsoType; /**< gso_type */
|
---|
| 257 | uint16_t uHdrLen; /**< hdr_len */
|
---|
| 258 | uint16_t uGsoSize; /**< gso_size */
|
---|
| 259 | uint16_t uChksumStart; /**< Chksum_start */
|
---|
| 260 | uint16_t uChksumOffset; /**< Chksum_offset */
|
---|
| 261 | uint16_t uNumBuffers; /**< num_buffers */
|
---|
[82681] | 262 | };
|
---|
| 263 | #pragma pack()
|
---|
[84819] | 264 | typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
|
---|
| 265 | AssertCompileSize(VIRTIONETPKTHDR, 12);
|
---|
[82681] | 266 |
|
---|
| 267 | /* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
|
---|
| 268 | #pragma pack(1)
|
---|
| 269 | struct virtio_net_ctrl_hdr {
|
---|
| 270 | uint8_t uClass; /**< class */
|
---|
| 271 | uint8_t uCmd; /**< command */
|
---|
| 272 | };
|
---|
| 273 | #pragma pack()
|
---|
[82775] | 274 | typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
|
---|
[82681] | 275 |
|
---|
[82775] | 276 | typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
|
---|
[82681] | 277 |
|
---|
| 278 | /* Command entry fAck values */
|
---|
[92939] | 279 | #define VIRTIONET_OK 0 /**< Internal success status */
|
---|
| 280 | #define VIRTIONET_ERROR 1 /**< Internal failure status */
|
---|
[82681] | 281 |
|
---|
| 282 | /** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
|
---|
| 283 | * @{ */
|
---|
| 284 | #define VIRTIONET_CTRL_RX 0 /**< Control class: Receive filtering */
|
---|
| 285 | #define VIRTIONET_CTRL_RX_PROMISC 0 /**< Promiscuous mode */
|
---|
| 286 | #define VIRTIONET_CTRL_RX_ALLMULTI 1 /**< All-multicast receive */
|
---|
| 287 | #define VIRTIONET_CTRL_RX_ALLUNI 2 /**< All-unicast receive */
|
---|
| 288 | #define VIRTIONET_CTRL_RX_NOMULTI 3 /**< No multicast receive */
|
---|
| 289 | #define VIRTIONET_CTRL_RX_NOUNI 4 /**< No unicast receive */
|
---|
| 290 | #define VIRTIONET_CTRL_RX_NOBCAST 5 /**< No broadcast receive */
|
---|
| 291 | /** @} */
|
---|
| 292 |
|
---|
[82775] | 293 | typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
|
---|
| 294 | typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
|
---|
[82681] | 295 | typedef uint8_t VIRTIONET_CTRL_MAC_ENTRIES[][6];
|
---|
| 296 |
|
---|
| 297 | /** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
|
---|
| 298 | * @{ */
|
---|
[84876] | 299 | #define VIRTIONET_CTRL_MAC 1 /**< Control class: MAC address filtering */
|
---|
| 300 | #define VIRTIONET_CTRL_MAC_TABLE_SET 0 /**< Set MAC table */
|
---|
| 301 | #define VIRTIONET_CTRL_MAC_ADDR_SET 1 /**< Set default MAC address */
|
---|
[82681] | 302 | /** @} */
|
---|
| 303 |
|
---|
| 304 | /** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
|
---|
| 305 | * @{ */
|
---|
[84876] | 306 | #define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */
|
---|
| 307 | #define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */
|
---|
| 308 | #define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */
|
---|
[82681] | 309 | /** @} */
|
---|
| 310 |
|
---|
| 311 | /** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
|
---|
| 312 | * @{ */
|
---|
[84876] | 313 | #define VIRTIONET_CTRL_ANNOUNCE 3 /**< Control class: Gratuitous Packet Sending */
|
---|
| 314 | #define VIRTIONET_CTRL_ANNOUNCE_ACK 0 /**< Gratuitous Packet Sending ACK */
|
---|
[82681] | 315 | /** @} */
|
---|
| 316 |
|
---|
| 317 | struct virtio_net_ctrl_mq {
|
---|
[84876] | 318 | uint16_t uVirtqueuePairs; /**< virtqueue_pairs */
|
---|
[82681] | 319 | };
|
---|
| 320 |
|
---|
| 321 | /** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
|
---|
| 322 | * @{ */
|
---|
[84876] | 323 | #define VIRTIONET_CTRL_MQ 4 /**< Control class: Receive steering */
|
---|
| 324 | #define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET 0 /**< Set number of TX/RX queues */
|
---|
| 325 | #define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN 1 /**< Minimum number of TX/RX queues */
|
---|
| 326 | #define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 /**< Maximum number of TX/RX queues */
|
---|
[82681] | 327 | /** @} */
|
---|
| 328 |
|
---|
[84876] | 329 | uint64_t uOffloads; /**< offloads */
|
---|
[82681] | 330 |
|
---|
| 331 | /** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
|
---|
| 332 | * @{ */
|
---|
[85415] | 333 | #define VIRTIONET_CTRL_GUEST_OFFLOADS 5 /**< Control class: Offloads state configuration */
|
---|
[92939] | 334 | #define VIRTIONET_CTRL_GUEST_OFFLOADS_SET 0 /**< Apply new offloads configuration */
|
---|
[82681] | 335 | /** @} */
|
---|
| 336 |
|
---|
[92091] | 337 | typedef enum VIRTIONETPKTHDRTYPE
|
---|
| 338 | {
|
---|
[92939] | 339 | kVirtioNetUninitializedPktHdrType = 0, /**< Uninitialized (default) packet header type */
|
---|
| 340 | kVirtioNetModernPktHdrWithoutMrgRx = 1, /**< Packets should not be merged (modern driver) */
|
---|
| 341 | kVirtioNetModernPktHdrWithMrgRx = 2, /**< Packets should be merged (modern driver) */
|
---|
| 342 | kVirtioNetLegacyPktHdrWithoutMrgRx = 3, /**< Packets should not be merged (legacy driver) */
|
---|
| 343 | kVirtioNetLegacyPktHdrWithMrgRx = 4, /**< Packets should be merged (legacy driver) */
|
---|
[92091] | 344 | kVirtioNetFor32BitHack = 0x7fffffff
|
---|
| 345 | } VIRTIONETPKTHDRTYPE;
|
---|
| 346 |
|
---|
[82681] | 347 | /**
|
---|
[84774] | 348 | * device-specific queue info
|
---|
| 349 | */
|
---|
| 350 | struct VIRTIONETWORKER;
|
---|
| 351 | struct VIRTIONETWORKERR3;
|
---|
| 352 |
|
---|
[84803] | 353 | typedef struct VIRTIONETVIRTQ
|
---|
[84774] | 354 | {
|
---|
[85415] | 355 | uint16_t uIdx; /**< Index of this queue */
|
---|
[84774] | 356 | uint16_t align;
|
---|
[84876] | 357 | bool fCtlVirtq; /**< If set this queue is the control queue */
|
---|
| 358 | bool fHasWorker; /**< If set this queue has an associated worker */
|
---|
| 359 | bool fAttachedToVirtioCore; /**< Set if queue attached to virtio core */
|
---|
[92939] | 360 | char szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name */
|
---|
[84803] | 361 | } VIRTIONETVIRTQ, *PVIRTIONETVIRTQ;
|
---|
[84774] | 362 |
|
---|
| 363 | /**
|
---|
[82681] | 364 | * Worker thread context, shared state.
|
---|
| 365 | */
|
---|
| 366 | typedef struct VIRTIONETWORKER
|
---|
| 367 | {
|
---|
[84876] | 368 | SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
|
---|
[85415] | 369 | uint16_t uIdx; /**< Index of this worker */
|
---|
[84876] | 370 | bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
|
---|
| 371 | bool volatile fNotified; /**< Flags whether worker thread notified */
|
---|
| 372 | bool fAssigned; /**< Flags whether worker thread has been set up */
|
---|
[84774] | 373 | uint8_t pad;
|
---|
[82681] | 374 | } VIRTIONETWORKER;
|
---|
[84774] | 375 | /** Pointer to a virtio net worker. */
|
---|
[82681] | 376 | typedef VIRTIONETWORKER *PVIRTIONETWORKER;
|
---|
| 377 |
|
---|
| 378 | /**
|
---|
| 379 | * Worker thread context, ring-3 state.
|
---|
| 380 | */
|
---|
| 381 | typedef struct VIRTIONETWORKERR3
|
---|
| 382 | {
|
---|
[84876] | 383 | R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
|
---|
[85415] | 384 | uint16_t uIdx; /**< Index of this worker */
|
---|
[84774] | 385 | uint16_t pad;
|
---|
[82681] | 386 | } VIRTIONETWORKERR3;
|
---|
[84774] | 387 | /** Pointer to a virtio net worker. */
|
---|
[82681] | 388 | typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
|
---|
| 389 |
|
---|
| 390 | /**
|
---|
| 391 | * VirtIO Host NET device state, shared edition.
|
---|
| 392 | *
|
---|
| 393 | * @extends VIRTIOCORE
|
---|
| 394 | */
|
---|
| 395 | typedef struct VIRTIONET
|
---|
| 396 | {
|
---|
| 397 | /** The core virtio state. */
|
---|
| 398 | VIRTIOCORE Virtio;
|
---|
| 399 |
|
---|
| 400 | /** Virtio device-specific configuration */
|
---|
| 401 | VIRTIONET_CONFIG_T virtioNetConfig;
|
---|
| 402 |
|
---|
| 403 | /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
|
---|
[84803] | 404 | VIRTIONETWORKER aWorkers[VIRTIONET_MAX_VIRTQS];
|
---|
[82681] | 405 |
|
---|
| 406 | /** Track which VirtIO queues we've attached to */
|
---|
[84803] | 407 | VIRTIONETVIRTQ aVirtqs[VIRTIONET_MAX_VIRTQS];
|
---|
[82681] | 408 |
|
---|
[84774] | 409 | /** PDM device Instance name */
|
---|
| 410 | char szInst[16];
|
---|
[82681] | 411 |
|
---|
[84774] | 412 | /** VirtIO features negotiated with the guest, including generic core and device specific */
|
---|
| 413 | uint64_t fNegotiatedFeatures;
|
---|
[82681] | 414 |
|
---|
[84774] | 415 | /** Number of Rx/Tx queue pairs (only one if MQ feature not negotiated */
|
---|
[82681] | 416 | uint16_t cVirtqPairs;
|
---|
| 417 |
|
---|
[85415] | 418 | /** Number of Rx/Tx queue pairs that have already been initialized */
|
---|
| 419 | uint16_t cInitializedVirtqPairs;
|
---|
| 420 |
|
---|
[84774] | 421 | /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
|
---|
[91703] | 422 | uint16_t cVirtqs;
|
---|
[82681] | 423 |
|
---|
[84774] | 424 | /** Number of worker threads (one for the control queue and one for each Tx queue) */
|
---|
[83913] | 425 | uint16_t cWorkers;
|
---|
| 426 |
|
---|
[92939] | 427 | /** Alignment */
|
---|
[84774] | 428 | uint16_t alignment;
|
---|
[82681] | 429 |
|
---|
| 430 | /** Indicates transmission in progress -- only one thread is allowed. */
|
---|
| 431 | uint32_t uIsTransmitting;
|
---|
| 432 |
|
---|
[92939] | 433 | /** Link up delay (in milliseconds). */
|
---|
[84774] | 434 | uint32_t cMsLinkUpDelay;
|
---|
[82681] | 435 |
|
---|
[84774] | 436 | /** The number of actually used slots in aMacMulticastFilter. */
|
---|
| 437 | uint32_t cMulticastFilterMacs;
|
---|
| 438 |
|
---|
| 439 | /** The number of actually used slots in aMacUniicastFilter. */
|
---|
| 440 | uint32_t cUnicastFilterMacs;
|
---|
| 441 |
|
---|
| 442 | /** Semaphore leaf device's thread waits on until guest driver sends empty Rx bufs */
|
---|
| 443 | SUPSEMEVENT hEventRxDescAvail;
|
---|
| 444 |
|
---|
| 445 | /** Array of MAC multicast addresses accepted by RX filter. */
|
---|
| 446 | RTMAC aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
|
---|
| 447 |
|
---|
| 448 | /** Array of MAC unicast addresses accepted by RX filter. */
|
---|
| 449 | RTMAC aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
|
---|
| 450 |
|
---|
[82775] | 451 | /** Default MAC address which rx filtering accepts */
|
---|
| 452 | RTMAC rxFilterMacDefault;
|
---|
| 453 |
|
---|
[84774] | 454 | /** MAC address obtained from the configuration. */
|
---|
| 455 | RTMAC macConfigured;
|
---|
[82681] | 456 |
|
---|
[84774] | 457 | /** Bit array of VLAN filter, one bit per VLAN ID. */
|
---|
| 458 | uint8_t aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
|
---|
[82681] | 459 |
|
---|
[84774] | 460 | /** Set if PDM leaf device at the network interface is starved for Rx buffers */
|
---|
| 461 | bool volatile fLeafWantsEmptyRxBufs;
|
---|
[82681] | 462 |
|
---|
| 463 | /** Number of packet being sent/received to show in debug log. */
|
---|
[82775] | 464 | uint32_t uPktNo;
|
---|
[82681] | 465 |
|
---|
[82775] | 466 | /** Flags whether VirtIO core is in ready state */
|
---|
| 467 | uint8_t fVirtioReady;
|
---|
| 468 |
|
---|
[82681] | 469 | /** Resetting flag */
|
---|
[82775] | 470 | uint8_t fResetting;
|
---|
[82681] | 471 |
|
---|
| 472 | /** Promiscuous mode -- RX filter accepts all packets. */
|
---|
[82775] | 473 | uint8_t fPromiscuous;
|
---|
| 474 |
|
---|
| 475 | /** All multicast mode -- RX filter accepts all multicast packets. */
|
---|
| 476 | uint8_t fAllMulticast;
|
---|
| 477 |
|
---|
| 478 | /** All unicast mode -- RX filter accepts all unicast packets. */
|
---|
| 479 | uint8_t fAllUnicast;
|
---|
| 480 |
|
---|
| 481 | /** No multicast mode - Supresses multicast receive */
|
---|
[82863] | 482 | uint8_t fNoMulticast;
|
---|
[82775] | 483 |
|
---|
| 484 | /** No unicast mode - Suppresses unicast receive */
|
---|
| 485 | uint8_t fNoUnicast;
|
---|
| 486 |
|
---|
| 487 | /** No broadcast mode - Supresses broadcast receive */
|
---|
| 488 | uint8_t fNoBroadcast;
|
---|
| 489 |
|
---|
[92091] | 490 | /** Type of network pkt header based on guest driver version/features */
|
---|
| 491 | VIRTIONETPKTHDRTYPE ePktHdrType;
|
---|
| 492 |
|
---|
| 493 | /** Size of network pkt header based on guest driver version/features */
|
---|
| 494 | uint16_t cbPktHdr;
|
---|
| 495 |
|
---|
[84774] | 496 | /** True if physical cable is attached in configuration. */
|
---|
| 497 | bool fCableConnected;
|
---|
[82775] | 498 |
|
---|
[92939] | 499 | /** True if this device should offer legacy virtio support to the guest */
|
---|
| 500 | bool fOfferLegacy;
|
---|
| 501 |
|
---|
[83913] | 502 | /** @name Statistic
|
---|
| 503 | * @{ */
|
---|
| 504 | STAMCOUNTER StatReceiveBytes;
|
---|
| 505 | STAMCOUNTER StatTransmitBytes;
|
---|
| 506 | STAMCOUNTER StatReceiveGSO;
|
---|
| 507 | STAMCOUNTER StatTransmitPackets;
|
---|
| 508 | STAMCOUNTER StatTransmitGSO;
|
---|
| 509 | STAMCOUNTER StatTransmitCSum;
|
---|
| 510 | #ifdef VBOX_WITH_STATISTICS
|
---|
| 511 | STAMPROFILE StatReceive;
|
---|
| 512 | STAMPROFILE StatReceiveStore;
|
---|
| 513 | STAMPROFILEADV StatTransmit;
|
---|
| 514 | STAMPROFILE StatTransmitSend;
|
---|
| 515 | STAMPROFILE StatRxOverflow;
|
---|
| 516 | STAMCOUNTER StatRxOverflowWakeup;
|
---|
| 517 | STAMCOUNTER StatTransmitByNetwork;
|
---|
| 518 | STAMCOUNTER StatTransmitByThread;
|
---|
| 519 | /** @} */
|
---|
| 520 | #endif
|
---|
[82681] | 521 | } VIRTIONET;
|
---|
| 522 | /** Pointer to the shared state of the VirtIO Host NET device. */
|
---|
| 523 | typedef VIRTIONET *PVIRTIONET;
|
---|
| 524 |
|
---|
| 525 | /**
|
---|
| 526 | * VirtIO Host NET device state, ring-3 edition.
|
---|
| 527 | *
|
---|
| 528 | * @extends VIRTIOCORER3
|
---|
| 529 | */
|
---|
| 530 | typedef struct VIRTIONETR3
|
---|
| 531 | {
|
---|
| 532 | /** The core virtio ring-3 state. */
|
---|
| 533 | VIRTIOCORER3 Virtio;
|
---|
| 534 |
|
---|
| 535 | /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
|
---|
[84803] | 536 | VIRTIONETWORKERR3 aWorkers[VIRTIONET_MAX_VIRTQS];
|
---|
[82681] | 537 |
|
---|
| 538 | /** The device instance.
|
---|
[90774] | 539 | * @note This is _only_ for use whxen dealing with interface callbacks. */
|
---|
[82681] | 540 | PPDMDEVINSR3 pDevIns;
|
---|
| 541 |
|
---|
| 542 | /** Status LUN: Base interface. */
|
---|
| 543 | PDMIBASE IBase;
|
---|
| 544 |
|
---|
| 545 | /** Status LUN: LED port interface. */
|
---|
| 546 | PDMILEDPORTS ILeds;
|
---|
| 547 |
|
---|
| 548 | /** Status LUN: LED connector (peer). */
|
---|
| 549 | R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
|
---|
| 550 |
|
---|
| 551 | /** Status: LED */
|
---|
| 552 | PDMLED led;
|
---|
| 553 |
|
---|
| 554 | /** Attached network driver. */
|
---|
| 555 | R3PTRTYPE(PPDMIBASE) pDrvBase;
|
---|
| 556 |
|
---|
| 557 | /** Network port interface (down) */
|
---|
| 558 | PDMINETWORKDOWN INetworkDown;
|
---|
| 559 |
|
---|
| 560 | /** Network config port interface (main). */
|
---|
| 561 | PDMINETWORKCONFIG INetworkConfig;
|
---|
| 562 |
|
---|
| 563 | /** Connector of attached network driver. */
|
---|
| 564 | R3PTRTYPE(PPDMINETWORKUP) pDrv;
|
---|
| 565 |
|
---|
| 566 | /** Link Up(/Restore) Timer. */
|
---|
| 567 | TMTIMERHANDLE hLinkUpTimer;
|
---|
| 568 |
|
---|
| 569 | } VIRTIONETR3;
|
---|
[83028] | 570 |
|
---|
[82681] | 571 | /** Pointer to the ring-3 state of the VirtIO Host NET device. */
|
---|
| 572 | typedef VIRTIONETR3 *PVIRTIONETR3;
|
---|
| 573 |
|
---|
| 574 | /**
|
---|
| 575 | * VirtIO Host NET device state, ring-0 edition.
|
---|
| 576 | */
|
---|
| 577 | typedef struct VIRTIONETR0
|
---|
| 578 | {
|
---|
| 579 | /** The core virtio ring-0 state. */
|
---|
| 580 | VIRTIOCORER0 Virtio;
|
---|
| 581 | } VIRTIONETR0;
|
---|
| 582 | /** Pointer to the ring-0 state of the VirtIO Host NET device. */
|
---|
| 583 | typedef VIRTIONETR0 *PVIRTIONETR0;
|
---|
| 584 |
|
---|
| 585 | /**
|
---|
| 586 | * VirtIO Host NET device state, raw-mode edition.
|
---|
| 587 | */
|
---|
| 588 | typedef struct VIRTIONETRC
|
---|
| 589 | {
|
---|
| 590 | /** The core virtio raw-mode state. */
|
---|
| 591 | VIRTIOCORERC Virtio;
|
---|
| 592 | } VIRTIONETRC;
|
---|
| 593 | /** Pointer to the ring-0 state of the VirtIO Host NET device. */
|
---|
| 594 | typedef VIRTIONETRC *PVIRTIONETRC;
|
---|
| 595 |
|
---|
| 596 | /** @typedef VIRTIONETCC
|
---|
| 597 | * The instance data for the current context. */
|
---|
| 598 | typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
|
---|
| 599 |
|
---|
| 600 | /** @typedef PVIRTIONETCC
|
---|
| 601 | * Pointer to the instance data for the current context. */
|
---|
| 602 | typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
|
---|
| 603 |
|
---|
[84774] | 604 | #ifdef IN_RING3
|
---|
| 605 | static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
|
---|
[85415] | 606 | static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
|
---|
[84774] | 607 |
|
---|
[92939] | 608 | /**
|
---|
| 609 | * Helper function used when logging state of a VM thread.
|
---|
| 610 | *
|
---|
| 611 | * @param Thread
|
---|
| 612 | *
|
---|
| 613 | * @return Associated name of thread as a pointer to a zero-terminated string.
|
---|
| 614 | */
|
---|
[84774] | 615 | DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
|
---|
| 616 | {
|
---|
| 617 | if (!pThread)
|
---|
| 618 | return "<null>";
|
---|
| 619 |
|
---|
| 620 | switch(pThread->enmState)
|
---|
| 621 | {
|
---|
| 622 | case PDMTHREADSTATE_INVALID:
|
---|
| 623 | return "invalid state";
|
---|
| 624 | case PDMTHREADSTATE_INITIALIZING:
|
---|
| 625 | return "initializing";
|
---|
| 626 | case PDMTHREADSTATE_SUSPENDING:
|
---|
| 627 | return "suspending";
|
---|
| 628 | case PDMTHREADSTATE_SUSPENDED:
|
---|
| 629 | return "suspended";
|
---|
| 630 | case PDMTHREADSTATE_RESUMING:
|
---|
| 631 | return "resuming";
|
---|
| 632 | case PDMTHREADSTATE_RUNNING:
|
---|
| 633 | return "running";
|
---|
| 634 | case PDMTHREADSTATE_TERMINATING:
|
---|
| 635 | return "terminating";
|
---|
| 636 | case PDMTHREADSTATE_TERMINATED:
|
---|
| 637 | return "terminated";
|
---|
| 638 | default:
|
---|
| 639 | return "unknown state";
|
---|
| 640 | }
|
---|
| 641 | }
|
---|
| 642 | #endif
|
---|
| 643 |
|
---|
[82863] | 644 | /**
|
---|
[92939] | 645 | * Wakeup PDM managed downstream (e.g. hierarchically inferior device's) RX thread
|
---|
[82863] | 646 | */
|
---|
[93014] | 647 | static DECLCALLBACK(void) virtioNetWakeupRxBufWaiter(PPDMDEVINS pDevIns)
|
---|
[82863] | 648 | {
|
---|
| 649 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 650 |
|
---|
| 651 | AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
|
---|
| 652 |
|
---|
[83913] | 653 | STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
|
---|
[84774] | 654 | if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
|
---|
| 655 | {
|
---|
[91703] | 656 | Log10Func(("[%s] Waking downstream device's Rx buf waiter thread\n", pThis->szInst));
|
---|
[84774] | 657 | int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
|
---|
| 658 | AssertRC(rc);
|
---|
| 659 | }
|
---|
[82863] | 660 | }
|
---|
| 661 |
|
---|
[84351] | 662 | /**
|
---|
[92939] | 663 | * Guest notifying us of its activity with a queue. Figure out which queue and respond accordingly.
|
---|
| 664 | *
|
---|
[84803] | 665 | * @callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
|
---|
[84351] | 666 | */
|
---|
[84819] | 667 | static DECLCALLBACK(void) virtioNetVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
|
---|
[84351] | 668 | {
|
---|
| 669 | RT_NOREF(pVirtio);
|
---|
| 670 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 671 |
|
---|
[84819] | 672 | PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
|
---|
[86405] | 673 | PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
|
---|
[84351] | 674 |
|
---|
| 675 | #if defined (IN_RING3) && defined (LOG_ENABLED)
|
---|
[91703] | 676 | RTLogFlush(NULL);
|
---|
[84351] | 677 | #endif
|
---|
[84819] | 678 | if (IS_RX_VIRTQ(uVirtqNbr))
|
---|
[84351] | 679 | {
|
---|
[84876] | 680 | uint16_t cBufsAvailable = virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr);
|
---|
[84774] | 681 |
|
---|
| 682 | if (cBufsAvailable)
|
---|
| 683 | {
|
---|
[86407] | 684 | Log10Func(("%s %u empty bufs added to %s by guest (notifying leaf device)\n",
|
---|
| 685 | pThis->szInst, cBufsAvailable, pVirtq->szName));
|
---|
[84774] | 686 | virtioNetWakeupRxBufWaiter(pDevIns);
|
---|
| 687 | }
|
---|
| 688 | else
|
---|
[92939] | 689 | Log10Func(("%s \n\n***WARNING: %s notified but no empty bufs added by guest! (skip leaf dev. notification)\n\n",
|
---|
[86407] | 690 | pThis->szInst, pVirtq->szName));
|
---|
[84351] | 691 | }
|
---|
[84819] | 692 | else if (IS_TX_VIRTQ(uVirtqNbr) || IS_CTRL_VIRTQ(uVirtqNbr))
|
---|
[84351] | 693 | {
|
---|
| 694 | /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
|
---|
| 695 | if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
|
---|
| 696 | {
|
---|
| 697 | if (ASMAtomicReadBool(&pWorker->fSleeping))
|
---|
| 698 | {
|
---|
[91703] | 699 | Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
|
---|
[86405] | 700 |
|
---|
[84351] | 701 | int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
|
---|
| 702 | AssertRC(rc);
|
---|
| 703 | }
|
---|
[84774] | 704 | else
|
---|
[91703] | 705 | Log10Func(("[%s] %s has available buffers - worker already awake\n", pThis->szInst, pVirtq->szName));
|
---|
[84351] | 706 | }
|
---|
[84774] | 707 | else
|
---|
[91703] | 708 | Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
|
---|
[84351] | 709 | }
|
---|
[84774] | 710 | else
|
---|
[91703] | 711 | LogRelFunc(("[%s] unrecognized queue %s (idx=%d) notified\n", pThis->szInst, pVirtq->szName, uVirtqNbr));
|
---|
[84351] | 712 | }
|
---|
| 713 |
|
---|
| 714 | #ifdef IN_RING3 /* spans most of the file, at the moment. */
|
---|
| 715 |
|
---|
| 716 | /**
|
---|
| 717 | * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
|
---|
| 718 | */
|
---|
| 719 | static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
|
---|
| 720 | {
|
---|
[84774] | 721 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 722 | PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
|
---|
| 723 |
|
---|
[91703] | 724 | Log10Func(("[%s]\n", pThis->szInst));
|
---|
[84803] | 725 | RT_NOREF(pThis);
|
---|
[84774] | 726 | return PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
|
---|
[84351] | 727 | }
|
---|
| 728 |
|
---|
[92939] | 729 | /**
|
---|
| 730 | * Set queue names, distinguishing between modern or legacy mode.
|
---|
| 731 | *
|
---|
| 732 | * @note This makes it obvious during logging which mode this transitional device is
|
---|
| 733 | * operating in, legacy or modern.
|
---|
| 734 | *
|
---|
| 735 | * @param pThis Device specific device state
|
---|
| 736 | * @param fLegacy (input) true if running in legacy mode
|
---|
| 737 | * false if running in modern mode
|
---|
| 738 | */
|
---|
| 739 | DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis, uint32_t fLegacy)
|
---|
[82681] | 740 | {
|
---|
[92939] | 741 | RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, fLegacy ? "legacy-ctrlq" : " modern-ctrlq");
|
---|
| 742 | for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
|
---|
[82681] | 743 | {
|
---|
[92939] | 744 | RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-recvq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
|
---|
| 745 | RTStrPrintf(pThis->aVirtqs[TXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-xmitq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
|
---|
[82681] | 746 | }
|
---|
| 747 | }
|
---|
| 748 |
|
---|
[82863] | 749 | /**
|
---|
| 750 | * Dump a packet to debug log.
|
---|
| 751 | *
|
---|
| 752 | * @param pThis The virtio-net shared instance data.
|
---|
| 753 | * @param pbPacket The packet.
|
---|
| 754 | * @param cb The size of the packet.
|
---|
| 755 | * @param pszText A string denoting direction of packet transfer.
|
---|
| 756 | */
|
---|
| 757 | DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
|
---|
| 758 | {
|
---|
[92939] | 759 | #ifdef LOG_ENABLED
|
---|
[84430] | 760 | if (!LogIs12Enabled())
|
---|
| 761 | return;
|
---|
[92939] | 762 | #endif
|
---|
[84774] | 763 | vboxEthPacketDump(pThis->szInst, pszText, pbPacket, (uint32_t)cb);
|
---|
[82863] | 764 | }
|
---|
[82681] | 765 |
|
---|
[84774] | 766 | #ifdef LOG_ENABLED
|
---|
[84819] | 767 | void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONETPKTHDR pRxPktHdr,
|
---|
[85016] | 768 | uint16_t cVirtqBufs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS GCPhysRxBuf, uint8_t cbRxBuf)
|
---|
[84774] | 769 | {
|
---|
| 770 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
[85016] | 771 | pRxPktHdr->uNumBuffers = cVirtqBufs;
|
---|
[84774] | 772 | if (pRxPktHdr)
|
---|
| 773 | {
|
---|
[92939] | 774 | LogFunc(("%*c\nrxPktHdr\n"
|
---|
| 775 | " uFlags ......... %2.2x\n uGsoType ....... %2.2x\n uHdrLen ........ %4.4x\n"
|
---|
| 776 | " uGsoSize ....... %4.4x\n uChksumStart ... %4.4x\n uChksumOffset .. %4.4x\n",
|
---|
| 777 | 60, ' ', pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
|
---|
| 778 | pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset));
|
---|
[91703] | 779 | if (!virtioCoreIsLegacyMode(&pThis->Virtio) || FEATURE_ENABLED(MRG_RXBUF))
|
---|
[92939] | 780 | LogFunc((" uNumBuffers .... %4.4x\n", pRxPktHdr->uNumBuffers));
|
---|
[84819] | 781 | virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONETPKTHDR), 0, "Dump of virtual rPktHdr");
|
---|
[84774] | 782 | }
|
---|
| 783 | virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
|
---|
| 784 | LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
|
---|
| 785 | virtioCoreGCPhysHexDump(pDevIns, GCPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
|
---|
[92939] | 786 | LogFunc(("%*c", 60, '-'));
|
---|
[84774] | 787 | }
|
---|
| 788 |
|
---|
[84430] | 789 | #endif /* LOG_ENABLED */
|
---|
[84774] | 790 |
|
---|
| 791 | /**
|
---|
| 792 | * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
|
---|
| 793 | */
|
---|
| 794 | static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
|
---|
| 795 | {
|
---|
| 796 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 797 | PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
|
---|
| 798 |
|
---|
| 799 | bool fNone = pszArgs && *pszArgs == '\0';
|
---|
| 800 | bool fAll = pszArgs && (*pszArgs == 'a' || *pszArgs == 'A'); /* "all" */
|
---|
| 801 | bool fNetwork = pszArgs && (*pszArgs == 'n' || *pszArgs == 'N'); /* "network" */
|
---|
| 802 | bool fFeatures = pszArgs && (*pszArgs == 'f' || *pszArgs == 'F'); /* "features" */
|
---|
| 803 | bool fState = pszArgs && (*pszArgs == 's' || *pszArgs == 'S'); /* "state" */
|
---|
| 804 | bool fPointers = pszArgs && (*pszArgs == 'p' || *pszArgs == 'P'); /* "pointers" */
|
---|
[84803] | 805 | bool fVirtqs = pszArgs && (*pszArgs == 'q' || *pszArgs == 'Q'); /* "queues */
|
---|
[84774] | 806 |
|
---|
| 807 | /* Show basic information. */
|
---|
| 808 | pHlp->pfnPrintf(pHlp,
|
---|
| 809 | "\n"
|
---|
| 810 | "---------------------------------------------------------------------------\n"
|
---|
| 811 | "Debug Info: %s\n"
|
---|
| 812 | " (options: [a]ll, [n]et, [f]eatures, [s]tate, [p]ointers, [q]ueues)\n"
|
---|
| 813 | "---------------------------------------------------------------------------\n\n",
|
---|
[84806] | 814 | pThis->szInst);
|
---|
[84774] | 815 |
|
---|
| 816 | if (fNone)
|
---|
| 817 | return;
|
---|
| 818 |
|
---|
| 819 | /* Show offered/unoffered, accepted/rejected features */
|
---|
| 820 | if (fAll || fFeatures)
|
---|
| 821 | {
|
---|
[92091] | 822 | virtioCorePrintDeviceFeatures(&pThis->Virtio, pHlp, s_aDevSpecificFeatures,
|
---|
| 823 | RT_ELEMENTS(s_aDevSpecificFeatures));
|
---|
[84774] | 824 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 825 | }
|
---|
| 826 |
|
---|
| 827 | /* Show queues (and associate worker info if applicable) */
|
---|
[84803] | 828 | if (fAll || fVirtqs)
|
---|
[84774] | 829 | {
|
---|
[84803] | 830 | pHlp->pfnPrintf(pHlp, "Virtq information:\n\n");
|
---|
[91703] | 831 | for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
|
---|
[84774] | 832 | {
|
---|
[84819] | 833 | PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
|
---|
[84774] | 834 |
|
---|
[84803] | 835 | if (pVirtq->fHasWorker)
|
---|
[84774] | 836 | {
|
---|
[86405] | 837 | PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
|
---|
| 838 | PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
|
---|
[84774] | 839 |
|
---|
[86405] | 840 | Assert((pWorker->uIdx == pVirtq->uIdx));
|
---|
| 841 | Assert((pWorkerR3->uIdx == pVirtq->uIdx));
|
---|
| 842 |
|
---|
[84774] | 843 | if (pWorker->fAssigned)
|
---|
| 844 | {
|
---|
| 845 | pHlp->pfnPrintf(pHlp, " %-15s (pThread: %p %s) ",
|
---|
[84803] | 846 | pVirtq->szName,
|
---|
[84774] | 847 | pWorkerR3->pThread,
|
---|
| 848 | virtioNetThreadStateName(pWorkerR3->pThread));
|
---|
[84803] | 849 | if (pVirtq->fAttachedToVirtioCore)
|
---|
[84774] | 850 | {
|
---|
| 851 | pHlp->pfnPrintf(pHlp, "worker: ");
|
---|
| 852 | pHlp->pfnPrintf(pHlp, "%s", pWorker->fSleeping ? "blocking" : "unblocked");
|
---|
| 853 | pHlp->pfnPrintf(pHlp, "%s", pWorker->fNotified ? ", notified" : "");
|
---|
| 854 | }
|
---|
| 855 | else
|
---|
| 856 | if (pWorker->fNotified)
|
---|
| 857 | pHlp->pfnPrintf(pHlp, "not attached to virtio core");
|
---|
| 858 | }
|
---|
| 859 | }
|
---|
| 860 | else
|
---|
| 861 | {
|
---|
[84803] | 862 | pHlp->pfnPrintf(pHlp, " %-15s (INetworkDown's thread) %s", pVirtq->szName,
|
---|
| 863 | pVirtq->fAttachedToVirtioCore ? "" : "not attached to virtio core");
|
---|
[84774] | 864 | }
|
---|
| 865 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
[84819] | 866 | virtioCoreR3VirtqInfo(pDevIns, pHlp, pszArgs, uVirtqNbr);
|
---|
[84774] | 867 | pHlp->pfnPrintf(pHlp, " ---------------------------------------------------------------------\n");
|
---|
| 868 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 869 | }
|
---|
| 870 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 871 | }
|
---|
| 872 |
|
---|
| 873 | /* Show various pointers */
|
---|
| 874 | if (fAll || fPointers)
|
---|
| 875 | {
|
---|
[92939] | 876 | pHlp->pfnPrintf(pHlp, "Internal Pointers (for instance \"%s\"):\n\n", pThis->szInst);
|
---|
[84876] | 877 | pHlp->pfnPrintf(pHlp, " pDevIns ................... %p\n", pDevIns);
|
---|
[92939] | 878 | pHlp->pfnPrintf(pHlp, " PVIRTIOCORE ............... %p\n", &pThis->Virtio);
|
---|
[84876] | 879 | pHlp->pfnPrintf(pHlp, " PVIRTIONET ................ %p\n", pThis);
|
---|
| 880 | pHlp->pfnPrintf(pHlp, " PVIRTIONETCC .............. %p\n", pThisCC);
|
---|
[92939] | 881 | pHlp->pfnPrintf(pHlp, " VIRTIONETVIRTQ[] .......... %p\n", pThis->aVirtqs);
|
---|
[84876] | 882 | pHlp->pfnPrintf(pHlp, " pDrvBase .................. %p\n", pThisCC->pDrvBase);
|
---|
| 883 | pHlp->pfnPrintf(pHlp, " pDrv ...................... %p\n", pThisCC->pDrv);
|
---|
[84774] | 884 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 885 | }
|
---|
| 886 |
|
---|
| 887 | /* Show device state info */
|
---|
| 888 | if (fAll || fState)
|
---|
| 889 | {
|
---|
| 890 | pHlp->pfnPrintf(pHlp, "Device state:\n\n");
|
---|
| 891 | uint32_t fTransmitting = ASMAtomicReadU32(&pThis->uIsTransmitting);
|
---|
| 892 |
|
---|
| 893 | pHlp->pfnPrintf(pHlp, " Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
|
---|
| 894 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
[84876] | 895 | pHlp->pfnPrintf(pHlp, "Misc state\n");
|
---|
| 896 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
[92939] | 897 | pHlp->pfnPrintf(pHlp, " fOfferLegacy .............. %d\n", pThis->fOfferLegacy);
|
---|
[84876] | 898 | pHlp->pfnPrintf(pHlp, " fVirtioReady .............. %d\n", pThis->fVirtioReady);
|
---|
[92939] | 899 | pHlp->pfnPrintf(pHlp, " fResetting ................ %d\n", pThis->fResetting);
|
---|
[84876] | 900 | pHlp->pfnPrintf(pHlp, " fGenUpdatePending ......... %d\n", pThis->Virtio.fGenUpdatePending);
|
---|
| 901 | pHlp->pfnPrintf(pHlp, " fMsiSupport ............... %d\n", pThis->Virtio.fMsiSupport);
|
---|
| 902 | pHlp->pfnPrintf(pHlp, " uConfigGeneration ......... %d\n", pThis->Virtio.uConfigGeneration);
|
---|
[85016] | 903 | pHlp->pfnPrintf(pHlp, " uDeviceStatus ............. 0x%x\n", pThis->Virtio.fDeviceStatus);
|
---|
[84876] | 904 | pHlp->pfnPrintf(pHlp, " cVirtqPairs .,............. %d\n", pThis->cVirtqPairs);
|
---|
[91703] | 905 | pHlp->pfnPrintf(pHlp, " cVirtqs .,................. %d\n", pThis->cVirtqs);
|
---|
[84876] | 906 | pHlp->pfnPrintf(pHlp, " cWorkers .................. %d\n", pThis->cWorkers);
|
---|
[92950] | 907 | pHlp->pfnPrintf(pHlp, " MMIO mapping name ......... %d\n", pThisCC->Virtio.szMmioName);
|
---|
[84876] | 908 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
[84774] | 909 | }
|
---|
| 910 |
|
---|
| 911 | /* Show network related information */
|
---|
| 912 | if (fAll || fNetwork)
|
---|
| 913 | {
|
---|
| 914 | pHlp->pfnPrintf(pHlp, "Network configuration:\n\n");
|
---|
| 915 | pHlp->pfnPrintf(pHlp, " MAC: ...................... %RTmac\n", &pThis->macConfigured);
|
---|
| 916 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 917 | pHlp->pfnPrintf(pHlp, " Cable: .................... %s\n", pThis->fCableConnected ? "connected" : "disconnected");
|
---|
| 918 | pHlp->pfnPrintf(pHlp, " Link-up delay: ............ %d ms\n", pThis->cMsLinkUpDelay);
|
---|
| 919 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 920 | pHlp->pfnPrintf(pHlp, " Accept all multicast: ..... %s\n", pThis->fAllMulticast ? "true" : "false");
|
---|
| 921 | pHlp->pfnPrintf(pHlp, " Suppress broadcast: ....... %s\n", pThis->fNoBroadcast ? "true" : "false");
|
---|
| 922 | pHlp->pfnPrintf(pHlp, " Suppress unicast: ......... %s\n", pThis->fNoUnicast ? "true" : "false");
|
---|
| 923 | pHlp->pfnPrintf(pHlp, " Suppress multicast: ....... %s\n", pThis->fNoMulticast ? "true" : "false");
|
---|
| 924 | pHlp->pfnPrintf(pHlp, " Promiscuous: .............. %s\n", pThis->fPromiscuous ? "true" : "false");
|
---|
| 925 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 926 | pHlp->pfnPrintf(pHlp, " Default Rx MAC filter: .... %RTmac\n", pThis->rxFilterMacDefault);
|
---|
| 927 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 928 |
|
---|
| 929 | pHlp->pfnPrintf(pHlp, " Unicast filter MACs:\n");
|
---|
| 930 |
|
---|
| 931 | if (!pThis->cUnicastFilterMacs)
|
---|
| 932 | pHlp->pfnPrintf(pHlp, " <none>\n");
|
---|
| 933 |
|
---|
| 934 | for (uint32_t i = 0; i < pThis->cUnicastFilterMacs; i++)
|
---|
| 935 | pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacUnicastFilter[i]);
|
---|
| 936 |
|
---|
| 937 | pHlp->pfnPrintf(pHlp, "\n Multicast filter MACs:\n");
|
---|
| 938 |
|
---|
| 939 | if (!pThis->cMulticastFilterMacs)
|
---|
| 940 | pHlp->pfnPrintf(pHlp, " <none>\n");
|
---|
| 941 |
|
---|
| 942 | for (uint32_t i = 0; i < pThis->cMulticastFilterMacs; i++)
|
---|
| 943 | pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacMulticastFilter[i]);
|
---|
| 944 |
|
---|
| 945 | pHlp->pfnPrintf(pHlp, "\n\n");
|
---|
| 946 | pHlp->pfnPrintf(pHlp, " Leaf starved: ............. %s\n", pThis->fLeafWantsEmptyRxBufs ? "true" : "false");
|
---|
| 947 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 948 | }
|
---|
[84819] | 949 | /** @todo implement this
|
---|
| 950 | * pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 951 | * virtioCoreR3Info(pDevIns, pHlp, pszArgs);
|
---|
| 952 | */
|
---|
[84774] | 953 | pHlp->pfnPrintf(pHlp, "\n");
|
---|
| 954 | }
|
---|
| 955 |
|
---|
[92939] | 956 | /**
|
---|
| 957 | * Checks whether certain mutually dependent negotiated features are clustered in required combinations.
|
---|
| 958 | *
|
---|
| 959 | * @note See VirtIO 1.0 spec, Section 5.1.3.1
|
---|
| 960 | *
|
---|
| 961 | * @param fFeatures Bitmask of negotiated features to evaluate
|
---|
| 962 | *
|
---|
| 963 | * @returns true if valid feature combination(s) found.
|
---|
| 964 | * false if non-valid feature set.
|
---|
| 965 | */
|
---|
[82681] | 966 | DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
|
---|
| 967 | {
|
---|
[82961] | 968 | uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
|
---|
| 969 | || fFeatures & VIRTIONET_F_GUEST_TSO6
|
---|
| 970 | || fFeatures & VIRTIONET_F_GUEST_UFO;
|
---|
[82681] | 971 |
|
---|
[82961] | 972 | uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
|
---|
| 973 | || fFeatures & VIRTIONET_F_HOST_TSO6
|
---|
| 974 | || fFeatures & VIRTIONET_F_HOST_UFO;
|
---|
[82681] | 975 |
|
---|
[82961] | 976 | uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
|
---|
| 977 | || fFeatures & VIRTIONET_F_CTRL_VLAN
|
---|
| 978 | || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
|
---|
| 979 | || fFeatures & VIRTIONET_F_MQ
|
---|
| 980 | || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
|
---|
[82681] | 981 |
|
---|
[82775] | 982 | if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
|
---|
[82681] | 983 | return false;
|
---|
| 984 |
|
---|
[82775] | 985 | if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
|
---|
[82681] | 986 | return false;
|
---|
| 987 |
|
---|
| 988 | if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
|
---|
| 989 | return false;
|
---|
| 990 |
|
---|
| 991 | if ( fFeatures & VIRTIONET_F_GUEST_ECN
|
---|
| 992 | && !( fFeatures & VIRTIONET_F_GUEST_TSO4
|
---|
| 993 | || fFeatures & VIRTIONET_F_GUEST_TSO6))
|
---|
| 994 | return false;
|
---|
| 995 |
|
---|
| 996 | if ( fFeatures & VIRTIONET_F_HOST_ECN
|
---|
| 997 | && !( fFeatures & VIRTIONET_F_HOST_TSO4
|
---|
| 998 | || fFeatures & VIRTIONET_F_HOST_TSO6))
|
---|
| 999 | return false;
|
---|
| 1000 | return true;
|
---|
| 1001 | }
|
---|
| 1002 |
|
---|
[92939] | 1003 | /**
|
---|
| 1004 | * Read or write device-specific configuration parameters.
|
---|
| 1005 | * This is called by VirtIO core code a guest-initiated MMIO access is made to access device-specific
|
---|
| 1006 | * configuration
|
---|
| 1007 | *
|
---|
| 1008 | * @note See VirtIO 1.0 spec, 2.3 Device Configuration Space
|
---|
| 1009 | *
|
---|
| 1010 | * @param pThis Pointer to device-specific state
|
---|
| 1011 | * @param uOffsetOfAccess Offset (within VIRTIONET_CONFIG_T)
|
---|
| 1012 | * @param pv Pointer to data to read or write
|
---|
| 1013 | * @param cb Number of bytes to read or write
|
---|
| 1014 | * @param fWrite True if writing, false if reading
|
---|
| 1015 | *
|
---|
| 1016 | * @returns VINF_SUCCESS if successful, or VINF_IOM_MMIO_UNUSED if fails (bad offset or size)
|
---|
| 1017 | */
|
---|
[91703] | 1018 | static int virtioNetR3DevCfgAccess(PVIRTIONET pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
|
---|
[82681] | 1019 | {
|
---|
| 1020 | AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
|
---|
| 1021 |
|
---|
[85016] | 1022 | if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess))
|
---|
| 1023 | VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
|
---|
[82863] | 1024 | #if FEATURE_OFFERED(STATUS)
|
---|
[82681] | 1025 | else
|
---|
[95609] | 1026 | if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess))
|
---|
[85016] | 1027 | VIRTIO_DEV_CONFIG_ACCESS_READONLY( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
|
---|
[82681] | 1028 | #endif
|
---|
[82863] | 1029 | #if FEATURE_OFFERED(MQ)
|
---|
[82681] | 1030 | else
|
---|
[85016] | 1031 | if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess))
|
---|
| 1032 | VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
|
---|
[82681] | 1033 | #endif
|
---|
| 1034 | else
|
---|
| 1035 | {
|
---|
[85016] | 1036 | LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n",
|
---|
| 1037 | pThis->szInst, uOffsetOfAccess, uOffsetOfAccess, cb));
|
---|
[82681] | 1038 | return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
|
---|
| 1039 | }
|
---|
| 1040 | return VINF_SUCCESS;
|
---|
| 1041 | }
|
---|
| 1042 |
|
---|
| 1043 | /**
|
---|
| 1044 | * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
|
---|
| 1045 | */
|
---|
| 1046 | static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
|
---|
| 1047 | {
|
---|
[82961] | 1048 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 1049 |
|
---|
[84048] | 1050 | RT_NOREF(pThis);
|
---|
[91703] | 1051 | return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
|
---|
[82681] | 1052 | }
|
---|
| 1053 |
|
---|
| 1054 | /**
|
---|
| 1055 | * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
|
---|
| 1056 | */
|
---|
| 1057 | static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
|
---|
| 1058 | {
|
---|
[82961] | 1059 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 1060 |
|
---|
[98063] | 1061 | Log10Func(("[%s] uOffset: %u, cb: %u: %.*Rhxs\n", pThis->szInst, uOffset, cb, cb, pv));
|
---|
[84048] | 1062 | RT_NOREF(pThis);
|
---|
[91703] | 1063 | return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
|
---|
[82681] | 1064 | }
|
---|
| 1065 |
|
---|
[92939] | 1066 | static int virtioNetR3VirtqDestroy(PVIRTIOCORE pVirtio, PVIRTIONETVIRTQ pVirtq)
|
---|
| 1067 | {
|
---|
| 1068 | PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
|
---|
| 1069 | PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pVirtio->pDevInsR3, PVIRTIONETCC);
|
---|
| 1070 | PVIRTIONETWORKER pWorker = &pThis->aWorkers[pVirtq->uIdx];
|
---|
| 1071 | PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[pVirtq->uIdx];
|
---|
[82681] | 1072 |
|
---|
[93000] | 1073 | int rc = VINF_SUCCESS, rcThread;
|
---|
[92939] | 1074 | Log10Func(("[%s] Destroying \"%s\"", pThis->szInst, pVirtq->szName));
|
---|
| 1075 | if (pVirtq->fHasWorker)
|
---|
| 1076 | {
|
---|
| 1077 | Log10((" and its worker"));
|
---|
[100398] | 1078 | rc = PDMDevHlpThreadDestroy(pVirtio->pDevInsR3, pWorkerR3->pThread, &rcThread);
|
---|
| 1079 | AssertRCReturn(rc, rc);
|
---|
| 1080 | pWorkerR3->pThread = 0;
|
---|
| 1081 |
|
---|
[92939] | 1082 | rc = PDMDevHlpSUPSemEventClose(pVirtio->pDevInsR3, pWorker->hEvtProcess);
|
---|
| 1083 | AssertRCReturn(rc, rc);
|
---|
| 1084 | pWorker->hEvtProcess = 0;
|
---|
[100398] | 1085 |
|
---|
[92939] | 1086 | pVirtq->fHasWorker = false;
|
---|
| 1087 | }
|
---|
| 1088 | pWorker->fAssigned = false;
|
---|
| 1089 | pVirtq->fCtlVirtq = false;
|
---|
| 1090 | Log10(("\n"));
|
---|
| 1091 | return rc;
|
---|
| 1092 | }
|
---|
| 1093 |
|
---|
[97824] | 1094 | /**
|
---|
| 1095 | * Takes down the link temporarily if its current status is up.
|
---|
| 1096 | *
|
---|
| 1097 | * This is used during restore and when replumbing the network link.
|
---|
| 1098 | *
|
---|
| 1099 | * The temporary link outage is supposed to indicate to the OS that all network
|
---|
| 1100 | * connections have been lost and that it for instance is appropriate to
|
---|
| 1101 | * renegotiate any DHCP lease.
|
---|
| 1102 | *
|
---|
| 1103 | * @param pDevIns The device instance.
|
---|
| 1104 | * @param pThis The virtio-net shared instance data.
|
---|
| 1105 | * @param pThisCC The virtio-net ring-3 instance data.
|
---|
| 1106 | */
|
---|
| 1107 | static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
|
---|
| 1108 | {
|
---|
| 1109 | if (IS_LINK_UP(pThis))
|
---|
| 1110 | {
|
---|
| 1111 | SET_LINK_DOWN(pThis);
|
---|
[92939] | 1112 |
|
---|
[97824] | 1113 | /* Re-establish link in 5 seconds. */
|
---|
| 1114 | int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
|
---|
| 1115 | AssertRC(rc);
|
---|
| 1116 |
|
---|
| 1117 | LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
|
---|
| 1118 | }
|
---|
| 1119 | }
|
---|
| 1120 |
|
---|
| 1121 |
|
---|
| 1122 | static void virtioNetConfigurePktHdr(PVIRTIONET pThis, uint32_t fLegacy)
|
---|
| 1123 | {
|
---|
| 1124 | /* Calculate network packet header type and size based on what we know now */
|
---|
| 1125 | pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
|
---|
| 1126 | if (!fLegacy)
|
---|
| 1127 | /* Modern (e.g. >= VirtIO 1.0) device specification's pkt size rules */
|
---|
| 1128 | if (FEATURE_ENABLED(MRG_RXBUF))
|
---|
| 1129 | pThis->ePktHdrType = kVirtioNetModernPktHdrWithMrgRx;
|
---|
| 1130 | else /* Modern guest driver with MRG_RX feature disabled */
|
---|
| 1131 | pThis->ePktHdrType = kVirtioNetModernPktHdrWithoutMrgRx;
|
---|
| 1132 | else
|
---|
| 1133 | {
|
---|
| 1134 | /* Legacy (e.g. < VirtIO 1.0) device specification's pkt size rules */
|
---|
| 1135 | if (FEATURE_ENABLED(MRG_RXBUF))
|
---|
| 1136 | pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithMrgRx;
|
---|
| 1137 | else /* Legacy guest with MRG_RX feature disabled */
|
---|
| 1138 | {
|
---|
| 1139 | pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
|
---|
| 1140 | pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
|
---|
| 1141 | }
|
---|
| 1142 | }
|
---|
| 1143 | }
|
---|
| 1144 |
|
---|
| 1145 |
|
---|
[82681] | 1146 | /*********************************************************************************************************************************
|
---|
| 1147 | * Saved state *
|
---|
| 1148 | *********************************************************************************************************************************/
|
---|
| 1149 |
|
---|
| 1150 | /**
|
---|
| 1151 | * @callback_method_impl{FNSSMDEVLOADEXEC}
|
---|
[92939] | 1152 | *
|
---|
| 1153 | * @note: This is included to accept and migrate VMs that had used the original VirtualBox legacy-only virtio-net (network card)
|
---|
| 1154 | * controller device emulator ("DevVirtioNet.cpp") to work with this superset of VirtIO compatibility known
|
---|
| 1155 | * as a transitional device (see PDM-invoked device constructor comments for more information)
|
---|
[82681] | 1156 | */
|
---|
[92939] | 1157 | static DECLCALLBACK(int) virtioNetR3LegacyDeviceLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass,
|
---|
[98121] | 1158 | RTMAC uMacLoaded)
|
---|
[82681] | 1159 | {
|
---|
| 1160 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 1161 | PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
|
---|
[82775] | 1162 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
[92939] | 1163 | int rc;
|
---|
[82681] | 1164 |
|
---|
[92939] | 1165 | Log7Func(("[%s] LOAD EXEC (LEGACY)!!\n", pThis->szInst));
|
---|
| 1166 |
|
---|
[93165] | 1167 | if ( memcmp(&uMacLoaded.au8, &pThis->macConfigured.au8, sizeof(uMacLoaded))
|
---|
| 1168 | && ( uPass == 0
|
---|
| 1169 | || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
|
---|
[92939] | 1170 | LogRelFunc(("[%s]: The mac address differs: config=%RTmac saved=%RTmac\n",
|
---|
| 1171 | pThis->szInst, &pThis->macConfigured, &uMacLoaded));
|
---|
| 1172 |
|
---|
[93165] | 1173 | if (uPass == SSM_PASS_FINAL)
|
---|
| 1174 | {
|
---|
| 1175 | /* Call the virtio core to have it load legacy device state */
|
---|
| 1176 | rc = virtioCoreR3LegacyDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion, VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY);
|
---|
| 1177 | AssertRCReturn(rc, rc);
|
---|
| 1178 | /*
|
---|
| 1179 | * Scan constructor-determined virtqs to determine if they are all valid-as-restored.
|
---|
| 1180 | * If so, nudge them with a signal, otherwise destroy the unusable queue(s)
|
---|
| 1181 | * to avoid tripping up the other queue processing logic.
|
---|
| 1182 | */
|
---|
| 1183 | int cVirtqsToRemove = 0;
|
---|
| 1184 | for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
|
---|
[92939] | 1185 | {
|
---|
[93165] | 1186 | PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
|
---|
| 1187 | if (pVirtq->fHasWorker)
|
---|
[92939] | 1188 | {
|
---|
[93165] | 1189 | if (!virtioCoreR3VirtqIsEnabled(&pThis->Virtio, uVirtqNbr))
|
---|
[92939] | 1190 | {
|
---|
[93165] | 1191 | virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
|
---|
| 1192 | ++cVirtqsToRemove;
|
---|
| 1193 | }
|
---|
| 1194 | else
|
---|
| 1195 | {
|
---|
| 1196 | if (virtioCoreR3VirtqIsAttached(&pThis->Virtio, uVirtqNbr))
|
---|
[92939] | 1197 | {
|
---|
[93165] | 1198 | Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
|
---|
| 1199 | rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[pVirtq->uIdx].hEvtProcess);
|
---|
| 1200 | AssertRCReturn(rc, rc);
|
---|
[92939] | 1201 | }
|
---|
| 1202 | }
|
---|
| 1203 | }
|
---|
[93165] | 1204 | }
|
---|
| 1205 | AssertMsg(cVirtqsToRemove < 2, ("Multiple unusable queues in saved state unexpected\n"));
|
---|
| 1206 | pThis->cVirtqs -= cVirtqsToRemove;
|
---|
[92939] | 1207 |
|
---|
[93165] | 1208 | pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus;
|
---|
| 1209 | pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
|
---|
[92939] | 1210 |
|
---|
[93165] | 1211 | rc = pHlp->pfnSSMGetMem(pSSM, pThis->virtioNetConfig.uMacAddress.au8, sizeof(pThis->virtioNetConfig.uMacAddress));
|
---|
| 1212 | AssertRCReturn(rc, rc);
|
---|
| 1213 |
|
---|
| 1214 | if (uVersion > VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY)
|
---|
| 1215 | {
|
---|
[94976] | 1216 | /* Zero-out the the Unicast/Multicast filter table */
|
---|
| 1217 | memset(&pThis->aMacUnicastFilter[0], 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
|
---|
| 1218 |
|
---|
[93165] | 1219 | rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
|
---|
[92939] | 1220 | AssertRCReturn(rc, rc);
|
---|
[93165] | 1221 | rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
|
---|
| 1222 | AssertRCReturn(rc, rc);
|
---|
| 1223 | /*
|
---|
| 1224 | * The 0.95 legacy virtio spec defines a control queue command VIRTIO_NET_CTRL_MAC_TABLE_SET,
|
---|
| 1225 | * wherein guest driver configures two variable length mac filter tables: A unicast filter,
|
---|
| 1226 | * and a multicast filter. However original VBox virtio-net saved both sets of filter entries
|
---|
| 1227 | * in a single table, abandoning the distinction between unicast and multicast filters. It preserved
|
---|
| 1228 | * only *one* filter's table length, leaving no way to separate table back out into respective unicast
|
---|
| 1229 | * and multicast tables this device implementation preserves. Deduced from legacy code, the original
|
---|
| 1230 | * assumption was that the both MAC filters are whitelists that can be processed identically
|
---|
| 1231 | * (from the standpoint of a *single* host receiver), such that the distinction between unicast and
|
---|
| 1232 | * multicast doesn't matter in any one VM's context. Little choice here but to save the undifferentiated
|
---|
| 1233 | * unicast & multicast MACs to the unicast filter table and leave multicast table empty/unused.
|
---|
| 1234 | */
|
---|
| 1235 | uint32_t cCombinedUnicastMulticastEntries;
|
---|
| 1236 | rc = pHlp->pfnSSMGetU32(pSSM, &cCombinedUnicastMulticastEntries);
|
---|
| 1237 | AssertRCReturn(rc, rc);
|
---|
| 1238 | AssertReturn(cCombinedUnicastMulticastEntries <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
|
---|
| 1239 | pThis->cUnicastFilterMacs = cCombinedUnicastMulticastEntries;
|
---|
| 1240 | rc = pHlp->pfnSSMGetMem(pSSM, pThis->aMacUnicastFilter, cCombinedUnicastMulticastEntries * sizeof(RTMAC));
|
---|
| 1241 | AssertRCReturn(rc, rc);
|
---|
| 1242 | rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
|
---|
| 1243 | AssertRCReturn(rc, rc);
|
---|
[92939] | 1244 | }
|
---|
| 1245 | else
|
---|
| 1246 | {
|
---|
| 1247 | pThis->fAllMulticast = false;
|
---|
| 1248 | pThis->cUnicastFilterMacs = 0;
|
---|
| 1249 | memset(&pThis->aMacUnicastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
|
---|
| 1250 |
|
---|
| 1251 | memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
|
---|
| 1252 |
|
---|
| 1253 | pThis->fPromiscuous = true;
|
---|
| 1254 | if (pThisCC->pDrv)
|
---|
| 1255 | pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
|
---|
| 1256 | }
|
---|
| 1257 |
|
---|
| 1258 | /*
|
---|
| 1259 | * Log the restored VirtIO feature selection.
|
---|
| 1260 | */
|
---|
| 1261 | pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
|
---|
[95611] | 1262 | /** @todo shouldn't we update the virtio header size here? it depends on the negotiated features. */
|
---|
[92939] | 1263 | virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
|
---|
| 1264 |
|
---|
| 1265 | /*
|
---|
| 1266 | * Configure remaining transitional device parameters presumably or deductively
|
---|
| 1267 | * as these weren't part of the legacy device code thus it didn't save them to SSM
|
---|
| 1268 | */
|
---|
| 1269 | pThis->fCableConnected = 1;
|
---|
| 1270 | pThis->fAllUnicast = 0;
|
---|
| 1271 | pThis->fNoMulticast = 0;
|
---|
| 1272 | pThis->fNoUnicast = 0;
|
---|
| 1273 | pThis->fNoBroadcast = 0;
|
---|
| 1274 |
|
---|
| 1275 | /* Zero out the multicast table and count, all MAC filters, if any, are in the unicast filter table */
|
---|
| 1276 | pThis->cMulticastFilterMacs = 0;
|
---|
| 1277 | memset(&pThis->aMacMulticastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
|
---|
| 1278 | }
|
---|
| 1279 | return VINF_SUCCESS;
|
---|
| 1280 | }
|
---|
| 1281 |
|
---|
| 1282 | /**
|
---|
| 1283 | * @callback_method_impl{FNSSMDEVLOADEXEC}
|
---|
| 1284 | *
|
---|
| 1285 | * @note: This loads state saved by a Modern (VirtIO 1.0+) device, of which this transitional device is one,
|
---|
| 1286 | * and thus supports both legacy and modern guest virtio drivers.
|
---|
| 1287 | */
|
---|
[97824] | 1288 | static DECLCALLBACK(int) virtioNetR3ModernLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
|
---|
[92939] | 1289 | {
|
---|
| 1290 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 1291 | PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
|
---|
| 1292 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
| 1293 | int rc;
|
---|
| 1294 |
|
---|
[82681] | 1295 | RT_NOREF(pThisCC);
|
---|
[92939] | 1296 |
|
---|
[93002] | 1297 | RTMAC uMacLoaded, uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
|
---|
[92939] | 1298 | rc = pHlp->pfnSSMGetMem(pSSM, &uMacLoaded.au8, sizeof(uMacLoaded.au8));
|
---|
| 1299 | AssertRCReturn(rc, rc);
|
---|
| 1300 | if (memcmp(&uMacLoaded.au8, uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8)))
|
---|
| 1301 | {
|
---|
| 1302 | rc = virtioNetR3LegacyDeviceLoadExec(pDevIns, pSSM, uVersion, uPass, uMacLoaded);
|
---|
| 1303 | return rc;
|
---|
| 1304 | }
|
---|
| 1305 |
|
---|
[91703] | 1306 | Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
|
---|
[82681] | 1307 |
|
---|
| 1308 | AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
|
---|
[92939] | 1309 | AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVEDSTATE_VERSION,
|
---|
[82681] | 1310 | ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
|
---|
| 1311 |
|
---|
[92939] | 1312 | virtioNetR3SetVirtqNames(pThis, false /* fLegacy */);
|
---|
[82681] | 1313 |
|
---|
[83913] | 1314 | pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
|
---|
| 1315 |
|
---|
[91703] | 1316 | pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtqs);
|
---|
[92939] | 1317 | AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2) + 1, VERR_OUT_OF_RANGE);
|
---|
[83913] | 1318 | pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
|
---|
[92939] | 1319 | AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS , VERR_OUT_OF_RANGE);
|
---|
[83913] | 1320 |
|
---|
[91703] | 1321 | for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
|
---|
[92939] | 1322 | pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
|
---|
[82961] | 1323 |
|
---|
[92939] | 1324 | /* Config checks */
|
---|
[83913] | 1325 | RTMAC macConfigured;
|
---|
| 1326 | rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
|
---|
| 1327 | AssertRCReturn(rc, rc);
|
---|
| 1328 | if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
|
---|
| 1329 | && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
|
---|
[92939] | 1330 | LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
|
---|
| 1331 | pThis->szInst, &pThis->macConfigured, &macConfigured));
|
---|
| 1332 | memcpy(pThis->virtioNetConfig.uMacAddress.au8, macConfigured.au8, sizeof(macConfigured.au8));
|
---|
| 1333 |
|
---|
| 1334 | #if FEATURE_OFFERED(STATUS)
|
---|
| 1335 | uint16_t fChkStatus;
|
---|
| 1336 | pHlp->pfnSSMGetU16( pSSM, &fChkStatus);
|
---|
| 1337 | if (fChkStatus == 0xffff)
|
---|
| 1338 | {
|
---|
| 1339 | /* Dummy value in saved state because status feature wasn't enabled at the time */
|
---|
| 1340 | pThis->virtioNetConfig.uStatus = 0; /* VIRTIO_NET_S_ANNOUNCE disabled */
|
---|
| 1341 | pThis->virtioNetConfig.uStatus = !!IS_LINK_UP(pThis); /* VIRTIO_NET_IS_LINK_UP (bit 0) */
|
---|
| 1342 | }
|
---|
| 1343 | else
|
---|
| 1344 | pThis->virtioNetConfig.uStatus = fChkStatus;
|
---|
| 1345 | #else
|
---|
| 1346 | uint16_t fDiscard;
|
---|
| 1347 | pHlp->pfnSSMGetU16( pSSM, &fDiscard);
|
---|
[83913] | 1348 | #endif
|
---|
[84383] | 1349 |
|
---|
[83913] | 1350 | #if FEATURE_OFFERED(MQ)
|
---|
[92939] | 1351 | uint16_t uCheckMaxVirtqPairs;
|
---|
| 1352 | pHlp->pfnSSMGetU16( pSSM, &uCheckMaxVirtqPairs);
|
---|
| 1353 | if (uCheckMaxVirtqPairs)
|
---|
| 1354 | pThis->virtioNetConfig.uMaxVirtqPairs = uCheckMaxVirtqPairs;
|
---|
| 1355 | else
|
---|
| 1356 | pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_CTRL_MQ_VQ_PAIRS;
|
---|
| 1357 | #else
|
---|
| 1358 | uint16_t fDiscard;
|
---|
| 1359 | pHlp->pfnSSMGetU16( pSSM, &fDiscard);
|
---|
[83913] | 1360 | #endif
|
---|
| 1361 |
|
---|
[92939] | 1362 | /* Save device-specific part */
|
---|
| 1363 | pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
|
---|
| 1364 | pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
|
---|
| 1365 | pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
|
---|
| 1366 | pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
|
---|
| 1367 | pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
|
---|
| 1368 | pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
|
---|
| 1369 | pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
|
---|
[83913] | 1370 |
|
---|
[92939] | 1371 | pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
|
---|
| 1372 | AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
|
---|
| 1373 | pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
|
---|
[83913] | 1374 |
|
---|
[92939] | 1375 | if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
|
---|
| 1376 | memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
|
---|
| 1377 | (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
|
---|
[83913] | 1378 |
|
---|
[92939] | 1379 | pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
|
---|
| 1380 | AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
|
---|
| 1381 | pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
|
---|
[83913] | 1382 |
|
---|
[92939] | 1383 | if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
|
---|
| 1384 | memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
|
---|
| 1385 | (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
|
---|
[83913] | 1386 |
|
---|
[92939] | 1387 | rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
|
---|
| 1388 | AssertRCReturn(rc, rc);
|
---|
[82681] | 1389 | /*
|
---|
| 1390 | * Call the virtio core to let it load its state.
|
---|
| 1391 | */
|
---|
[92939] | 1392 | rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion,
|
---|
| 1393 | VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
|
---|
| 1394 | AssertRCReturn(rc, rc);
|
---|
[82681] | 1395 | /*
|
---|
[92939] | 1396 | * Since the control queue is created proactively in the constructor to accomodate worst-case
|
---|
| 1397 | * legacy guests, even though the queue may have been deducted from queue count while saving state,
|
---|
| 1398 | * we must explicitly remove queue and associated worker thread and context at this point,
|
---|
| 1399 | * or presence of bogus control queue will confuse operations.
|
---|
| 1400 | */
|
---|
| 1401 | PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[CTRLQIDX];
|
---|
| 1402 | if (FEATURE_DISABLED(CTRL_VQ) || !virtioCoreIsVirtqEnabled(&pThis->Virtio, CTRLQIDX))
|
---|
| 1403 | {
|
---|
| 1404 | virtioCoreR3VirtqDetach(&pThis->Virtio, CTRLQIDX);
|
---|
| 1405 | virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
|
---|
| 1406 | pVirtq->fAttachedToVirtioCore = false;
|
---|
| 1407 | --pThis->cWorkers;
|
---|
| 1408 | }
|
---|
| 1409 | /*
|
---|
[82681] | 1410 | * Nudge queue workers
|
---|
| 1411 | */
|
---|
[92939] | 1412 | for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
|
---|
[82681] | 1413 | {
|
---|
[92939] | 1414 | pVirtq = &pThis->aVirtqs[uVirtqNbr];
|
---|
[84803] | 1415 | if (pVirtq->fAttachedToVirtioCore)
|
---|
[82681] | 1416 | {
|
---|
[92939] | 1417 | if (pVirtq->fHasWorker)
|
---|
| 1418 | {
|
---|
| 1419 | PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
|
---|
| 1420 | Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
|
---|
| 1421 | rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
|
---|
| 1422 | AssertRCReturn(rc, rc);
|
---|
| 1423 | }
|
---|
[82681] | 1424 | }
|
---|
| 1425 | }
|
---|
[92939] | 1426 | pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus; /* reflects state to guest driver */
|
---|
| 1427 | pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
|
---|
[97824] | 1428 | virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
|
---|
[82681] | 1429 | return rc;
|
---|
| 1430 | }
|
---|
| 1431 |
|
---|
| 1432 | /**
|
---|
[97824] | 1433 | * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
|
---|
| 1434 | * loading.}
|
---|
| 1435 | */
|
---|
| 1436 | static DECLCALLBACK(int) virtioNetR3ModernLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
|
---|
| 1437 | {
|
---|
| 1438 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 1439 | PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
|
---|
| 1440 | RT_NOREF(pSSM);
|
---|
| 1441 |
|
---|
| 1442 | if (pThisCC->pDrv)
|
---|
| 1443 | pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulticast));
|
---|
| 1444 |
|
---|
| 1445 | /*
|
---|
| 1446 | * Indicate link down to the guest OS that all network connections have
|
---|
| 1447 | * been lost, unless we've been teleported here.
|
---|
| 1448 | */
|
---|
| 1449 | if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
|
---|
| 1450 | virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
|
---|
| 1451 |
|
---|
| 1452 | return VINF_SUCCESS;
|
---|
| 1453 | }
|
---|
| 1454 |
|
---|
| 1455 | /**
|
---|
[82681] | 1456 | * @callback_method_impl{FNSSMDEVSAVEEXEC}
|
---|
| 1457 | */
|
---|
[92939] | 1458 | static DECLCALLBACK(int) virtioNetR3ModernSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
|
---|
[82681] | 1459 | {
|
---|
| 1460 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 1461 | PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
|
---|
[82961] | 1462 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
[82681] | 1463 |
|
---|
| 1464 | RT_NOREF(pThisCC);
|
---|
[91703] | 1465 | Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
|
---|
[82681] | 1466 |
|
---|
[92939] | 1467 | /* Store a dummy MAC address that would never be actually assigned to a NIC
|
---|
| 1468 | * so that when load exec handler is called it can be easily determined
|
---|
| 1469 | * whether saved state is modern or legacy. This works because original
|
---|
| 1470 | * legacy code stored assigned NIC address as the first item of SSM state
|
---|
| 1471 | */
|
---|
[93002] | 1472 | RTMAC uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
|
---|
[92939] | 1473 | pHlp->pfnSSMPutMem(pSSM, &uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8));
|
---|
| 1474 |
|
---|
[83913] | 1475 | pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
|
---|
| 1476 |
|
---|
[91703] | 1477 | pHlp->pfnSSMPutU16( pSSM, pThis->cVirtqs);
|
---|
[83913] | 1478 | pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
|
---|
| 1479 |
|
---|
[91703] | 1480 | for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
|
---|
[84819] | 1481 | pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
|
---|
[92939] | 1482 | /*
|
---|
[82681] | 1483 |
|
---|
[92939] | 1484 | * Save device config area (accessed via MMIO)
|
---|
| 1485 | */
|
---|
[83913] | 1486 | pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
|
---|
| 1487 | sizeof(pThis->virtioNetConfig.uMacAddress.au8));
|
---|
[92939] | 1488 | #if FEATURE_OFFERED(STATUS)
|
---|
| 1489 | pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uStatus);
|
---|
| 1490 | #else
|
---|
| 1491 | /*
|
---|
| 1492 | * Relevant values are lower bits. Forcing this to 0xffff let's loadExec know this
|
---|
| 1493 | * feature was not enabled in saved state. VirtIO 1.0, 5.1.4
|
---|
| 1494 | */
|
---|
| 1495 | pHlp->pfnSSMPutU16( pSSM, 0xffff);
|
---|
| 1496 |
|
---|
[83913] | 1497 | #endif
|
---|
| 1498 | #if FEATURE_OFFERED(MQ)
|
---|
| 1499 | pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
|
---|
[92939] | 1500 | #else
|
---|
| 1501 | /*
|
---|
| 1502 | * Legal values for max_virtqueue_pairs are 0x1 -> 0x8000 *. Forcing zero let's loadExec know this
|
---|
| 1503 | * feature was not enabled in saved state. VirtIO 1.0, 5.1.4.1
|
---|
| 1504 | */
|
---|
| 1505 | pHlp->pfnSSMPutU16( pSSM, 0);
|
---|
[83913] | 1506 | #endif
|
---|
| 1507 |
|
---|
| 1508 | /* Save device-specific part */
|
---|
| 1509 | pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
|
---|
| 1510 | pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
|
---|
| 1511 | pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
|
---|
| 1512 | pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
|
---|
| 1513 | pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
|
---|
| 1514 | pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
|
---|
| 1515 | pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
|
---|
| 1516 |
|
---|
| 1517 | pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
|
---|
| 1518 | pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
|
---|
| 1519 |
|
---|
| 1520 | pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
|
---|
| 1521 | pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
|
---|
| 1522 |
|
---|
| 1523 | int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
|
---|
| 1524 | AssertRCReturn(rc, rc);
|
---|
| 1525 |
|
---|
[82681] | 1526 | /*
|
---|
| 1527 | * Call the virtio core to let it save its state.
|
---|
| 1528 | */
|
---|
[92939] | 1529 | return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
|
---|
[82681] | 1530 | }
|
---|
| 1531 |
|
---|
| 1532 |
|
---|
| 1533 | /*********************************************************************************************************************************
|
---|
| 1534 | * Device interface. *
|
---|
| 1535 | *********************************************************************************************************************************/
|
---|
| 1536 |
|
---|
[82863] | 1537 | #ifdef IN_RING3
|
---|
| 1538 |
|
---|
[92939] | 1539 | /**
|
---|
| 1540 | * Perform 16-bit 1's compliment checksum on provided packet in accordance with VirtIO specification,
|
---|
| 1541 | * pertinent to VIRTIO_NET_F_CSUM feature, which 'offloads' the Checksum feature from the driver
|
---|
| 1542 | * to save processor cycles, which is ironic in our case, where the controller device ('network card')
|
---|
| 1543 | * is emulated on the virtualization host.
|
---|
| 1544 | *
|
---|
| 1545 | * @note See VirtIO 1.0 spec, 5.1.6.2 Packet Transmission
|
---|
| 1546 | *
|
---|
| 1547 | * @param pBuf Pointer to r/w buffer with any portion to calculate checksum for
|
---|
| 1548 | * @param cbSize Number of bytes to checksum
|
---|
| 1549 | * @param uStart Where to start the checksum within the buffer
|
---|
| 1550 | * @param uOffset Offset past uStart point in the buffer to store checksum result
|
---|
| 1551 | *
|
---|
| 1552 | */
|
---|
| 1553 | DECLINLINE(void) virtioNetR3Calc16BitChecksum(uint8_t *pBuf, size_t cb, uint16_t uStart, uint16_t uOffset)
|
---|
[82826] | 1554 | {
|
---|
[92939] | 1555 | AssertReturnVoid(uStart < cb);
|
---|
| 1556 | AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cb);
|
---|
| 1557 |
|
---|
[82863] | 1558 | uint32_t chksum = 0;
|
---|
[92939] | 1559 | uint16_t *pu = (uint16_t *)(pBuf + uStart);
|
---|
[82863] | 1560 |
|
---|
[92939] | 1561 | cb -= uStart;
|
---|
[82863] | 1562 | while (cb > 1)
|
---|
| 1563 | {
|
---|
| 1564 | chksum += *pu++;
|
---|
| 1565 | cb -= 2;
|
---|
| 1566 | }
|
---|
| 1567 | if (cb)
|
---|
[92939] | 1568 | chksum += *(uint8_t *)pu;
|
---|
[82863] | 1569 | while (chksum >> 16)
|
---|
| 1570 | chksum = (chksum >> 16) + (chksum & 0xFFFF);
|
---|
[82775] | 1571 |
|
---|
[92939] | 1572 | /* Store 1's compliment of calculated sum */
|
---|
| 1573 | *(uint16_t *)(pBuf + uStart + uOffset) = ~chksum;
|
---|
[82826] | 1574 | }
|
---|
| 1575 |
|
---|
| 1576 | /**
|
---|
[82863] | 1577 | * Turns on/off the read status LED.
|
---|
| 1578 | *
|
---|
| 1579 | * @returns VBox status code.
|
---|
| 1580 | * @param pThis Pointer to the device state structure.
|
---|
| 1581 | * @param fOn New LED state.
|
---|
[82826] | 1582 | */
|
---|
[99775] | 1583 | static void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
|
---|
[82826] | 1584 | {
|
---|
[82863] | 1585 | if (fOn)
|
---|
| 1586 | pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
|
---|
| 1587 | else
|
---|
| 1588 | pThisR3->led.Actual.s.fReading = fOn;
|
---|
[82826] | 1589 | }
|
---|
| 1590 |
|
---|
| 1591 | /**
|
---|
[82863] | 1592 | * Turns on/off the write status LED.
|
---|
| 1593 | *
|
---|
| 1594 | * @returns VBox status code.
|
---|
| 1595 | * @param pThis Pointer to the device state structure.
|
---|
| 1596 | * @param fOn New LED state.
|
---|
[82826] | 1597 | */
|
---|
[99775] | 1598 | static void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
|
---|
[82826] | 1599 | {
|
---|
[82863] | 1600 | if (fOn)
|
---|
| 1601 | pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
|
---|
| 1602 | else
|
---|
| 1603 | pThisR3->led.Actual.s.fWriting = fOn;
|
---|
| 1604 | }
|
---|
[82826] | 1605 |
|
---|
[92939] | 1606 | /**
|
---|
| 1607 | * Check that the core is setup and ready and co-configured with guest virtio driver,
|
---|
| 1608 | * and verifies that the VM is running.
|
---|
| 1609 | *
|
---|
| 1610 | * @returns true if VirtIO core and device are in a running and operational state
|
---|
[84774] | 1611 | */
|
---|
| 1612 | DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
|
---|
| 1613 | {
|
---|
[85415] | 1614 | if (RT_LIKELY(pThis->fVirtioReady))
|
---|
[84774] | 1615 | {
|
---|
| 1616 | VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
|
---|
| 1617 | if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
|
---|
| 1618 | return true;
|
---|
| 1619 | }
|
---|
| 1620 | return false;
|
---|
| 1621 | }
|
---|
| 1622 |
|
---|
[82826] | 1623 | /**
|
---|
[82961] | 1624 | * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
|
---|
| 1625 | * available. This must be called before the pfnRecieve() method is called.
|
---|
[82826] | 1626 | *
|
---|
| 1627 | * @remarks As a side effect this function enables queue notification
|
---|
| 1628 | * if it cannot receive because the queue is empty.
|
---|
| 1629 | * It disables notification if it can receive.
|
---|
| 1630 | *
|
---|
| 1631 | * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
|
---|
| 1632 | * @thread RX
|
---|
| 1633 | */
|
---|
[84803] | 1634 | static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
|
---|
[82826] | 1635 | {
|
---|
[84774] | 1636 | int rc = VERR_INVALID_STATE;
|
---|
[92939] | 1637 | Log8Func(("[%s] ", pThis->szInst));
|
---|
[84774] | 1638 | if (!virtioNetIsOperational(pThis, pDevIns))
|
---|
[93001] | 1639 | Log8(("No Rx bufs available. (VirtIO core not ready)\n"));
|
---|
[84774] | 1640 |
|
---|
[93001] | 1641 | else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
|
---|
[93004] | 1642 | Log8(("[No Rx bufs available. (%s not enabled)\n", pRxVirtq->szName));
|
---|
[84774] | 1643 |
|
---|
[92939] | 1644 | else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->uIdx))
|
---|
[93004] | 1645 | Log8(("No Rx bufs available. (%s empty)\n", pRxVirtq->szName));
|
---|
[84774] | 1646 |
|
---|
[82826] | 1647 | else
|
---|
| 1648 | {
|
---|
[92939] | 1649 | Log8(("%s has %d empty guest bufs in avail ring\n", pRxVirtq->szName,
|
---|
| 1650 | virtioCoreVirtqAvailBufCount(pDevIns, &pThis->Virtio, pRxVirtq->uIdx)));
|
---|
[84774] | 1651 | rc = VINF_SUCCESS;
|
---|
[82826] | 1652 | }
|
---|
[85415] | 1653 | virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
|
---|
[84774] | 1654 | return rc;
|
---|
[83058] | 1655 | }
|
---|
[82826] | 1656 |
|
---|
[92939] | 1657 | /**
|
---|
| 1658 | * Find an Rx queue that has Rx packets in it, if *any* do.
|
---|
| 1659 | *
|
---|
| 1660 | * @todo When multiqueue (MQ) mode is fully supported and tested, some kind of round-robin
|
---|
| 1661 | * or randomization scheme should probably be incorporated here.
|
---|
| 1662 | *
|
---|
| 1663 | * @returns true if Rx pkts avail on queue and sets pRxVirtq to point to queue w/pkts found
|
---|
| 1664 | * @thread RX
|
---|
| 1665 | *
|
---|
| 1666 | */
|
---|
| 1667 | static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
|
---|
[83058] | 1668 | {
|
---|
[84876] | 1669 | for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
|
---|
[84774] | 1670 | {
|
---|
[84876] | 1671 | PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
|
---|
[84803] | 1672 | if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
|
---|
[84774] | 1673 | {
|
---|
[84803] | 1674 | if (pRxVirtq)
|
---|
| 1675 | *pRxVirtq = pThisRxVirtq;
|
---|
[83058] | 1676 | return true;
|
---|
[84774] | 1677 | }
|
---|
| 1678 | }
|
---|
[83058] | 1679 | return false;
|
---|
[82826] | 1680 | }
|
---|
[82961] | 1681 |
|
---|
[82826] | 1682 | /**
|
---|
[82863] | 1683 | * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
|
---|
[82826] | 1684 | */
|
---|
[82863] | 1685 | static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
|
---|
[82826] | 1686 | {
|
---|
[82863] | 1687 | PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
|
---|
| 1688 | PPDMDEVINS pDevIns = pThisCC->pDevIns;
|
---|
[82826] | 1689 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 1690 |
|
---|
[85415] | 1691 | if (!virtioNetIsOperational(pThis, pDevIns))
|
---|
| 1692 | return VERR_INTERRUPTED;
|
---|
| 1693 |
|
---|
[92939] | 1694 | if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
|
---|
[82961] | 1695 | {
|
---|
[92939] | 1696 | Log10Func(("[%s] Rx bufs available, releasing waiter...\n", pThis->szInst));
|
---|
[84774] | 1697 | return VINF_SUCCESS;
|
---|
[82961] | 1698 | }
|
---|
[82863] | 1699 | if (!timeoutMs)
|
---|
| 1700 | return VERR_NET_NO_BUFFER_SPACE;
|
---|
[82826] | 1701 |
|
---|
[91703] | 1702 | LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
|
---|
[82961] | 1703 |
|
---|
[84774] | 1704 | ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
|
---|
[83913] | 1705 | STAM_PROFILE_START(&pThis->StatRxOverflow, a);
|
---|
[82826] | 1706 |
|
---|
[82961] | 1707 | do {
|
---|
[92939] | 1708 | if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
|
---|
[82961] | 1709 | {
|
---|
[91703] | 1710 | Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
|
---|
[85415] | 1711 | ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
|
---|
[84774] | 1712 | return VINF_SUCCESS;
|
---|
[82863] | 1713 | }
|
---|
[91703] | 1714 | Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
|
---|
[82826] | 1715 |
|
---|
[83058] | 1716 | int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
|
---|
[82961] | 1717 |
|
---|
| 1718 | if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
|
---|
[84774] | 1719 | {
|
---|
[85291] | 1720 | LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
|
---|
[85415] | 1721 |
|
---|
| 1722 | if (!virtioNetIsOperational(pThis, pDevIns))
|
---|
| 1723 | break;
|
---|
| 1724 |
|
---|
[82961] | 1725 | continue;
|
---|
[84774] | 1726 | }
|
---|
| 1727 | if (RT_FAILURE(rc)) {
|
---|
| 1728 | LogFunc(("Waken due to failure %Rrc\n", rc));
|
---|
[82863] | 1729 | RTThreadSleep(1);
|
---|
[84774] | 1730 | }
|
---|
[83913] | 1731 | } while (virtioNetIsOperational(pThis, pDevIns));
|
---|
[82961] | 1732 |
|
---|
[83913] | 1733 | STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
|
---|
[84774] | 1734 | ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
|
---|
[82826] | 1735 |
|
---|
[91703] | 1736 | Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
|
---|
[82863] | 1737 | return VERR_INTERRUPTED;
|
---|
[82826] | 1738 | }
|
---|
| 1739 |
|
---|
| 1740 | /**
|
---|
| 1741 | * Sets up the GSO context according to the Virtio header.
|
---|
| 1742 | *
|
---|
| 1743 | * @param pGso The GSO context to setup.
|
---|
| 1744 | * @param pCtx The context descriptor.
|
---|
| 1745 | */
|
---|
[84819] | 1746 | DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
|
---|
[82826] | 1747 | {
|
---|
| 1748 | pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
|
---|
| 1749 |
|
---|
[82863] | 1750 | if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
|
---|
[82826] | 1751 | {
|
---|
| 1752 | AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
|
---|
| 1753 | return NULL;
|
---|
| 1754 | }
|
---|
[82863] | 1755 | switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
|
---|
[82826] | 1756 | {
|
---|
[82863] | 1757 | case VIRTIONET_HDR_GSO_TCPV4:
|
---|
[82826] | 1758 | pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
|
---|
[82863] | 1759 | pGso->cbHdrsSeg = pPktHdr->uHdrLen;
|
---|
[82826] | 1760 | break;
|
---|
[82863] | 1761 | case VIRTIONET_HDR_GSO_TCPV6:
|
---|
[82826] | 1762 | pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
|
---|
[82863] | 1763 | pGso->cbHdrsSeg = pPktHdr->uHdrLen;
|
---|
[82826] | 1764 | break;
|
---|
[82863] | 1765 | case VIRTIONET_HDR_GSO_UDP:
|
---|
[82826] | 1766 | pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
|
---|
[82863] | 1767 | pGso->cbHdrsSeg = pPktHdr->uChksumStart;
|
---|
[82826] | 1768 | break;
|
---|
| 1769 | default:
|
---|
| 1770 | return NULL;
|
---|
| 1771 | }
|
---|
[82863] | 1772 | if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
|
---|
| 1773 | pGso->offHdr2 = pPktHdr->uChksumStart;
|
---|
[82826] | 1774 | else
|
---|
| 1775 | {
|
---|
| 1776 | AssertMsgFailed(("GSO without checksum offloading!\n"));
|
---|
| 1777 | return NULL;
|
---|
| 1778 | }
|
---|
| 1779 | pGso->offHdr1 = sizeof(RTNETETHERHDR);
|
---|
[82863] | 1780 | pGso->cbHdrsTotal = pPktHdr->uHdrLen;
|
---|
| 1781 | pGso->cbMaxSeg = pPktHdr->uGsoSize;
|
---|
[95592] | 1782 | /* Mark GSO frames with zero MSS as PDMNETWORKGSOTYPE_INVALID, so they will be ignored by send. */
|
---|
| 1783 | if (pPktHdr->uGsoType != VIRTIONET_HDR_GSO_NONE && pPktHdr->uGsoSize == 0)
|
---|
| 1784 | pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
|
---|
[82826] | 1785 | return pGso;
|
---|
| 1786 | }
|
---|
| 1787 |
|
---|
| 1788 | /**
|
---|
| 1789 | * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
|
---|
| 1790 | */
|
---|
| 1791 | static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
|
---|
| 1792 | {
|
---|
[82863] | 1793 | PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
|
---|
| 1794 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
|
---|
[82826] | 1795 | memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
|
---|
| 1796 | return VINF_SUCCESS;
|
---|
| 1797 | }
|
---|
| 1798 |
|
---|
| 1799 | /**
|
---|
| 1800 | * Returns true if it is a broadcast packet.
|
---|
| 1801 | *
|
---|
| 1802 | * @returns true if destination address indicates broadcast.
|
---|
| 1803 | * @param pvBuf The ethernet packet.
|
---|
| 1804 | */
|
---|
| 1805 | DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
|
---|
| 1806 | {
|
---|
| 1807 | static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
---|
| 1808 | return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
|
---|
| 1809 | }
|
---|
| 1810 |
|
---|
| 1811 | /**
|
---|
| 1812 | * Returns true if it is a multicast packet.
|
---|
| 1813 | *
|
---|
| 1814 | * @remarks returns true for broadcast packets as well.
|
---|
| 1815 | * @returns true if destination address indicates multicast.
|
---|
| 1816 | * @param pvBuf The ethernet packet.
|
---|
| 1817 | */
|
---|
| 1818 | DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
|
---|
| 1819 | {
|
---|
| 1820 | return (*(char*)pvBuf) & 1;
|
---|
| 1821 | }
|
---|
[92939] | 1822 |
|
---|
[82826] | 1823 | /**
|
---|
[82775] | 1824 | * Determines if the packet is to be delivered to upper layer.
|
---|
| 1825 | *
|
---|
| 1826 | * @returns true if packet is intended for this node.
|
---|
| 1827 | * @param pThis Pointer to the state structure.
|
---|
[82826] | 1828 | * @param pvBuf The ethernet packet.
|
---|
| 1829 | * @param cb Number of bytes available in the packet.
|
---|
[82775] | 1830 | */
|
---|
[82826] | 1831 | static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
|
---|
[82681] | 1832 | {
|
---|
[82961] | 1833 |
|
---|
[93000] | 1834 | RT_NOREF(cb);
|
---|
| 1835 |
|
---|
[92939] | 1836 | #ifdef LOG_ENABLED
|
---|
[83153] | 1837 | if (LogIs11Enabled())
|
---|
| 1838 | {
|
---|
| 1839 | char *pszType;
|
---|
| 1840 | if (virtioNetR3IsMulticast(pvBuf))
|
---|
[91703] | 1841 | pszType = (char *)"mcast";
|
---|
[83153] | 1842 | else if (virtioNetR3IsBroadcast(pvBuf))
|
---|
[91703] | 1843 | pszType = (char *)"bcast";
|
---|
[83153] | 1844 | else
|
---|
[91703] | 1845 | pszType = (char *)"ucast";
|
---|
[83153] | 1846 |
|
---|
[91703] | 1847 | LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
|
---|
| 1848 | pThis->virtioNetConfig.uMacAddress.au8,
|
---|
| 1849 | pThis->fPromiscuous ? " promisc" : "",
|
---|
| 1850 | pThis->fAllMulticast ? " all-mcast" : "",
|
---|
[83153] | 1851 | pvBuf, pszType));
|
---|
| 1852 | }
|
---|
[92939] | 1853 | #endif
|
---|
[83153] | 1854 |
|
---|
[91703] | 1855 | if (pThis->fPromiscuous) {
|
---|
| 1856 | Log11(("\n"));
|
---|
[82775] | 1857 | return true;
|
---|
[91703] | 1858 | }
|
---|
[82775] | 1859 |
|
---|
| 1860 | /* Ignore everything outside of our VLANs */
|
---|
[82826] | 1861 | uint16_t *uPtr = (uint16_t *)pvBuf;
|
---|
| 1862 |
|
---|
[82775] | 1863 | /* Compare TPID with VLAN Ether Type */
|
---|
[82863] | 1864 | if ( uPtr[6] == RT_H2BE_U16(0x8100)
|
---|
| 1865 | && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
|
---|
[82775] | 1866 | {
|
---|
[91703] | 1867 | Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
|
---|
[82775] | 1868 | return false;
|
---|
| 1869 | }
|
---|
[83153] | 1870 |
|
---|
[82826] | 1871 | if (virtioNetR3IsBroadcast(pvBuf))
|
---|
[83153] | 1872 | {
|
---|
[91703] | 1873 | Log11(("acpt (bcast)\n"));
|
---|
[92939] | 1874 | #ifdef LOG_ENABLED
|
---|
[83153] | 1875 | if (LogIs12Enabled())
|
---|
| 1876 | virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
|
---|
[92939] | 1877 | #endif
|
---|
[82775] | 1878 | return true;
|
---|
[83153] | 1879 | }
|
---|
[82826] | 1880 | if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
|
---|
[83153] | 1881 | {
|
---|
[91703] | 1882 | Log11(("acpt (all-mcast)\n"));
|
---|
[92939] | 1883 | #ifdef LOG_ENABLED
|
---|
[83153] | 1884 | if (LogIs12Enabled())
|
---|
| 1885 | virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
|
---|
[92939] | 1886 | #endif
|
---|
[82775] | 1887 | return true;
|
---|
[83153] | 1888 | }
|
---|
[82775] | 1889 |
|
---|
[82826] | 1890 | if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
|
---|
[83153] | 1891 | {
|
---|
[91703] | 1892 | Log11(("acpt (to-node)\n"));
|
---|
[92939] | 1893 | #ifdef LOG_ENABLED
|
---|
[83153] | 1894 | if (LogIs12Enabled())
|
---|
| 1895 | virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
|
---|
[92939] | 1896 | #endif
|
---|
[82775] | 1897 | return true;
|
---|
[83153] | 1898 | }
|
---|
[82775] | 1899 |
|
---|
[82826] | 1900 | for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
|
---|
[83153] | 1901 | {
|
---|
[82826] | 1902 | if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
|
---|
[83153] | 1903 | {
|
---|
[91703] | 1904 | Log11(("acpt (mcast whitelist)\n"));
|
---|
[92939] | 1905 | #ifdef LOG_ENABLED
|
---|
[83153] | 1906 | if (LogIs12Enabled())
|
---|
| 1907 | virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
|
---|
[92939] | 1908 | #endif
|
---|
[82775] | 1909 | return true;
|
---|
[83153] | 1910 | }
|
---|
| 1911 | }
|
---|
[82775] | 1912 |
|
---|
[82826] | 1913 | for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
|
---|
| 1914 | if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
|
---|
[83153] | 1915 | {
|
---|
[91703] | 1916 | Log11(("acpt (ucast whitelist)\n"));
|
---|
[82826] | 1917 | return true;
|
---|
[83153] | 1918 | }
|
---|
[92939] | 1919 | #ifdef LOG_ENABLED
|
---|
[91703] | 1920 | if (LogIs11Enabled())
|
---|
[83153] | 1921 | Log(("... reject\n"));
|
---|
[92939] | 1922 | #endif
|
---|
[82826] | 1923 |
|
---|
[82775] | 1924 | return false;
|
---|
[82681] | 1925 | }
|
---|
[82775] | 1926 |
|
---|
[92939] | 1927 |
|
---|
| 1928 | /**
|
---|
| 1929 | * This handles the case where Rx packet must be transfered to guest driver via multiple buffers using
|
---|
| 1930 | * copy tactics slower than preferred method using a single virtq buf. Yet this is an available option
|
---|
| 1931 | * for guests. Although cited in the spec it's to accomodate guest that perhaps have memory constraints
|
---|
| 1932 | * wherein guest may benefit from smaller buffers (see MRG_RXBUF feature), in practice it is seen
|
---|
| 1933 | * that without MRG_RXBUF the linux guest enqueues 'huge' multi-segment buffers so that the largest
|
---|
| 1934 | * conceivable Rx packet can be contained in a single buffer, where for most transactions most of that
|
---|
| 1935 | * memory will be unfilled, so it is typically both wasteful and *slower* to avoid MRG_RXBUF.
|
---|
| 1936 | *
|
---|
| 1937 | * As an optimization, this multi-buffer copy is only used when:
|
---|
| 1938 | *
|
---|
| 1939 | * A. Guest has negotiated MRG_RXBUF
|
---|
| 1940 | * B. Next packet in the Rx avail queue isn't big enough to contain Rx pkt hdr+data.
|
---|
| 1941 | *
|
---|
| 1942 | * Architecture is defined in VirtIO 1.1 5.1.6 (Device Operations), which has improved
|
---|
| 1943 | * wording over the VirtIO 1.0 specification, but, as an implementation note, there is one
|
---|
| 1944 | * ambiguity that needs clarification:
|
---|
| 1945 | *
|
---|
| 1946 | * The VirtIO 1.1, 5.1.6.4 explains something in a potentially misleading way. And note,
|
---|
| 1947 | * the VirtIO spec makes a document-wide assertion that the distinction between
|
---|
| 1948 | * "SHOULD" and "MUST" is to be taken quite literally.
|
---|
| 1949 | *
|
---|
| 1950 | * The confusion is that VirtIO 1.1, 5.1.6.3.1 essentially says guest driver "SHOULD" populate
|
---|
| 1951 | * Rx queue with buffers large enough to accomodate full pkt hdr + data. That's a grammatical
|
---|
| 1952 | * error (dangling participle).
|
---|
| 1953 | *
|
---|
| 1954 | * In practice we MUST assume "SHOULD" strictly applies to the word *populate*, -not- to buffer
|
---|
| 1955 | * size, because ultimately buffer minimum size is predicated on configuration parameters,
|
---|
| 1956 | * specifically, when MRG_RXBUF feature is disabled, the driver *MUST* provide Rx bufs
|
---|
| 1957 | * (if and when it can provide them), that are *large enough* to hold pkt hdr + payload.
|
---|
| 1958 | *
|
---|
| 1959 | * Therefore, proper interpretation of 5.1.6.3.1 is, the guest *should* (ideally) keep Rx virtq
|
---|
| 1960 | * populated with appropriately sized buffers to *prevent starvation* (i.e. starvation may be
|
---|
| 1961 | * unavoidable thus can't be prohibited). As it would be a ludicrous to presume 5.1.6.3.1 is
|
---|
| 1962 | * giving guests leeway to violate MRG_RXBUF feature buf size constraints.
|
---|
| 1963 | *
|
---|
| 1964 | * @param pDevIns PDM instance
|
---|
| 1965 | * @param pThis Device instance
|
---|
| 1966 | * @param pvBuf Pointer to incoming GSO Rx data from downstream device
|
---|
| 1967 | * @param cb Amount of data given
|
---|
| 1968 | * @param rxPktHdr Rx pkt Header that's been massaged into VirtIO semantics
|
---|
| 1969 | * @param pRxVirtq Pointer to Rx virtq
|
---|
| 1970 | * @param pVirtqBuf Initial virtq buffer to start copying Rx hdr/pkt to guest into
|
---|
| 1971 | *
|
---|
| 1972 | */
|
---|
| 1973 | static int virtioNetR3RxPktMultibufXfer(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint8_t *pvPktBuf, size_t cb,
|
---|
[98121] | 1974 | PVIRTIONETPKTHDR pRxPktHdr, PVIRTIONETVIRTQ pRxVirtq, PVIRTQBUF pVirtqBuf)
|
---|
[82775] | 1975 | {
|
---|
[85016] | 1976 |
|
---|
[92939] | 1977 | size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
|
---|
| 1978 | size_t cbPktHdr = pThis->cbPktHdr;
|
---|
[83165] | 1979 |
|
---|
[92939] | 1980 | AssertMsgReturn(cbBufRemaining >= pThis->cbPktHdr,
|
---|
| 1981 | ("guest-provided Rx buf not large enough to store pkt hdr"), VERR_INTERNAL_ERROR);
|
---|
[82775] | 1982 |
|
---|
[92939] | 1983 | Log7Func((" Sending packet header to guest...\n"));
|
---|
[82775] | 1984 |
|
---|
[92939] | 1985 | /* Copy packet header to rx buf provided by caller. */
|
---|
[92999] | 1986 | size_t cbHdrEnqueued = pVirtqBuf->cbPhysReturn == cbPktHdr ? cbPktHdr : 0;
|
---|
[92939] | 1987 | virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, cbHdrEnqueued);
|
---|
[83913] | 1988 |
|
---|
[92939] | 1989 | /* Cache address of uNumBuffers field of pkthdr to update ex post facto */
|
---|
| 1990 | RTGCPHYS GCPhysNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
|
---|
[94969] | 1991 | uint16_t cVirtqBufsUsed = 0;
|
---|
[92939] | 1992 | cbBufRemaining -= cbPktHdr;
|
---|
| 1993 | /*
|
---|
| 1994 | * Copy packet to guest using as many buffers as necessary, tracking and handling whether
|
---|
| 1995 | * the buf containing the packet header was already written to the Rx queue's used buffer ring.
|
---|
| 1996 | */
|
---|
| 1997 | uint64_t uPktOffset = 0;
|
---|
| 1998 | while(uPktOffset < cb)
|
---|
| 1999 | {
|
---|
| 2000 | Log7Func((" Sending packet data (in buffer #%d) to guest...\n", cVirtqBufsUsed));
|
---|
| 2001 | size_t cbBounded = RT_MIN(cbBufRemaining, cb - uPktOffset);
|
---|
| 2002 | (void) virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbBounded,
|
---|
| 2003 | pvPktBuf + uPktOffset, pVirtqBuf, cbBounded + (cbPktHdr - cbHdrEnqueued) /* cbEnqueue */);
|
---|
| 2004 | ++cVirtqBufsUsed;
|
---|
| 2005 | cbBufRemaining -= cbBounded;
|
---|
[94969] | 2006 | uPktOffset += cbBounded;
|
---|
[92939] | 2007 | if (uPktOffset < cb)
|
---|
[82775] | 2008 | {
|
---|
[92939] | 2009 | cbHdrEnqueued = cbPktHdr;
|
---|
[94969] | 2010 | int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
|
---|
[92939] | 2011 | AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
|
---|
[94969] | 2012 | AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
|
---|
| 2013 | ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
|
---|
| 2014 | VERR_INTERNAL_ERROR);
|
---|
| 2015 | cbBufRemaining = pVirtqBuf->cbPhysReturn;
|
---|
[82775] | 2016 | }
|
---|
| 2017 | }
|
---|
| 2018 |
|
---|
[92939] | 2019 | /* Fix-up pkthdr (in guest phys. memory) with number of buffers (descriptors) that were processed */
|
---|
| 2020 | int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysNumBuffers, &cVirtqBufsUsed, sizeof(cVirtqBufsUsed));
|
---|
| 2021 | AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
|
---|
[83913] | 2022 |
|
---|
[85415] | 2023 | virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
|
---|
[92939] | 2024 | Log7(("\n"));
|
---|
| 2025 | return rc;
|
---|
[83913] | 2026 | }
|
---|
| 2027 |
|
---|
| 2028 | /**
|
---|
| 2029 | * Pad and store received packet.
|
---|
| 2030 | *
|
---|
| 2031 | * @remarks Make sure that the packet appears to upper layer as one coming
|
---|
| 2032 | * from real Ethernet: pad it and insert FCS.
|
---|
| 2033 | *
|
---|
| 2034 | * @returns VBox status code.
|
---|
| 2035 | * @param pDevIns The device instance.
|
---|
| 2036 | * @param pThis The virtio-net shared instance data.
|
---|
| 2037 | * @param pvBuf The available data.
|
---|
| 2038 | * @param cb Number of bytes available in the buffer.
|
---|
| 2039 | * @param pGso Pointer to Global Segmentation Offload structure
|
---|
[85415] | 2040 | * @param pRxVirtq Pointer to Rx virtqueue
|
---|
[83913] | 2041 | * @thread RX
|
---|
| 2042 | */
|
---|
[92939] | 2043 |
|
---|
| 2044 | static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, const void *pvBuf, size_t cb,
|
---|
| 2045 | PVIRTIONETPKTHDR pRxPktHdr, uint8_t cbPktHdr, PVIRTIONETVIRTQ pRxVirtq)
|
---|
[83913] | 2046 | {
|
---|
| 2047 | RT_NOREF(pThisCC);
|
---|
[94969] | 2048 | VIRTQBUF_T VirtqBuf;
|
---|
[97293] | 2049 |
|
---|
| 2050 | VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
|
---|
| 2051 | VirtqBuf.cRefs = 1;
|
---|
| 2052 |
|
---|
[94969] | 2053 | PVIRTQBUF pVirtqBuf = &VirtqBuf;
|
---|
| 2054 | int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
|
---|
[92939] | 2055 | AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
|
---|
[94969] | 2056 | AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
|
---|
| 2057 | ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
|
---|
| 2058 | VERR_INTERNAL_ERROR);
|
---|
[100371] | 2059 |
|
---|
[92939] | 2060 | /*
|
---|
| 2061 | * Try to do fast (e.g. single-buffer) copy to guest, even if MRG_RXBUF feature is enabled
|
---|
| 2062 | */
|
---|
| 2063 | STAM_PROFILE_START(&pThis->StatReceiveStore, a);
|
---|
| 2064 | if (RT_LIKELY(FEATURE_DISABLED(MRG_RXBUF))
|
---|
| 2065 | || RT_LIKELY(pVirtqBuf->cbPhysReturn > cb + cbPktHdr))
|
---|
[83913] | 2066 | {
|
---|
[92939] | 2067 | Log7Func(("Send Rx packet header and data to guest (single-buffer copy)...\n"));
|
---|
| 2068 | pRxPktHdr->uNumBuffers = 1;
|
---|
| 2069 | rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, 0 /* cbEnqueue */);
|
---|
[92951] | 2070 | if (rc == VINF_SUCCESS)
|
---|
| 2071 | rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cb, pvBuf, pVirtqBuf, cbPktHdr + cb /* cbEnqueue */);
|
---|
[92939] | 2072 | virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
|
---|
[92951] | 2073 | AssertMsgReturn(rc == VINF_SUCCESS, ("%Rrc\n", rc), rc);
|
---|
[83913] | 2074 | }
|
---|
| 2075 | else
|
---|
| 2076 | {
|
---|
[92939] | 2077 | Log7Func(("Send Rx pkt to guest (merged-buffer copy [MRG_RXBUF feature])...\n"));
|
---|
| 2078 | rc = virtioNetR3RxPktMultibufXfer(pDevIns, pThis, (uint8_t *)pvBuf, cb, pRxPktHdr, pRxVirtq, pVirtqBuf);
|
---|
| 2079 | return rc;
|
---|
[83913] | 2080 | }
|
---|
[92939] | 2081 | STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
|
---|
| 2082 | return VINF_SUCCESS;
|
---|
[82775] | 2083 | }
|
---|
| 2084 |
|
---|
| 2085 | /**
|
---|
| 2086 | * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
|
---|
| 2087 | */
|
---|
[98121] | 2088 | static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb,
|
---|
| 2089 | PCPDMNETWORKGSO pGso)
|
---|
[82775] | 2090 | {
|
---|
[92939] | 2091 | PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
|
---|
| 2092 | PPDMDEVINS pDevIns = pThisCC->pDevIns;
|
---|
| 2093 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 2094 | VIRTIONETPKTHDR rxPktHdr = { 0, VIRTIONET_HDR_GSO_NONE, 0, 0, 0, 0, 0 };
|
---|
[85415] | 2095 |
|
---|
[82961] | 2096 | if (!pThis->fVirtioReady)
|
---|
| 2097 | {
|
---|
| 2098 | LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
|
---|
| 2099 | return VERR_INTERRUPTED;
|
---|
| 2100 | }
|
---|
[92939] | 2101 | /*
|
---|
| 2102 | * If GSO (Global Segment Offloading) was received from downstream PDM network device, massage the
|
---|
| 2103 | * PDM-provided GSO parameters into VirtIO semantics, which get passed to guest virtio-net via
|
---|
| 2104 | * Rx pkt header. See VirtIO 1.1, 5.1.6 Device Operation for more information.
|
---|
| 2105 | */
|
---|
[82775] | 2106 | if (pGso)
|
---|
| 2107 | {
|
---|
[92939] | 2108 | LogFunc(("[%s] (%RTmac) \n", pThis->szInst, pvBuf));
|
---|
[82775] | 2109 |
|
---|
[92939] | 2110 | rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
|
---|
| 2111 | rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
|
---|
| 2112 | rxPktHdr.uGsoSize = pGso->cbMaxSeg;
|
---|
| 2113 | rxPktHdr.uChksumStart = pGso->offHdr2;
|
---|
| 2114 |
|
---|
[82863] | 2115 | switch (pGso->u8Type)
|
---|
[82775] | 2116 | {
|
---|
| 2117 | case PDMNETWORKGSOTYPE_IPV4_TCP:
|
---|
[92939] | 2118 | rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
|
---|
| 2119 | rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
|
---|
[82775] | 2120 | break;
|
---|
| 2121 | case PDMNETWORKGSOTYPE_IPV6_TCP:
|
---|
[92939] | 2122 | rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
|
---|
| 2123 | rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
|
---|
[82775] | 2124 | break;
|
---|
| 2125 | case PDMNETWORKGSOTYPE_IPV4_UDP:
|
---|
[92939] | 2126 | rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
|
---|
| 2127 | rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
|
---|
[82775] | 2128 | break;
|
---|
| 2129 | default:
|
---|
[92939] | 2130 | LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
|
---|
| 2131 | return VERR_NOT_SUPPORTED;
|
---|
[82775] | 2132 | }
|
---|
[92939] | 2133 | STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
|
---|
| 2134 | Log2Func(("[%s] gso type=%#x, cbHdrsTotal=%u cbHdrsSeg=%u mss=%u offHdr1=%#x offHdr2=%#x\n",
|
---|
| 2135 | pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
|
---|
| 2136 | pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
|
---|
[82775] | 2137 | }
|
---|
| 2138 |
|
---|
[92939] | 2139 | /*
|
---|
| 2140 | * Find a virtq with Rx bufs on avail ring, if any, and copy the packet to the guest's Rx buffer.
|
---|
| 2141 | * @todo pk: PROBABLY NOT A SOPHISTICATED ENOUGH QUEUE SELECTION ALGORTITH FOR OPTIMAL MQ (FEATURE) SUPPORT
|
---|
| 2142 | */
|
---|
[84876] | 2143 | for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
|
---|
[82961] | 2144 | {
|
---|
[84876] | 2145 | PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
|
---|
[85291] | 2146 | if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
|
---|
[82961] | 2147 | {
|
---|
[92939] | 2148 | int rc = VINF_SUCCESS;
|
---|
[83913] | 2149 | STAM_PROFILE_START(&pThis->StatReceive, a);
|
---|
[82961] | 2150 | virtioNetR3SetReadLed(pThisCC, true);
|
---|
| 2151 | if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
|
---|
[83913] | 2152 | {
|
---|
[92939] | 2153 | /* rxPktHdr is local stack variable that should not go out of scope in this use */
|
---|
| 2154 | rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pThisCC, pvBuf, cb, &rxPktHdr, pThis->cbPktHdr, pRxVirtq);
|
---|
[83913] | 2155 | STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
|
---|
| 2156 | }
|
---|
[82961] | 2157 | virtioNetR3SetReadLed(pThisCC, false);
|
---|
[83913] | 2158 | STAM_PROFILE_STOP(&pThis->StatReceive, a);
|
---|
[82961] | 2159 | return rc;
|
---|
| 2160 | }
|
---|
[82775] | 2161 | }
|
---|
[82961] | 2162 | return VERR_INTERRUPTED;
|
---|
[82775] | 2163 | }
|
---|
| 2164 |
|
---|
[82863] | 2165 | /**
|
---|
| 2166 | * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
|
---|
| 2167 | */
|
---|
| 2168 | static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
|
---|
[82775] | 2169 | {
|
---|
[92939] | 2170 |
|
---|
| 2171 | #ifdef LOG_ENABLED
|
---|
| 2172 | PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
|
---|
| 2173 | PPDMDEVINS pDevIns = pThisCC->pDevIns;
|
---|
| 2174 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
|
---|
| 2175 | LogFunc(("[%s] (%RTmac)\n", pThis->szInst, pvBuf));
|
---|
| 2176 | #endif
|
---|
| 2177 |
|
---|
[82863] | 2178 | return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
|
---|
[82775] | 2179 | }
|
---|
| 2180 |
|
---|
[92939] | 2181 | /*
|
---|
| 2182 | * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's Rx packet receive filtering.
|
---|
| 2183 | * See VirtIO 1.0, 5.1.6.5.1
|
---|
| 2184 | *
|
---|
| 2185 | * @param pThis virtio-net instance
|
---|
| 2186 | * @param pCtrlPktHdr Control packet header (which includes command parameters)
|
---|
| 2187 | * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
|
---|
| 2188 | */
|
---|
[85016] | 2189 | static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
|
---|
[84819] | 2190 | PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
|
---|
[82775] | 2191 | {
|
---|
| 2192 |
|
---|
[91703] | 2193 | #define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
|
---|
[82863] | 2194 |
|
---|
[91703] | 2195 | LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
|
---|
[82775] | 2196 | switch(pCtrlPktHdr->uCmd)
|
---|
| 2197 | {
|
---|
| 2198 | case VIRTIONET_CTRL_RX_PROMISC:
|
---|
| 2199 | break;
|
---|
| 2200 | case VIRTIONET_CTRL_RX_ALLMULTI:
|
---|
| 2201 | break;
|
---|
| 2202 | case VIRTIONET_CTRL_RX_ALLUNI:
|
---|
| 2203 | /* fallthrough */
|
---|
| 2204 | case VIRTIONET_CTRL_RX_NOMULTI:
|
---|
| 2205 | /* fallthrough */
|
---|
| 2206 | case VIRTIONET_CTRL_RX_NOUNI:
|
---|
| 2207 | /* fallthrough */
|
---|
| 2208 | case VIRTIONET_CTRL_RX_NOBCAST:
|
---|
[82863] | 2209 | AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
|
---|
| 2210 | ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
|
---|
[82775] | 2211 | VIRTIONET_ERROR);
|
---|
| 2212 | /* fall out */
|
---|
| 2213 | }
|
---|
| 2214 |
|
---|
| 2215 | uint8_t fOn, fPromiscChanged = false;
|
---|
[85016] | 2216 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
|
---|
[82775] | 2217 |
|
---|
| 2218 | switch(pCtrlPktHdr->uCmd)
|
---|
| 2219 | {
|
---|
| 2220 | case VIRTIONET_CTRL_RX_PROMISC:
|
---|
[84048] | 2221 | pThis->fPromiscuous = RT_BOOL(fOn);
|
---|
[82775] | 2222 | fPromiscChanged = true;
|
---|
[82863] | 2223 | LOG_VIRTIONET_FLAG(fPromiscuous);
|
---|
[82775] | 2224 | break;
|
---|
| 2225 | case VIRTIONET_CTRL_RX_ALLMULTI:
|
---|
[84048] | 2226 | pThis->fAllMulticast = RT_BOOL(fOn);
|
---|
[82775] | 2227 | fPromiscChanged = true;
|
---|
| 2228 | LOG_VIRTIONET_FLAG(fAllMulticast);
|
---|
| 2229 | break;
|
---|
| 2230 | case VIRTIONET_CTRL_RX_ALLUNI:
|
---|
[84048] | 2231 | pThis->fAllUnicast = RT_BOOL(fOn);
|
---|
[82775] | 2232 | LOG_VIRTIONET_FLAG(fAllUnicast);
|
---|
| 2233 | break;
|
---|
| 2234 | case VIRTIONET_CTRL_RX_NOMULTI:
|
---|
[84048] | 2235 | pThis->fNoMulticast = RT_BOOL(fOn);
|
---|
[82775] | 2236 | LOG_VIRTIONET_FLAG(fNoMulticast);
|
---|
| 2237 | break;
|
---|
| 2238 | case VIRTIONET_CTRL_RX_NOUNI:
|
---|
[84048] | 2239 | pThis->fNoUnicast = RT_BOOL(fOn);
|
---|
[82775] | 2240 | LOG_VIRTIONET_FLAG(fNoUnicast);
|
---|
| 2241 | break;
|
---|
| 2242 | case VIRTIONET_CTRL_RX_NOBCAST:
|
---|
[84048] | 2243 | pThis->fNoBroadcast = RT_BOOL(fOn);
|
---|
[82775] | 2244 | LOG_VIRTIONET_FLAG(fNoBroadcast);
|
---|
| 2245 | break;
|
---|
| 2246 | }
|
---|
| 2247 |
|
---|
| 2248 | if (pThisCC->pDrv && fPromiscChanged)
|
---|
[85016] | 2249 | pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
|
---|
[82775] | 2250 |
|
---|
| 2251 | return VIRTIONET_OK;
|
---|
| 2252 | }
|
---|
| 2253 |
|
---|
[92939] | 2254 | /*
|
---|
| 2255 | * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MAC filter tables
|
---|
| 2256 | * See VirtIO 1.0, 5.1.6.5.2
|
---|
| 2257 | *
|
---|
| 2258 | * @param pThis virtio-net instance
|
---|
| 2259 | * @param pCtrlPktHdr Control packet header (which includes command parameters)
|
---|
| 2260 | * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
|
---|
| 2261 | */
|
---|
[85016] | 2262 | static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
|
---|
[82775] | 2263 | {
|
---|
[91703] | 2264 | LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
|
---|
[82863] | 2265 |
|
---|
[91703] | 2266 |
|
---|
[84819] | 2267 | AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
|
---|
[82863] | 2268 | ("insufficient descriptor space for ctrl pkt hdr"),
|
---|
| 2269 | VIRTIONET_ERROR);
|
---|
[82775] | 2270 |
|
---|
[84819] | 2271 | size_t cbRemaining = pVirtqBuf->cbPhysSend;
|
---|
[82863] | 2272 | switch(pCtrlPktHdr->uCmd)
|
---|
[82775] | 2273 | {
|
---|
| 2274 | case VIRTIONET_CTRL_MAC_ADDR_SET:
|
---|
| 2275 | {
|
---|
| 2276 | /* Set default Rx filter MAC */
|
---|
[85016] | 2277 | AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
|
---|
| 2278 | ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
|
---|
| 2279 |
|
---|
[92939] | 2280 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(RTMAC));
|
---|
[82775] | 2281 | break;
|
---|
| 2282 | }
|
---|
| 2283 | case VIRTIONET_CTRL_MAC_TABLE_SET:
|
---|
| 2284 | {
|
---|
| 2285 | VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
|
---|
| 2286 |
|
---|
| 2287 | /* Load unicast MAC filter table */
|
---|
[85016] | 2288 | AssertMsgReturn(cbRemaining >= sizeof(cMacs),
|
---|
| 2289 | ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
|
---|
| 2290 |
|
---|
| 2291 | /* Fetch count of unicast filter MACs from guest buffer */
|
---|
| 2292 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
|
---|
[82775] | 2293 | cbRemaining -= sizeof(cMacs);
|
---|
[85016] | 2294 |
|
---|
[91703] | 2295 | Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
|
---|
[85016] | 2296 |
|
---|
[101400] | 2297 | AssertMsgReturn(cMacs <= RT_ELEMENTS(pThis->aMacUnicastFilter),
|
---|
| 2298 | ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
|
---|
| 2299 |
|
---|
[82961] | 2300 | if (cMacs)
|
---|
| 2301 | {
|
---|
| 2302 | uint32_t cbMacs = cMacs * sizeof(RTMAC);
|
---|
[85016] | 2303 |
|
---|
| 2304 | AssertMsgReturn(cbRemaining >= cbMacs,
|
---|
| 2305 | ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
|
---|
| 2306 |
|
---|
[92939] | 2307 |
|
---|
[85016] | 2308 | /* Fetch unicast table contents from guest buffer */
|
---|
| 2309 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
|
---|
[82961] | 2310 | cbRemaining -= cbMacs;
|
---|
| 2311 | }
|
---|
[82775] | 2312 | pThis->cUnicastFilterMacs = cMacs;
|
---|
| 2313 |
|
---|
| 2314 | /* Load multicast MAC filter table */
|
---|
[85016] | 2315 | AssertMsgReturn(cbRemaining >= sizeof(cMacs),
|
---|
| 2316 | ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
|
---|
| 2317 |
|
---|
| 2318 | /* Fetch count of multicast filter MACs from guest buffer */
|
---|
| 2319 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
|
---|
[82775] | 2320 | cbRemaining -= sizeof(cMacs);
|
---|
[85016] | 2321 |
|
---|
[91703] | 2322 | Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
|
---|
[85016] | 2323 |
|
---|
[101400] | 2324 | AssertMsgReturn(cMacs <= RT_ELEMENTS(pThis->aMacMulticastFilter),
|
---|
| 2325 | ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
|
---|
| 2326 |
|
---|
[82961] | 2327 | if (cMacs)
|
---|
| 2328 | {
|
---|
| 2329 | uint32_t cbMacs = cMacs * sizeof(RTMAC);
|
---|
[85016] | 2330 |
|
---|
| 2331 | AssertMsgReturn(cbRemaining >= cbMacs,
|
---|
| 2332 | ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
|
---|
| 2333 |
|
---|
| 2334 | /* Fetch multicast table contents from guest buffer */
|
---|
| 2335 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
|
---|
[82961] | 2336 | cbRemaining -= cbMacs;
|
---|
| 2337 | }
|
---|
[82775] | 2338 | pThis->cMulticastFilterMacs = cMacs;
|
---|
| 2339 |
|
---|
| 2340 | #ifdef LOG_ENABLED
|
---|
[91703] | 2341 | LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
|
---|
[92939] | 2342 | for(unsigned i = 0; i < pThis->cUnicastFilterMacs; i++)
|
---|
[82775] | 2343 | LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
|
---|
| 2344 |
|
---|
[91703] | 2345 | LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
|
---|
[92939] | 2346 | for(unsigned i = 0; i < pThis->cMulticastFilterMacs; i++)
|
---|
[83028] | 2347 | LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
|
---|
[82775] | 2348 | #endif
|
---|
[85415] | 2349 | break;
|
---|
[82775] | 2350 | }
|
---|
[85415] | 2351 | default:
|
---|
| 2352 | LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
|
---|
| 2353 | return VIRTIONET_ERROR;
|
---|
[82775] | 2354 | }
|
---|
| 2355 | return VIRTIONET_OK;
|
---|
| 2356 | }
|
---|
| 2357 |
|
---|
[92939] | 2358 | /*
|
---|
| 2359 | * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MQ (multiqueue) operations.
|
---|
| 2360 | * See VirtIO 1.0, 5.1.6.5.5
|
---|
| 2361 | *
|
---|
| 2362 | * @param pThis virtio-net instance
|
---|
| 2363 | * @param pCtrlPktHdr Control packet header (which includes command parameters)
|
---|
| 2364 | * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
|
---|
| 2365 | */
|
---|
[85415] | 2366 | static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
|
---|
| 2367 | {
|
---|
[91703] | 2368 | LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
|
---|
[85415] | 2369 |
|
---|
| 2370 | uint16_t cVirtqPairs;
|
---|
| 2371 | switch(pCtrlPktHdr->uCmd)
|
---|
| 2372 | {
|
---|
| 2373 | case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
|
---|
| 2374 | {
|
---|
[100883] | 2375 | size_t cbRemaining = pVirtqBuf->cbPhysSend;
|
---|
[85415] | 2376 |
|
---|
[101400] | 2377 | AssertMsgReturn(cbRemaining >= sizeof(cVirtqPairs),
|
---|
[85415] | 2378 | ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
|
---|
| 2379 |
|
---|
| 2380 | /* Fetch number of virtq pairs from guest buffer */
|
---|
| 2381 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
|
---|
| 2382 |
|
---|
[101400] | 2383 | AssertMsgReturn(cVirtqPairs <= VIRTIONET_MAX_QPAIRS,
|
---|
[92998] | 2384 | ("[%s] Guest CTRL MQ virtq pair count out of range [%d])\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
|
---|
[85415] | 2385 |
|
---|
[91703] | 2386 | LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
|
---|
[85415] | 2387 | pThis->cVirtqPairs = cVirtqPairs;
|
---|
| 2388 | break;
|
---|
| 2389 | }
|
---|
| 2390 | default:
|
---|
| 2391 | LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
|
---|
| 2392 | return VIRTIONET_ERROR;
|
---|
| 2393 | }
|
---|
| 2394 |
|
---|
| 2395 | /*
|
---|
| 2396 | * The MQ control function is invoked by the guest in an RPC like manner to change
|
---|
| 2397 | * the Rx/Tx queue pair count. If the new value exceeds the number of queues
|
---|
| 2398 | * (and associated workers) already initialized initialize only the new queues and
|
---|
| 2399 | * respective workers.
|
---|
| 2400 | */
|
---|
| 2401 | if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
|
---|
| 2402 | {
|
---|
[92939] | 2403 | virtioNetR3SetVirtqNames(pThis, virtioCoreIsLegacyMode(&pThis->Virtio));
|
---|
[85415] | 2404 | int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
|
---|
| 2405 | if (RT_FAILURE(rc))
|
---|
| 2406 | {
|
---|
| 2407 | LogRelFunc(("Failed to create worker threads\n"));
|
---|
| 2408 | return VIRTIONET_ERROR;
|
---|
| 2409 | }
|
---|
| 2410 | }
|
---|
| 2411 | return VIRTIONET_OK;
|
---|
| 2412 | }
|
---|
| 2413 |
|
---|
[92939] | 2414 | /*
|
---|
| 2415 | * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's VLAN filtering.
|
---|
| 2416 | * See VirtIO 1.0, 5.1.6.5.3
|
---|
| 2417 | *
|
---|
| 2418 | * @param pThis virtio-net instance
|
---|
| 2419 | * @param pCtrlPktHdr Control packet header (which includes command parameters)
|
---|
| 2420 | * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
|
---|
| 2421 | */
|
---|
[85016] | 2422 | static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
|
---|
[82775] | 2423 | {
|
---|
[91703] | 2424 | LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
|
---|
[82961] | 2425 |
|
---|
[82775] | 2426 | uint16_t uVlanId;
|
---|
[100883] | 2427 | size_t cbRemaining = pVirtqBuf->cbPhysSend;
|
---|
[85016] | 2428 |
|
---|
[100883] | 2429 | AssertMsgReturn(cbRemaining >= sizeof(uVlanId),
|
---|
[85415] | 2430 | ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
|
---|
[85016] | 2431 |
|
---|
| 2432 | /* Fetch VLAN ID from guest buffer */
|
---|
| 2433 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
|
---|
| 2434 |
|
---|
[100883] | 2435 | AssertMsgReturn(uVlanId < VIRTIONET_MAX_VLAN_ID,
|
---|
[84774] | 2436 | ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
|
---|
[85016] | 2437 |
|
---|
[91703] | 2438 | LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
|
---|
[85016] | 2439 |
|
---|
[82775] | 2440 | switch (pCtrlPktHdr->uCmd)
|
---|
| 2441 | {
|
---|
| 2442 | case VIRTIONET_CTRL_VLAN_ADD:
|
---|
| 2443 | ASMBitSet(pThis->aVlanFilter, uVlanId);
|
---|
| 2444 | break;
|
---|
| 2445 | case VIRTIONET_CTRL_VLAN_DEL:
|
---|
| 2446 | ASMBitClear(pThis->aVlanFilter, uVlanId);
|
---|
| 2447 | break;
|
---|
| 2448 | default:
|
---|
[85415] | 2449 | LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
|
---|
[82775] | 2450 | return VIRTIONET_ERROR;
|
---|
| 2451 | }
|
---|
| 2452 | return VIRTIONET_OK;
|
---|
| 2453 | }
|
---|
| 2454 |
|
---|
[92939] | 2455 | /**
|
---|
| 2456 | * Processes control command from guest.
|
---|
| 2457 | * See VirtIO 1.0 spec, 5.1.6 "Device Operation" and 5.1.6.5 "Control Virtqueue".
|
---|
| 2458 | *
|
---|
| 2459 | * The control command is contained in a virtio buffer pulled from the virtio-net defined control queue (ctrlq).
|
---|
| 2460 | * Command type is parsed is dispatched to a command-specific device-configuration handler function (e.g. RX, MAC, VLAN, MQ
|
---|
| 2461 | * and ANNOUNCE).
|
---|
| 2462 | *
|
---|
| 2463 | * This function handles all parts of the host-side of the ctrlq round-trip buffer processing.
|
---|
| 2464 | *
|
---|
[92951] | 2465 | * Invoked by worker for virtio-net control queue to process a queued control command buffer.
|
---|
[92939] | 2466 | *
|
---|
| 2467 | * @param pDevIns PDM device instance
|
---|
| 2468 | * @param pThis virtio-net device instance
|
---|
| 2469 | * @param pThisCC virtio-net device instance
|
---|
| 2470 | * @param pVirtqBuf pointer to buffer pulled from virtq (input to this function)
|
---|
| 2471 | */
|
---|
[82775] | 2472 | static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
|
---|
[84819] | 2473 | PVIRTQBUF pVirtqBuf)
|
---|
[82775] | 2474 | {
|
---|
[92939] | 2475 | if (!(pThis->fNegotiatedFeatures & VIRTIONET_F_CTRL_VQ))
|
---|
| 2476 | LogFunc(("[%s] WARNING: Guest using CTRL queue w/o negotiating VIRTIONET_F_CTRL_VQ feature\n", pThis->szInst));
|
---|
| 2477 |
|
---|
[91703] | 2478 | LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
|
---|
[82775] | 2479 |
|
---|
[84819] | 2480 | if (pVirtqBuf->cbPhysSend < 2)
|
---|
[82775] | 2481 | {
|
---|
[91703] | 2482 | LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
|
---|
[82775] | 2483 | return;
|
---|
| 2484 | }
|
---|
[84819] | 2485 | else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
|
---|
[82775] | 2486 | {
|
---|
[91703] | 2487 | LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
|
---|
[82775] | 2488 | return;
|
---|
| 2489 | }
|
---|
| 2490 |
|
---|
| 2491 | /*
|
---|
| 2492 | * Allocate buffer and read in the control command
|
---|
| 2493 | */
|
---|
[97814] | 2494 | VIRTIONET_CTRL_HDR_T CtrlPktHdr; RT_ZERO(CtrlPktHdr);
|
---|
[100883] | 2495 | AssertLogRelMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(CtrlPktHdr),
|
---|
| 2496 | ("DESC chain too small for CTRL pkt header"));
|
---|
| 2497 | virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &CtrlPktHdr, sizeof(CtrlPktHdr));
|
---|
[82863] | 2498 |
|
---|
[97814] | 2499 | Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, CtrlPktHdr.uClass, CtrlPktHdr.uCmd));
|
---|
[82961] | 2500 |
|
---|
[82775] | 2501 | uint8_t uAck;
|
---|
[97814] | 2502 | switch (CtrlPktHdr.uClass)
|
---|
[82775] | 2503 | {
|
---|
| 2504 | case VIRTIONET_CTRL_RX:
|
---|
[97814] | 2505 | uAck = virtioNetR3CtrlRx(pThis, pThisCC, &CtrlPktHdr, pVirtqBuf);
|
---|
[82775] | 2506 | break;
|
---|
| 2507 | case VIRTIONET_CTRL_MAC:
|
---|
[97814] | 2508 | uAck = virtioNetR3CtrlMac(pThis, &CtrlPktHdr, pVirtqBuf);
|
---|
[82775] | 2509 | break;
|
---|
| 2510 | case VIRTIONET_CTRL_VLAN:
|
---|
[97814] | 2511 | uAck = virtioNetR3CtrlVlan(pThis, &CtrlPktHdr, pVirtqBuf);
|
---|
[82775] | 2512 | break;
|
---|
[85415] | 2513 | case VIRTIONET_CTRL_MQ:
|
---|
[97814] | 2514 | uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, &CtrlPktHdr, pVirtqBuf);
|
---|
[85415] | 2515 | break;
|
---|
[82961] | 2516 | case VIRTIONET_CTRL_ANNOUNCE:
|
---|
| 2517 | uAck = VIRTIONET_OK;
|
---|
| 2518 | if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
|
---|
| 2519 | {
|
---|
[83913] | 2520 | LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
|
---|
[84774] | 2521 | "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
|
---|
[82961] | 2522 | break;
|
---|
| 2523 | }
|
---|
[97814] | 2524 | if (CtrlPktHdr.uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
|
---|
[82961] | 2525 | {
|
---|
[91703] | 2526 | LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
|
---|
[82961] | 2527 | break;
|
---|
| 2528 | }
|
---|
[92939] | 2529 | #if FEATURE_OFFERED(STATUS)
|
---|
[82961] | 2530 | pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
|
---|
[92939] | 2531 | #endif
|
---|
[91703] | 2532 | Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
|
---|
[82961] | 2533 | break;
|
---|
[82775] | 2534 | default:
|
---|
[97814] | 2535 | LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", CtrlPktHdr.uClass));
|
---|
[82775] | 2536 | uAck = VIRTIONET_ERROR;
|
---|
| 2537 | }
|
---|
| 2538 |
|
---|
[82826] | 2539 | /* Return CTRL packet Ack byte (result code) to guest driver */
|
---|
[82961] | 2540 | RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
|
---|
[97814] | 2541 | RTSGBUF SgBuf;
|
---|
[82775] | 2542 |
|
---|
[97814] | 2543 | RTSgBufInit(&SgBuf, aStaticSegs, RT_ELEMENTS(aStaticSegs));
|
---|
| 2544 | virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, &SgBuf, pVirtqBuf, true /* fFence */);
|
---|
[85415] | 2545 | virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
|
---|
[82775] | 2546 |
|
---|
[83028] | 2547 | LogFunc(("%s Finished processing CTRL command with status %s\n",
|
---|
[84774] | 2548 | pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
|
---|
[82775] | 2549 | }
|
---|
| 2550 |
|
---|
[92939] | 2551 | /**
|
---|
| 2552 | * Reads virtio-net pkt header from provided Phy. addr of virtio descriptor chain
|
---|
| 2553 | * (e.g. S/G segment from guest-driver provided buffer pulled from Tx virtq)
|
---|
| 2554 | * Verifies state and supported modes, sets TCP header size.
|
---|
| 2555 | *
|
---|
| 2556 | * @param pVirtio VirtIO core instance data
|
---|
| 2557 | * @param pThis virtio-net instance
|
---|
| 2558 | * @param pDevIns PDM device instance
|
---|
| 2559 | * @param GCPhys Phys. Address from where to read virtio-net pkt header
|
---|
| 2560 | * @param pPktHdr Where to store read Tx pkt hdr (virtio pkt hdr size is determined from instance configuration)
|
---|
| 2561 | * @param cbFrame Total pkt frame size to inform bounds check
|
---|
| 2562 | */
|
---|
| 2563 | static int virtioNetR3ReadVirtioTxPktHdr(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
|
---|
[82826] | 2564 | {
|
---|
[92091] | 2565 | int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
|
---|
[82826] | 2566 | if (RT_FAILURE(rc))
|
---|
[83499] | 2567 | return rc;
|
---|
[82775] | 2568 |
|
---|
[84774] | 2569 | LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
|
---|
[82863] | 2570 | pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
|
---|
[83664] | 2571 | pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
|
---|
[82826] | 2572 |
|
---|
| 2573 | if (pPktHdr->uGsoType)
|
---|
| 2574 | {
|
---|
| 2575 | /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
|
---|
[83664] | 2576 | AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
|
---|
[84048] | 2577 | && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
|
---|
[83664] | 2578 | ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
|
---|
[82826] | 2579 |
|
---|
[92939] | 2580 | uint32_t uTcpHdrSize;
|
---|
[82826] | 2581 | switch (pPktHdr->uGsoType)
|
---|
| 2582 | {
|
---|
| 2583 | case VIRTIONET_HDR_GSO_TCPV4:
|
---|
| 2584 | case VIRTIONET_HDR_GSO_TCPV6:
|
---|
[92939] | 2585 | uTcpHdrSize = sizeof(RTNETTCP);
|
---|
[82826] | 2586 | break;
|
---|
| 2587 | case VIRTIONET_HDR_GSO_UDP:
|
---|
[92939] | 2588 | uTcpHdrSize = 0;
|
---|
[82826] | 2589 | break;
|
---|
| 2590 | default:
|
---|
[83499] | 2591 | LogFunc(("Bad GSO type in packet header\n"));
|
---|
| 2592 | return VERR_INVALID_PARAMETER;
|
---|
[82826] | 2593 | }
|
---|
| 2594 | /* Header + MSS must not exceed the packet size. */
|
---|
[92939] | 2595 | AssertMsgReturn(RT_LIKELY(uTcpHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
|
---|
[83499] | 2596 | ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
|
---|
[82826] | 2597 | }
|
---|
[83499] | 2598 |
|
---|
[93013] | 2599 | AssertMsgReturn( !(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
|
---|
[83664] | 2600 | || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
|
---|
| 2601 | ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
|
---|
| 2602 | sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
|
---|
| 2603 | VERR_BUFFER_OVERFLOW);
|
---|
[83499] | 2604 |
|
---|
| 2605 | return VINF_SUCCESS;
|
---|
[82826] | 2606 | }
|
---|
| 2607 |
|
---|
[92939] | 2608 | /**
|
---|
| 2609 | * Transmits single GSO frame via PDM framework to downstream PDM device, to emit from virtual NIC.
|
---|
| 2610 | *
|
---|
| 2611 | * This does final prep of GSO parameters including checksum calculation if configured
|
---|
| 2612 | * (e.g. if VIRTIONET_HDR_F_NEEDS_CSUM flag is set).
|
---|
| 2613 | *
|
---|
| 2614 | * @param pThis virtio-net instance
|
---|
| 2615 | * @param pThisCC virtio-net instance
|
---|
| 2616 | * @param pSgBuf PDM S/G buffer containing pkt and hdr to transmit
|
---|
| 2617 | * @param pGso GSO parameters used for the packet
|
---|
| 2618 | * @param pPktHdr virtio-net pkt header to adapt to PDM semantics
|
---|
| 2619 | */
|
---|
[82826] | 2620 | static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
|
---|
[98121] | 2621 | PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
|
---|
[82826] | 2622 | {
|
---|
[92939] | 2623 |
|
---|
[82826] | 2624 | virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
|
---|
| 2625 | if (pGso)
|
---|
| 2626 | {
|
---|
[92939] | 2627 | /* Some guests (RHEL) may report HdrLen excluding transport layer header!
|
---|
| 2628 | * Thus cannot use cdHdrs provided by the guest because of different ways
|
---|
| 2629 | * it gets filled out by different versions of kernels. */
|
---|
| 2630 | Log4Func(("%s HdrLen before adjustment %d.\n", pThis->szInst, pGso->cbHdrsTotal));
|
---|
| 2631 | switch (pGso->u8Type)
|
---|
[82826] | 2632 | {
|
---|
[92939] | 2633 | case PDMNETWORKGSOTYPE_IPV4_TCP:
|
---|
| 2634 | case PDMNETWORKGSOTYPE_IPV6_TCP:
|
---|
| 2635 | pGso->cbHdrsTotal = pPktHdr->uChksumStart +
|
---|
| 2636 | ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
|
---|
| 2637 | AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
|
---|
| 2638 | ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
|
---|
| 2639 | pGso->cbHdrsSeg = pGso->cbHdrsTotal;
|
---|
| 2640 | break;
|
---|
| 2641 | case PDMNETWORKGSOTYPE_IPV4_UDP:
|
---|
| 2642 | pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
|
---|
| 2643 | pGso->cbHdrsSeg = pPktHdr->uChksumStart;
|
---|
| 2644 | break;
|
---|
[95592] | 2645 | case PDMNETWORKGSOTYPE_INVALID:
|
---|
| 2646 | LogFunc(("%s ignoring invalid GSO frame\n", pThis->szInst));
|
---|
| 2647 | return VERR_INVALID_PARAMETER;
|
---|
[82826] | 2648 | }
|
---|
[92939] | 2649 | /* Update GSO structure embedded into the frame */
|
---|
| 2650 | ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
|
---|
| 2651 | ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
|
---|
| 2652 | Log4Func(("%s adjusted HdrLen to %d.\n",
|
---|
| 2653 | pThis->szInst, pGso->cbHdrsTotal));
|
---|
[83028] | 2654 | Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
|
---|
[84774] | 2655 | pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
|
---|
[82826] | 2656 | pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
|
---|
[83913] | 2657 | STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
|
---|
[82826] | 2658 | }
|
---|
[82863] | 2659 | else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
|
---|
[82826] | 2660 | {
|
---|
[83913] | 2661 | STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
|
---|
[82826] | 2662 | /*
|
---|
| 2663 | * This is not GSO frame but checksum offloading is requested.
|
---|
| 2664 | */
|
---|
[92939] | 2665 | virtioNetR3Calc16BitChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
|
---|
[82863] | 2666 | pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
|
---|
[82826] | 2667 | }
|
---|
| 2668 |
|
---|
[84774] | 2669 | return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
|
---|
[82826] | 2670 | }
|
---|
| 2671 |
|
---|
[92939] | 2672 | /**
|
---|
| 2673 | * Non-reentrant function transmits all available packets from specified Tx virtq to downstream
|
---|
| 2674 | * PDM device (if cable is connected). For each Tx pkt, virtio-net pkt header is converted
|
---|
| 2675 | * to required GSO information (VBox host network stack semantics)
|
---|
| 2676 | *
|
---|
| 2677 | * @param pDevIns PDM device instance
|
---|
| 2678 | * @param pThis virtio-net device instance
|
---|
| 2679 | * @param pThisCC virtio-net device instance
|
---|
| 2680 | * @param pTxVirtq Address of transmit virtq
|
---|
| 2681 | * @param fOnWorkerThread Flag to PDM whether to use caller's or or PDM transmit worker's thread.
|
---|
| 2682 | */
|
---|
| 2683 | static int virtioNetR3TransmitPkts(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
|
---|
[98121] | 2684 | PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
|
---|
[82826] | 2685 | {
|
---|
[82863] | 2686 | PVIRTIOCORE pVirtio = &pThis->Virtio;
|
---|
[82826] | 2687 |
|
---|
| 2688 |
|
---|
[82863] | 2689 | if (!pThis->fVirtioReady)
|
---|
[82826] | 2690 | {
|
---|
[91703] | 2691 | LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
|
---|
[84774] | 2692 | pThis->szInst, pThis->virtioNetConfig.uStatus));
|
---|
[91703] | 2693 | return VERR_IGNORED;
|
---|
[82826] | 2694 | }
|
---|
| 2695 |
|
---|
| 2696 | if (!pThis->fCableConnected)
|
---|
| 2697 | {
|
---|
[91703] | 2698 | Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
|
---|
| 2699 | return VERR_IGNORED;
|
---|
[82826] | 2700 | }
|
---|
| 2701 |
|
---|
[90931] | 2702 | /*
|
---|
[92939] | 2703 | * Only one thread is allowed to transmit at a time, others should skip transmission as the packets
|
---|
| 2704 | * will be picked up by the transmitting thread.
|
---|
[90931] | 2705 | */
|
---|
| 2706 | if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
|
---|
[91703] | 2707 | return VERR_IGNORED;
|
---|
[90774] | 2708 |
|
---|
[82826] | 2709 | PPDMINETWORKUP pDrv = pThisCC->pDrv;
|
---|
| 2710 | if (pDrv)
|
---|
| 2711 | {
|
---|
| 2712 | int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
|
---|
| 2713 | Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
|
---|
| 2714 | if (rc == VERR_TRY_AGAIN)
|
---|
| 2715 | {
|
---|
| 2716 | ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
|
---|
[91703] | 2717 | return VERR_TRY_AGAIN;
|
---|
[82826] | 2718 | }
|
---|
| 2719 | }
|
---|
[85415] | 2720 | int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
|
---|
[83153] | 2721 | if (!cPkts)
|
---|
| 2722 | {
|
---|
[91703] | 2723 | LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
|
---|
[82826] | 2724 |
|
---|
[83153] | 2725 | if (pDrv)
|
---|
| 2726 | pDrv->pfnEndXmit(pDrv);
|
---|
| 2727 |
|
---|
| 2728 | ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
|
---|
[91703] | 2729 | return VERR_MISSING;
|
---|
[83153] | 2730 | }
|
---|
[91703] | 2731 | LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
|
---|
[83153] | 2732 |
|
---|
[82863] | 2733 | virtioNetR3SetWriteLed(pThisCC, true);
|
---|
[82826] | 2734 |
|
---|
[94969] | 2735 | /* Disable notifications until all available descriptors have been processed */
|
---|
| 2736 | if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX))
|
---|
| 2737 | virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, false /* fEnable */);
|
---|
| 2738 |
|
---|
[82961] | 2739 | int rc;
|
---|
[94969] | 2740 | VIRTQBUF_T VirtqBuf;
|
---|
[97293] | 2741 |
|
---|
| 2742 | VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
|
---|
| 2743 | VirtqBuf.cRefs = 1;
|
---|
| 2744 |
|
---|
[94969] | 2745 | PVIRTQBUF pVirtqBuf = &VirtqBuf;
|
---|
| 2746 | while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, pVirtqBuf)) == VINF_SUCCESS)
|
---|
[82826] | 2747 | {
|
---|
[91703] | 2748 | Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
|
---|
[82961] | 2749 |
|
---|
[84819] | 2750 | PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
|
---|
[83499] | 2751 | PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
|
---|
| 2752 | uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
|
---|
[92939] | 2753 | size_t uFrameSize = 0;
|
---|
[82826] | 2754 |
|
---|
[92091] | 2755 | AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
|
---|
[91703] | 2756 | ("Desc chain's first seg has insufficient space for pkt header!\n"),
|
---|
| 2757 | VERR_INTERNAL_ERROR);
|
---|
| 2758 |
|
---|
[94969] | 2759 | VIRTIONETPKTHDR PktHdr;
|
---|
| 2760 | PVIRTIONETPKTHDR pPktHdr = &PktHdr;
|
---|
[91703] | 2761 |
|
---|
[92939] | 2762 | /* Compute total frame size from guest (including virtio-net pkt hdr) */
|
---|
| 2763 | for (unsigned i = 0; i < cSegsFromGuest && uFrameSize < VIRTIONET_MAX_FRAME_SIZE; i++)
|
---|
| 2764 | uFrameSize += paSegsFromGuest[i].cbSeg;
|
---|
[82826] | 2765 |
|
---|
[92939] | 2766 | Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uFrameSize));
|
---|
| 2767 | Assert(uFrameSize <= VIRTIONET_MAX_FRAME_SIZE);
|
---|
[82826] | 2768 |
|
---|
| 2769 | /* Truncate oversized frames. */
|
---|
[92939] | 2770 | if (uFrameSize > VIRTIONET_MAX_FRAME_SIZE)
|
---|
| 2771 | uFrameSize = VIRTIONET_MAX_FRAME_SIZE;
|
---|
[82826] | 2772 |
|
---|
[83499] | 2773 | if (pThisCC->pDrv)
|
---|
[82826] | 2774 | {
|
---|
[92939] | 2775 | uFrameSize -= pThis->cbPktHdr;
|
---|
| 2776 | /*
|
---|
| 2777 | * Peel off pkt header and convert to PDM/GSO semantics.
|
---|
| 2778 | */
|
---|
| 2779 | rc = virtioNetR3ReadVirtioTxPktHdr(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uFrameSize /* cbFrame */);
|
---|
[84774] | 2780 | if (RT_FAILURE(rc))
|
---|
[91703] | 2781 | return rc;
|
---|
[92091] | 2782 | virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
|
---|
[84774] | 2783 |
|
---|
[91703] | 2784 | PDMNETWORKGSO Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
|
---|
[84774] | 2785 |
|
---|
[92939] | 2786 | /* Allocate PDM transmit buffer to send guest provided network frame from to VBox network leaf device */
|
---|
[82863] | 2787 | PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
|
---|
[92939] | 2788 | rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uFrameSize, pGso, &pSgBufToPdmLeafDevice);
|
---|
| 2789 |
|
---|
| 2790 | /*
|
---|
| 2791 | * Copy virtio-net guest S/G buffer to PDM leaf driver S/G buffer
|
---|
| 2792 | * converting from GCphys to virt memory at the same time
|
---|
| 2793 | */
|
---|
[82826] | 2794 | if (RT_SUCCESS(rc))
|
---|
| 2795 | {
|
---|
[83913] | 2796 | STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
|
---|
| 2797 | STAM_PROFILE_START(&pThis->StatTransmitSend, a);
|
---|
| 2798 |
|
---|
[83499] | 2799 | size_t cbCopied = 0;
|
---|
[92939] | 2800 | size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uFrameSize;
|
---|
| 2801 | uint64_t uOffset = 0;
|
---|
[83499] | 2802 | while (cbRemain)
|
---|
[82826] | 2803 | {
|
---|
[83499] | 2804 | PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
|
---|
[84774] | 2805 | uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
|
---|
[83499] | 2806 | uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
|
---|
[84774] | 2807 | uint64_t srcSgCur = (uint64_t)pSgPhysSend->GCPhysCur;
|
---|
[83499] | 2808 | cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
|
---|
[91703] | 2809 | virtioCoreGCPhysRead(pVirtio, pDevIns,
|
---|
[84774] | 2810 | (RTGCPHYS)pSgPhysSend->GCPhysCur,
|
---|
[83499] | 2811 | ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
|
---|
[84876] | 2812 | virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
|
---|
[83499] | 2813 | cbRemain -= cbCopied;
|
---|
| 2814 | uOffset += cbCopied;
|
---|
| 2815 | }
|
---|
[82826] | 2816 |
|
---|
[92939] | 2817 | LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
|
---|
| 2818 | uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
|
---|
[83499] | 2819 |
|
---|
[91703] | 2820 | rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
|
---|
[82961] | 2821 | if (RT_FAILURE(rc))
|
---|
| 2822 | {
|
---|
[91703] | 2823 | LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
|
---|
[83913] | 2824 | STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
|
---|
| 2825 | STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
|
---|
[82961] | 2826 | pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
|
---|
| 2827 | }
|
---|
[83913] | 2828 | STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
|
---|
| 2829 | STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
|
---|
[82826] | 2830 | }
|
---|
| 2831 | else
|
---|
| 2832 | {
|
---|
[92939] | 2833 | Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
|
---|
[82826] | 2834 | /* Stop trying to fetch TX descriptors until we get more bandwidth. */
|
---|
| 2835 | break;
|
---|
| 2836 | }
|
---|
| 2837 |
|
---|
[85415] | 2838 | virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
|
---|
[82826] | 2839 |
|
---|
[84876] | 2840 | /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
|
---|
[85415] | 2841 | virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
|
---|
| 2842 | virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
|
---|
[83499] | 2843 | }
|
---|
[83587] | 2844 |
|
---|
[94969] | 2845 | /* Before we break the loop we need to check if the queue is empty,
|
---|
| 2846 | * re-enable notifications, and then re-check again to avoid missing
|
---|
| 2847 | * a notification for the descriptor that is added to the queue
|
---|
| 2848 | * after we have checked it on being empty, but before we re-enabled
|
---|
| 2849 | * notifications.
|
---|
| 2850 | */
|
---|
| 2851 | if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
|
---|
| 2852 | && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
|
---|
| 2853 | virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
|
---|
[82826] | 2854 | }
|
---|
| 2855 | virtioNetR3SetWriteLed(pThisCC, false);
|
---|
| 2856 |
|
---|
| 2857 | if (pDrv)
|
---|
| 2858 | pDrv->pfnEndXmit(pDrv);
|
---|
[82961] | 2859 |
|
---|
[82826] | 2860 | ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
|
---|
[91703] | 2861 | return VINF_SUCCESS;
|
---|
[82826] | 2862 | }
|
---|
| 2863 |
|
---|
| 2864 | /**
|
---|
| 2865 | * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
|
---|
| 2866 | */
|
---|
[82863] | 2867 | static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
|
---|
[82826] | 2868 | {
|
---|
[83153] | 2869 | LogFunc(("\n"));
|
---|
[82826] | 2870 | PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
|
---|
| 2871 | PPDMDEVINS pDevIns = pThisCC->pDevIns;
|
---|
| 2872 | PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
|
---|
[84803] | 2873 | PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(0)];
|
---|
[83913] | 2874 | STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
|
---|
| 2875 |
|
---|
[92939] | 2876 | (void)virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
|
---|
[82826] | 2877 | }
|
---|
| 2878 |
|
---|
|
---|