VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevVirtioNet.cpp

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 160.8 KB
RevLine 
[82681]1/* $Id: DevVirtioNet.cpp 106061 2024-09-16 14:03:52Z vboxsync $ $Revision: 106061 $ $Date: 2024-09-16 14:03:52 +0000 (Mon, 16 Sep 2024) $ $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/*
[106061]15 * Copyright (C) 2006-2024 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]114static 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 \
[103416]141 ( VIRTIONET_F_CSUM \
[82681]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 \
[103416]148 | VIRTIONET_F_GUEST_CSUM) /* @bugref(4796) Guest must handle partial chksums */
[82681]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]254struct 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]264typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
265AssertCompileSize(VIRTIONETPKTHDR, 12);
[82681]266
267/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
268#pragma pack(1)
269struct virtio_net_ctrl_hdr {
270 uint8_t uClass; /**< class */
271 uint8_t uCmd; /**< command */
272};
273#pragma pack()
[82775]274typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
[82681]275
[82775]276typedef 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]293typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
294typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
[82681]295typedef 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
317struct 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]329uint64_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]337typedef 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 */
350struct VIRTIONETWORKER;
351struct VIRTIONETWORKERR3;
352
[84803]353typedef 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 */
366typedef 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]376typedef VIRTIONETWORKER *PVIRTIONETWORKER;
377
378/**
379 * Worker thread context, ring-3 state.
380 */
381typedef 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]388typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
389
390/**
391 * VirtIO Host NET device state, shared edition.
392 *
393 * @extends VIRTIOCORE
394 */
395typedef 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
[106001]481 /** No multicast mode - Suppresses multicast receive */
[82863]482 uint8_t fNoMulticast;
[82775]483
484 /** No unicast mode - Suppresses unicast receive */
485 uint8_t fNoUnicast;
486
[106001]487 /** No broadcast mode - Suppresses broadcast receive */
[82775]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. */
523typedef VIRTIONET *PVIRTIONET;
524
525/**
526 * VirtIO Host NET device state, ring-3 edition.
527 *
528 * @extends VIRTIOCORER3
529 */
530typedef 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. */
572typedef VIRTIONETR3 *PVIRTIONETR3;
573
574/**
575 * VirtIO Host NET device state, ring-0 edition.
576 */
577typedef 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. */
583typedef VIRTIONETR0 *PVIRTIONETR0;
584
585/**
586 * VirtIO Host NET device state, raw-mode edition.
587 */
588typedef 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. */
594typedef VIRTIONETRC *PVIRTIONETRC;
595
596/** @typedef VIRTIONETCC
597 * The instance data for the current context. */
598typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
599
600/** @typedef PVIRTIONETCC
601 * Pointer to the instance data for the current context. */
602typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
603
[84774]604#ifdef IN_RING3
605static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
[85415]606static 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]615DECLINLINE(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]647static 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]667static 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 */
719static 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 */
739DECLINLINE(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 */
757DECLINLINE(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]767void 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 */
794static 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);
[104432]907 pHlp->pfnPrintf(pHlp, " MMIO mapping name ......... %s\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");
[104432]926 pHlp->pfnPrintf(pHlp, " Default Rx MAC filter: .... %RTmac\n", &pThis->rxFilterMacDefault);
[84774]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]966DECLINLINE(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]1018static 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 */
1046static 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 */
1057static 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]1066static 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 */
1107static 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
1122static 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]1157static 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]1288static 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 */
1436static 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]1458static 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 */
1553DECLINLINE(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]1583static 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]1598static 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 */
1612DECLINLINE(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]1634static 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 */
1667static 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]1685static 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]1746DECLINLINE(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 */
1791static 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 */
1805DECLINLINE(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 */
1818DECLINLINE(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]1831static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
[82681]1832{
[82961]1833
[93000]1834RT_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 */
1973static 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
2044static 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]2088static 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 */
2168static 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]2189static 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]2262static 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]2366static 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]2422static 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]2472static 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 */
2563static 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]2620static 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 */
2683static 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));
[102827]2809 /*
2810 * Guest sent a bogus S/G chain, there doesn't seem to be a way to report an error but
2811 * as this shouldn't happen anyway we just stop proccessing this chain.
2812 */
2813 if (RT_UNLIKELY(!cbCopied))
2814 break;
[91703]2815 virtioCoreGCPhysRead(pVirtio, pDevIns,
[84774]2816 (RTGCPHYS)pSgPhysSend->GCPhysCur,
[83499]2817 ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
[84876]2818 virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
[83499]2819 cbRemain -= cbCopied;
2820 uOffset += cbCopied;
2821 }
[82826]2822
[92939]2823 LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
2824 uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
[83499]2825
[91703]2826 rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
[82961]2827 if (RT_FAILURE(rc))
2828 {
[91703]2829 LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
[83913]2830 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2831 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
[82961]2832 pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
2833 }
[83913]2834 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2835 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
[82826]2836 }
2837 else
2838 {
[92939]2839 Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
[82826]2840 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
2841 break;
2842 }
2843
[85415]2844 virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
[82826]2845
[84876]2846 /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
[85415]2847 virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
2848 virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
[83499]2849 }
[83587]2850
[94969]2851 /* Before we break the loop we need to check if the queue is empty,
2852 * re-enable notifications, and then re-check again to avoid missing
2853 * a notification for the descriptor that is added to the queue
2854 * after we have checked it on being empty, but before we re-enabled
2855 * notifications.
2856 */
2857 if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
2858 && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
2859 virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
[82826]2860 }
2861 virtioNetR3SetWriteLed(pThisCC, false);
2862
2863 if (pDrv)
2864 pDrv->pfnEndXmit(pDrv);
[82961]2865
[82826]2866 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
[91703]2867 return VINF_SUCCESS;
[82826]2868}
2869
2870/**
2871 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2872 */
[82863]2873static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
[82826]2874{
[83153]2875 LogFunc(("\n"));
[82826]2876 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2877 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2878 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);