VirtualBox

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

Last change on this file since 99775 was 99775, checked in by vboxsync, 2 years ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 162.7 KB
Line 
1/* $Id: DevVirtioNet.cpp 99775 2023-05-12 12:21:58Z vboxsync $ $Revision: 99775 $ $Date: 2023-05-12 12:21:58 +0000 (Fri, 12 May 2023) $ $Author: vboxsync $ */
2
3/** @file
4 * VBox storage devices - Virtio NET Driver
5 *
6 * Log-levels used:
7 * - Level 1: The most important (but usually rare) things to note
8 * - Level 2: NET command logging
9 * - Level 3: Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
10 * - Level 6: Device <-> Guest Driver negotation, traffic, notifications and state handling
11 * - Level 12: Brief formatted hex dumps of I/O data
12 */
13
14/*
15 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
16 *
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
34 */
35
36/*******************************************************************************************************************************
37* Header Files *
38***************************************************************************************************************************** **/
39#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
40#define VIRTIONET_WITH_GSO
41
42#include <iprt/types.h>
43#include <iprt/errcore.h>
44#include <iprt/assert.h>
45#include <iprt/string.h>
46
47#include <VBox/sup.h>
48#include <VBox/vmm/pdmdev.h>
49#include <VBox/vmm/stam.h>
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>
55
56
57#ifdef IN_RING3
58# include <VBox/VBoxPktDmp.h>
59# include <iprt/alloc.h>
60# include <iprt/memcache.h>
61# include <iprt/semaphore.h>
62# include <iprt/sg.h>
63# include <iprt/param.h>
64# include <iprt/uuid.h>
65#endif
66#include "../VirtIO/VirtioCore.h"
67
68#include "VBoxDD.h"
69
70#define VIRTIONET_TRANSITIONAL_ENABLE_FLAG 1 /** < If set behave as VirtIO "transitional" device */
71
72/** The current saved state version for the virtio core. */
73#define VIRTIONET_SAVEDSTATE_VERSION UINT32_C(1)
74#define VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY UINT32_C(1) /**< Grandfathered in from DevVirtioNet.cpp */
75#define VIRTIONET_SAVEDSTATE_VERSION_LEGACY UINT32_C(2) /**< Grandfathered in from DevVirtioNet.cpp */
76#define VIRTIONET_VERSION_MARKER_MAC_ADDR { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /** SSM handling */
77
78/*
79 * Glossary of networking acronyms used in feature names below:
80 *
81 * GSO = Generic Segmentation Offload
82 * TSO = TCP Segmentation Offload
83 * UFO = UDP Fragmentation Offload
84 * ECN = Explicit Congestion Notification
85 */
86
87/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
88 * @{ */
89#define VIRTIONET_F_CSUM RT_BIT_64(0) /**< Handle packets with partial checksum */
90#define VIRTIONET_F_GUEST_CSUM RT_BIT_64(1) /**< Handles packets with partial checksum */
91#define VIRTIONET_F_CTRL_GUEST_OFFLOADS RT_BIT_64(2) /**< Control channel offloads reconfig support */
92#define VIRTIONET_F_MAC RT_BIT_64(5) /**< Device has given MAC address */
93#define VIRTIONET_F_GUEST_TSO4 RT_BIT_64(7) /**< Driver can receive TSOv4 */
94#define VIRTIONET_F_GUEST_TSO6 RT_BIT_64(8) /**< Driver can receive TSOv6 */
95#define VIRTIONET_F_GUEST_ECN RT_BIT_64(9) /**< Driver can receive TSO with ECN */
96#define VIRTIONET_F_GUEST_UFO RT_BIT_64(10) /**< Driver can receive UFO */
97#define VIRTIONET_F_HOST_TSO4 RT_BIT_64(11) /**< Device can receive TSOv4 */
98#define VIRTIONET_F_HOST_TSO6 RT_BIT_64(12) /**< Device can receive TSOv6 */
99#define VIRTIONET_F_HOST_ECN RT_BIT_64(13) /**< Device can receive TSO with ECN */
100#define VIRTIONET_F_HOST_UFO RT_BIT_64(14) /**< Device can receive UFO */
101#define VIRTIONET_F_MRG_RXBUF RT_BIT_64(15) /**< Driver can merge receive buffers */
102#define VIRTIONET_F_STATUS RT_BIT_64(16) /**< Config status field is available */
103#define VIRTIONET_F_CTRL_VQ RT_BIT_64(17) /**< Control channel is available */
104#define VIRTIONET_F_CTRL_RX RT_BIT_64(18) /**< Control channel RX mode + MAC addr filtering */
105#define VIRTIONET_F_CTRL_VLAN RT_BIT_64(19) /**< Control channel VLAN filtering */
106#define VIRTIONET_F_CTRL_RX_EXTRA RT_BIT_64(20) /**< Control channel RX mode extra functions */
107#define VIRTIONET_F_GUEST_ANNOUNCE RT_BIT_64(21) /**< Driver can send gratuitous packets */
108#define VIRTIONET_F_MQ RT_BIT_64(22) /**< Support ultiqueue with auto receive steering */
109#define VIRTIONET_F_CTRL_MAC_ADDR RT_BIT_64(23) /**< Set MAC address through control channel */
110/** @} */
111
112#ifdef IN_RING3
113static const VIRTIO_FEATURES_LIST s_aDevSpecificFeatures[] =
114{
115 { VIRTIONET_F_STATUS, " STATUS Configuration status field is available.\n" },
116 { VIRTIONET_F_MAC, " MAC Host has given MAC address.\n" },
117 { VIRTIONET_F_CTRL_VQ, " CTRL_VQ Control channel is available.\n" },
118 { VIRTIONET_F_CTRL_MAC_ADDR, " CTRL_MAC_ADDR Set MAC address through control channel.\n" },
119 { VIRTIONET_F_CTRL_RX, " CTRL_RX Control channel RX mode support.\n" },
120 { VIRTIONET_F_CTRL_VLAN, " CTRL_VLAN Control channel VLAN filtering.\n" },
121 { VIRTIONET_F_CTRL_GUEST_OFFLOADS, " CTRL_GUEST_OFFLOADS Control channel offloads reconfiguration support.\n" },
122 { VIRTIONET_F_GUEST_CSUM, " GUEST_CSUM Guest handles packets with partial checksum.\n" },
123 { VIRTIONET_F_GUEST_ANNOUNCE, " GUEST_ANNOUNCE Guest can send gratuitous packets.\n" },
124 { VIRTIONET_F_GUEST_TSO4, " GUEST_TSO4 Guest can receive TSOv4.\n" },
125 { VIRTIONET_F_GUEST_TSO6, " GUEST_TSO6 Guest can receive TSOv6.\n" },
126 { VIRTIONET_F_GUEST_ECN, " GUEST_ECN Guest can receive TSO with ECN.\n" },
127 { VIRTIONET_F_GUEST_UFO, " GUEST_UFO Guest can receive UFO.\n" },
128 { VIRTIONET_F_HOST_TSO4, " HOST_TSO4 Host can receive TSOv4.\n" },
129 { VIRTIONET_F_HOST_TSO6, " HOST_TSO6 Host can receive TSOv6.\n" },
130 { VIRTIONET_F_HOST_ECN, " HOST_ECN Host can receive TSO with ECN.\n" },
131 { VIRTIONET_F_HOST_UFO, " HOST_UFO Host can receive UFO.\n" },
132 { VIRTIONET_F_MQ, " MQ Host supports multiqueue with automatic receive steering.\n" },
133 { VIRTIONET_F_CSUM, " CSUM Host handles packets with partial checksum.\n" },
134 { VIRTIONET_F_MRG_RXBUF, " MRG_RXBUF Guest can merge receive buffers.\n" },
135};
136#endif
137
138#ifdef VIRTIONET_WITH_GSO
139# define VIRTIONET_HOST_FEATURES_GSO \
140 VIRTIONET_F_CSUM \
141 | VIRTIONET_F_HOST_TSO4 \
142 | VIRTIONET_F_HOST_TSO6 \
143 | VIRTIONET_F_HOST_UFO \
144 | VIRTIONET_F_GUEST_TSO4 \
145 | VIRTIONET_F_GUEST_TSO6 \
146 | VIRTIONET_F_GUEST_UFO \
147 | VIRTIONET_F_GUEST_CSUM /* @bugref(4796) Guest must handle partial chksums */
148#else
149# define VIRTIONET_HOST_FEATURES_GSO
150#endif
151
152#define VIRTIONET_HOST_FEATURES_OFFERED \
153 VIRTIONET_F_STATUS \
154 | VIRTIONET_F_GUEST_ANNOUNCE \
155 | VIRTIONET_F_MAC \
156 | VIRTIONET_F_CTRL_VQ \
157 | VIRTIONET_F_CTRL_RX \
158 | VIRTIONET_F_CTRL_VLAN \
159 | VIRTIONET_HOST_FEATURES_GSO \
160 | VIRTIONET_F_MRG_RXBUF
161
162#define FEATURE_ENABLED(feature) RT_BOOL(!!(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature))
163#define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
164#define FEATURE_OFFERED(feature) VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
165
166#if FEATURE_OFFERED(MQ)
167/* Instance data doesn't allow an array large enough to contain VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX entries */
168# define VIRTIONET_MAX_QPAIRS 1 /* This should be increased at some point and made to work */
169#else
170# define VIRTIONET_MAX_QPAIRS VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN /* default, VirtIO 1.0, 5.1.6.5.5 */
171#endif
172
173#define VIRTIONET_CTRL_MQ_VQ_PAIRS 64
174#define VIRTIONET_MAX_WORKERS VIRTIONET_MAX_QPAIRS + 1
175#define VIRTIONET_MAX_VIRTQS (VIRTIONET_MAX_QPAIRS * 2 + 1)
176#define VIRTIONET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP pkt size + Eth. header w/VLAN tag */
177#define VIRTIONET_MAC_FILTER_LEN 64
178#define VIRTIONET_MAX_VLAN_ID 4096
179#define VIRTIONET_RX_SEG_COUNT 32
180
181#define VIRTQNAME(uVirtqNbr) (pThis->aVirtqs[uVirtqNbr]->szName)
182#define CBVIRTQNAME(uVirtqNbr) RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
183
184#define IS_TX_VIRTQ(n) ((n) != CTRLQIDX && ((n) & 1))
185#define IS_RX_VIRTQ(n) ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
186#define IS_CTRL_VIRTQ(n) ((n) == CTRLQIDX)
187
188/*
189 * Macros to calculate queue type-pecific index number regardless of scale. VirtIO 1.0, 5.1.2
190 */
191#define RXQIDX(qPairIdx) (qPairIdx * 2)
192#define TXQIDX(qPairIdx) (RXQIDX(qPairIdx) + 1)
193#define CTRLQIDX (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
194
195#define IS_LINK_UP(pState) !!(pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
196#define IS_LINK_DOWN(pState) !IS_LINK_UP(pState)
197
198#define SET_LINK_UP(pState) \
199 LogFunc(("SET_LINK_UP\n")); \
200 pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
201 virtioCoreNotifyConfigChanged(&pThis->Virtio)
202
203#define SET_LINK_DOWN(pState) \
204 LogFunc(("SET_LINK_DOWN\n")); \
205 pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
206 virtioCoreNotifyConfigChanged(&pThis->Virtio)
207
208#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
209 (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
210
211#define PCI_DEVICE_ID_VIRTIONET_HOST 0x1000 /**< VirtIO transitional device ID for network card */
212#define PCI_CLASS_BASE_NETWORK_CONTROLLER 0x0200 /**< PCI Network device class */
213#define PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER 0x00 /**< PCI NET Controller subclass */
214#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
215#define VIRTIONET_PCI_CLASS 0x01 /**< Base class Mass Storage? */
216
217/**
218 * VirtIO Network (virtio-net) device-specific configuration subregion (VirtIO 1.0, 5.1.4)
219 * Guest MMIO is processed through callback to VirtIO core which forwards references to network configuration
220 * fields to this device-specific code through a callback.
221 */
222#pragma pack(1)
223
224 typedef struct virtio_net_config
225 {
226 RTMAC uMacAddress; /**< mac */
227
228#if FEATURE_OFFERED(STATUS)
229 uint16_t uStatus; /**< status */
230#endif
231
232#if FEATURE_OFFERED(MQ)
233 uint16_t uMaxVirtqPairs; /**< max_virtq_pairs */
234#endif
235
236 } VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
237
238#pragma pack()
239
240#define VIRTIONET_F_LINK_UP 1 /**< config status: Link is up */
241#define VIRTIONET_F_ANNOUNCE 2 /**< config status: Announce */
242
243/** @name VirtIO 1.0 NET Host Device device specific control types
244 * @{ */
245#define VIRTIONET_HDR_F_NEEDS_CSUM 1 /**< flags: Packet needs checksum */
246#define VIRTIONET_HDR_GSO_NONE 0 /**< gso_type: No Global Segmentation Offset */
247#define VIRTIONET_HDR_GSO_TCPV4 1 /**< gso_type: Global Segment Offset for TCPV4 */
248#define VIRTIONET_HDR_GSO_UDP 3 /**< gso_type: Global Segment Offset for UDP */
249#define VIRTIONET_HDR_GSO_TCPV6 4 /**< gso_type: Global Segment Offset for TCPV6 */
250#define VIRTIONET_HDR_GSO_ECN 0x80 /**< gso_type: Explicit Congestion Notification */
251/** @} */
252
253/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
254#pragma pack(1)
255struct virtio_net_pkt_hdr {
256 uint8_t uFlags; /**< flags */
257 uint8_t uGsoType; /**< gso_type */
258 uint16_t uHdrLen; /**< hdr_len */
259 uint16_t uGsoSize; /**< gso_size */
260 uint16_t uChksumStart; /**< Chksum_start */
261 uint16_t uChksumOffset; /**< Chksum_offset */
262 uint16_t uNumBuffers; /**< num_buffers */
263};
264#pragma pack()
265typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
266AssertCompileSize(VIRTIONETPKTHDR, 12);
267
268/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
269#pragma pack(1)
270struct virtio_net_ctrl_hdr {
271 uint8_t uClass; /**< class */
272 uint8_t uCmd; /**< command */
273};
274#pragma pack()
275typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
276
277typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
278
279/* Command entry fAck values */
280#define VIRTIONET_OK 0 /**< Internal success status */
281#define VIRTIONET_ERROR 1 /**< Internal failure status */
282
283/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
284 * @{ */
285#define VIRTIONET_CTRL_RX 0 /**< Control class: Receive filtering */
286#define VIRTIONET_CTRL_RX_PROMISC 0 /**< Promiscuous mode */
287#define VIRTIONET_CTRL_RX_ALLMULTI 1 /**< All-multicast receive */
288#define VIRTIONET_CTRL_RX_ALLUNI 2 /**< All-unicast receive */
289#define VIRTIONET_CTRL_RX_NOMULTI 3 /**< No multicast receive */
290#define VIRTIONET_CTRL_RX_NOUNI 4 /**< No unicast receive */
291#define VIRTIONET_CTRL_RX_NOBCAST 5 /**< No broadcast receive */
292/** @} */
293
294typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
295typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
296typedef uint8_t VIRTIONET_CTRL_MAC_ENTRIES[][6];
297
298/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
299 * @{ */
300#define VIRTIONET_CTRL_MAC 1 /**< Control class: MAC address filtering */
301#define VIRTIONET_CTRL_MAC_TABLE_SET 0 /**< Set MAC table */
302#define VIRTIONET_CTRL_MAC_ADDR_SET 1 /**< Set default MAC address */
303/** @} */
304
305/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
306 * @{ */
307#define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */
308#define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */
309#define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */
310/** @} */
311
312/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
313 * @{ */
314#define VIRTIONET_CTRL_ANNOUNCE 3 /**< Control class: Gratuitous Packet Sending */
315#define VIRTIONET_CTRL_ANNOUNCE_ACK 0 /**< Gratuitous Packet Sending ACK */
316/** @} */
317
318struct virtio_net_ctrl_mq {
319 uint16_t uVirtqueuePairs; /**< virtqueue_pairs */
320};
321
322/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
323 * @{ */
324#define VIRTIONET_CTRL_MQ 4 /**< Control class: Receive steering */
325#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET 0 /**< Set number of TX/RX queues */
326#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN 1 /**< Minimum number of TX/RX queues */
327#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 /**< Maximum number of TX/RX queues */
328/** @} */
329
330uint64_t uOffloads; /**< offloads */
331
332/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
333 * @{ */
334#define VIRTIONET_CTRL_GUEST_OFFLOADS 5 /**< Control class: Offloads state configuration */
335#define VIRTIONET_CTRL_GUEST_OFFLOADS_SET 0 /**< Apply new offloads configuration */
336/** @} */
337
338typedef enum VIRTIONETPKTHDRTYPE
339{
340 kVirtioNetUninitializedPktHdrType = 0, /**< Uninitialized (default) packet header type */
341 kVirtioNetModernPktHdrWithoutMrgRx = 1, /**< Packets should not be merged (modern driver) */
342 kVirtioNetModernPktHdrWithMrgRx = 2, /**< Packets should be merged (modern driver) */
343 kVirtioNetLegacyPktHdrWithoutMrgRx = 3, /**< Packets should not be merged (legacy driver) */
344 kVirtioNetLegacyPktHdrWithMrgRx = 4, /**< Packets should be merged (legacy driver) */
345 kVirtioNetFor32BitHack = 0x7fffffff
346} VIRTIONETPKTHDRTYPE;
347
348/**
349 * device-specific queue info
350 */
351struct VIRTIONETWORKER;
352struct VIRTIONETWORKERR3;
353
354typedef struct VIRTIONETVIRTQ
355{
356 uint16_t uIdx; /**< Index of this queue */
357 uint16_t align;
358 bool fCtlVirtq; /**< If set this queue is the control queue */
359 bool fHasWorker; /**< If set this queue has an associated worker */
360 bool fAttachedToVirtioCore; /**< Set if queue attached to virtio core */
361 char szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name */
362} VIRTIONETVIRTQ, *PVIRTIONETVIRTQ;
363
364/**
365 * Worker thread context, shared state.
366 */
367typedef struct VIRTIONETWORKER
368{
369 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
370 uint16_t uIdx; /**< Index of this worker */
371 bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
372 bool volatile fNotified; /**< Flags whether worker thread notified */
373 bool fAssigned; /**< Flags whether worker thread has been set up */
374 uint8_t pad;
375} VIRTIONETWORKER;
376/** Pointer to a virtio net worker. */
377typedef VIRTIONETWORKER *PVIRTIONETWORKER;
378
379/**
380 * Worker thread context, ring-3 state.
381 */
382typedef struct VIRTIONETWORKERR3
383{
384 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
385 uint16_t uIdx; /**< Index of this worker */
386 uint16_t pad;
387} VIRTIONETWORKERR3;
388/** Pointer to a virtio net worker. */
389typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
390
391/**
392 * VirtIO Host NET device state, shared edition.
393 *
394 * @extends VIRTIOCORE
395 */
396typedef struct VIRTIONET
397{
398 /** The core virtio state. */
399 VIRTIOCORE Virtio;
400
401 /** Virtio device-specific configuration */
402 VIRTIONET_CONFIG_T virtioNetConfig;
403
404 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
405 VIRTIONETWORKER aWorkers[VIRTIONET_MAX_VIRTQS];
406
407 /** Track which VirtIO queues we've attached to */
408 VIRTIONETVIRTQ aVirtqs[VIRTIONET_MAX_VIRTQS];
409
410 /** PDM device Instance name */
411 char szInst[16];
412
413 /** VirtIO features negotiated with the guest, including generic core and device specific */
414 uint64_t fNegotiatedFeatures;
415
416 /** Number of Rx/Tx queue pairs (only one if MQ feature not negotiated */
417 uint16_t cVirtqPairs;
418
419 /** Number of Rx/Tx queue pairs that have already been initialized */
420 uint16_t cInitializedVirtqPairs;
421
422 /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
423 uint16_t cVirtqs;
424
425 /** Number of worker threads (one for the control queue and one for each Tx queue) */
426 uint16_t cWorkers;
427
428 /** Alignment */
429 uint16_t alignment;
430
431 /** Indicates transmission in progress -- only one thread is allowed. */
432 uint32_t uIsTransmitting;
433
434 /** Link up delay (in milliseconds). */
435 uint32_t cMsLinkUpDelay;
436
437 /** The number of actually used slots in aMacMulticastFilter. */
438 uint32_t cMulticastFilterMacs;
439
440 /** The number of actually used slots in aMacUniicastFilter. */
441 uint32_t cUnicastFilterMacs;
442
443 /** Semaphore leaf device's thread waits on until guest driver sends empty Rx bufs */
444 SUPSEMEVENT hEventRxDescAvail;
445
446 /** Array of MAC multicast addresses accepted by RX filter. */
447 RTMAC aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
448
449 /** Array of MAC unicast addresses accepted by RX filter. */
450 RTMAC aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
451
452 /** Default MAC address which rx filtering accepts */
453 RTMAC rxFilterMacDefault;
454
455 /** MAC address obtained from the configuration. */
456 RTMAC macConfigured;
457
458 /** Bit array of VLAN filter, one bit per VLAN ID. */
459 uint8_t aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
460
461 /** Set if PDM leaf device at the network interface is starved for Rx buffers */
462 bool volatile fLeafWantsEmptyRxBufs;
463
464 /** Number of packet being sent/received to show in debug log. */
465 uint32_t uPktNo;
466
467 /** Flags whether VirtIO core is in ready state */
468 uint8_t fVirtioReady;
469
470 /** Resetting flag */
471 uint8_t fResetting;
472
473 /** Promiscuous mode -- RX filter accepts all packets. */
474 uint8_t fPromiscuous;
475
476 /** All multicast mode -- RX filter accepts all multicast packets. */
477 uint8_t fAllMulticast;
478
479 /** All unicast mode -- RX filter accepts all unicast packets. */
480 uint8_t fAllUnicast;
481
482 /** No multicast mode - Supresses multicast receive */
483 uint8_t fNoMulticast;
484
485 /** No unicast mode - Suppresses unicast receive */
486 uint8_t fNoUnicast;
487
488 /** No broadcast mode - Supresses broadcast receive */
489 uint8_t fNoBroadcast;
490
491 /** Type of network pkt header based on guest driver version/features */
492 VIRTIONETPKTHDRTYPE ePktHdrType;
493
494 /** Size of network pkt header based on guest driver version/features */
495 uint16_t cbPktHdr;
496
497 /** True if physical cable is attached in configuration. */
498 bool fCableConnected;
499
500 /** True if this device should offer legacy virtio support to the guest */
501 bool fOfferLegacy;
502
503 /** @name Statistic
504 * @{ */
505 STAMCOUNTER StatReceiveBytes;
506 STAMCOUNTER StatTransmitBytes;
507 STAMCOUNTER StatReceiveGSO;
508 STAMCOUNTER StatTransmitPackets;
509 STAMCOUNTER StatTransmitGSO;
510 STAMCOUNTER StatTransmitCSum;
511#ifdef VBOX_WITH_STATISTICS
512 STAMPROFILE StatReceive;
513 STAMPROFILE StatReceiveStore;
514 STAMPROFILEADV StatTransmit;
515 STAMPROFILE StatTransmitSend;
516 STAMPROFILE StatRxOverflow;
517 STAMCOUNTER StatRxOverflowWakeup;
518 STAMCOUNTER StatTransmitByNetwork;
519 STAMCOUNTER StatTransmitByThread;
520 /** @} */
521#endif
522} VIRTIONET;
523/** Pointer to the shared state of the VirtIO Host NET device. */
524typedef VIRTIONET *PVIRTIONET;
525
526/**
527 * VirtIO Host NET device state, ring-3 edition.
528 *
529 * @extends VIRTIOCORER3
530 */
531typedef struct VIRTIONETR3
532{
533 /** The core virtio ring-3 state. */
534 VIRTIOCORER3 Virtio;
535
536 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
537 VIRTIONETWORKERR3 aWorkers[VIRTIONET_MAX_VIRTQS];
538
539 /** The device instance.
540 * @note This is _only_ for use whxen dealing with interface callbacks. */
541 PPDMDEVINSR3 pDevIns;
542
543 /** Status LUN: Base interface. */
544 PDMIBASE IBase;
545
546 /** Status LUN: LED port interface. */
547 PDMILEDPORTS ILeds;
548
549 /** Status LUN: LED connector (peer). */
550 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
551
552 /** Status: LED */
553 PDMLED led;
554
555 /** Attached network driver. */
556 R3PTRTYPE(PPDMIBASE) pDrvBase;
557
558 /** Network port interface (down) */
559 PDMINETWORKDOWN INetworkDown;
560
561 /** Network config port interface (main). */
562 PDMINETWORKCONFIG INetworkConfig;
563
564 /** Connector of attached network driver. */
565 R3PTRTYPE(PPDMINETWORKUP) pDrv;
566
567 /** Link Up(/Restore) Timer. */
568 TMTIMERHANDLE hLinkUpTimer;
569
570} VIRTIONETR3;
571
572/** Pointer to the ring-3 state of the VirtIO Host NET device. */
573typedef VIRTIONETR3 *PVIRTIONETR3;
574
575/**
576 * VirtIO Host NET device state, ring-0 edition.
577 */
578typedef struct VIRTIONETR0
579{
580 /** The core virtio ring-0 state. */
581 VIRTIOCORER0 Virtio;
582} VIRTIONETR0;
583/** Pointer to the ring-0 state of the VirtIO Host NET device. */
584typedef VIRTIONETR0 *PVIRTIONETR0;
585
586/**
587 * VirtIO Host NET device state, raw-mode edition.
588 */
589typedef struct VIRTIONETRC
590{
591 /** The core virtio raw-mode state. */
592 VIRTIOCORERC Virtio;
593} VIRTIONETRC;
594/** Pointer to the ring-0 state of the VirtIO Host NET device. */
595typedef VIRTIONETRC *PVIRTIONETRC;
596
597/** @typedef VIRTIONETCC
598 * The instance data for the current context. */
599typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
600
601/** @typedef PVIRTIONETCC
602 * Pointer to the instance data for the current context. */
603typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
604
605#ifdef IN_RING3
606static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
607static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
608
609/**
610 * Helper function used when logging state of a VM thread.
611 *
612 * @param Thread
613 *
614 * @return Associated name of thread as a pointer to a zero-terminated string.
615 */
616DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
617{
618 if (!pThread)
619 return "<null>";
620
621 switch(pThread->enmState)
622 {
623 case PDMTHREADSTATE_INVALID:
624 return "invalid state";
625 case PDMTHREADSTATE_INITIALIZING:
626 return "initializing";
627 case PDMTHREADSTATE_SUSPENDING:
628 return "suspending";
629 case PDMTHREADSTATE_SUSPENDED:
630 return "suspended";
631 case PDMTHREADSTATE_RESUMING:
632 return "resuming";
633 case PDMTHREADSTATE_RUNNING:
634 return "running";
635 case PDMTHREADSTATE_TERMINATING:
636 return "terminating";
637 case PDMTHREADSTATE_TERMINATED:
638 return "terminated";
639 default:
640 return "unknown state";
641 }
642}
643#endif
644
645/**
646 * Wakeup PDM managed downstream (e.g. hierarchically inferior device's) RX thread
647 */
648static DECLCALLBACK(void) virtioNetWakeupRxBufWaiter(PPDMDEVINS pDevIns)
649{
650 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
651
652 AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
653
654 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
655 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
656 {
657 Log10Func(("[%s] Waking downstream device's Rx buf waiter thread\n", pThis->szInst));
658 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
659 AssertRC(rc);
660 }
661}
662
663/**
664 * Guest notifying us of its activity with a queue. Figure out which queue and respond accordingly.
665 *
666 * @callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
667 */
668static DECLCALLBACK(void) virtioNetVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
669{
670 RT_NOREF(pVirtio);
671 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
672
673 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
674 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
675
676#if defined (IN_RING3) && defined (LOG_ENABLED)
677 RTLogFlush(NULL);
678#endif
679 if (IS_RX_VIRTQ(uVirtqNbr))
680 {
681 uint16_t cBufsAvailable = virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr);
682
683 if (cBufsAvailable)
684 {
685 Log10Func(("%s %u empty bufs added to %s by guest (notifying leaf device)\n",
686 pThis->szInst, cBufsAvailable, pVirtq->szName));
687 virtioNetWakeupRxBufWaiter(pDevIns);
688 }
689 else
690 Log10Func(("%s \n\n***WARNING: %s notified but no empty bufs added by guest! (skip leaf dev. notification)\n\n",
691 pThis->szInst, pVirtq->szName));
692 }
693 else if (IS_TX_VIRTQ(uVirtqNbr) || IS_CTRL_VIRTQ(uVirtqNbr))
694 {
695 /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
696 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
697 {
698 if (ASMAtomicReadBool(&pWorker->fSleeping))
699 {
700 Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
701
702 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
703 AssertRC(rc);
704 }
705 else
706 Log10Func(("[%s] %s has available buffers - worker already awake\n", pThis->szInst, pVirtq->szName));
707 }
708 else
709 Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
710 }
711 else
712 LogRelFunc(("[%s] unrecognized queue %s (idx=%d) notified\n", pThis->szInst, pVirtq->szName, uVirtqNbr));
713}
714
715#ifdef IN_RING3 /* spans most of the file, at the moment. */
716
717/**
718 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
719 */
720static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
721{
722 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
723 PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
724
725 Log10Func(("[%s]\n", pThis->szInst));
726 RT_NOREF(pThis);
727 return PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
728}
729
730/**
731 * Set queue names, distinguishing between modern or legacy mode.
732 *
733 * @note This makes it obvious during logging which mode this transitional device is
734 * operating in, legacy or modern.
735 *
736 * @param pThis Device specific device state
737 * @param fLegacy (input) true if running in legacy mode
738 * false if running in modern mode
739 */
740DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis, uint32_t fLegacy)
741{
742 RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, fLegacy ? "legacy-ctrlq" : " modern-ctrlq");
743 for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
744 {
745 RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-recvq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
746 RTStrPrintf(pThis->aVirtqs[TXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-xmitq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
747 }
748}
749
750/**
751 * Dump a packet to debug log.
752 *
753 * @param pThis The virtio-net shared instance data.
754 * @param pbPacket The packet.
755 * @param cb The size of the packet.
756 * @param pszText A string denoting direction of packet transfer.
757 */
758DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
759{
760#ifdef LOG_ENABLED
761 if (!LogIs12Enabled())
762 return;
763#endif
764 vboxEthPacketDump(pThis->szInst, pszText, pbPacket, (uint32_t)cb);
765}
766
767#ifdef LOG_ENABLED
768void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONETPKTHDR pRxPktHdr,
769 uint16_t cVirtqBufs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS GCPhysRxBuf, uint8_t cbRxBuf)
770{
771 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
772 pRxPktHdr->uNumBuffers = cVirtqBufs;
773 if (pRxPktHdr)
774 {
775 LogFunc(("%*c\nrxPktHdr\n"
776 " uFlags ......... %2.2x\n uGsoType ....... %2.2x\n uHdrLen ........ %4.4x\n"
777 " uGsoSize ....... %4.4x\n uChksumStart ... %4.4x\n uChksumOffset .. %4.4x\n",
778 60, ' ', pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
779 pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset));
780 if (!virtioCoreIsLegacyMode(&pThis->Virtio) || FEATURE_ENABLED(MRG_RXBUF))
781 LogFunc((" uNumBuffers .... %4.4x\n", pRxPktHdr->uNumBuffers));
782 virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONETPKTHDR), 0, "Dump of virtual rPktHdr");
783 }
784 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
785 LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
786 virtioCoreGCPhysHexDump(pDevIns, GCPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
787 LogFunc(("%*c", 60, '-'));
788}
789
790#endif /* LOG_ENABLED */
791
792/**
793 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
794 */
795static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
796{
797 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
798 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
799
800 bool fNone = pszArgs && *pszArgs == '\0';
801 bool fAll = pszArgs && (*pszArgs == 'a' || *pszArgs == 'A'); /* "all" */
802 bool fNetwork = pszArgs && (*pszArgs == 'n' || *pszArgs == 'N'); /* "network" */
803 bool fFeatures = pszArgs && (*pszArgs == 'f' || *pszArgs == 'F'); /* "features" */
804 bool fState = pszArgs && (*pszArgs == 's' || *pszArgs == 'S'); /* "state" */
805 bool fPointers = pszArgs && (*pszArgs == 'p' || *pszArgs == 'P'); /* "pointers" */
806 bool fVirtqs = pszArgs && (*pszArgs == 'q' || *pszArgs == 'Q'); /* "queues */
807
808 /* Show basic information. */
809 pHlp->pfnPrintf(pHlp,
810 "\n"
811 "---------------------------------------------------------------------------\n"
812 "Debug Info: %s\n"
813 " (options: [a]ll, [n]et, [f]eatures, [s]tate, [p]ointers, [q]ueues)\n"
814 "---------------------------------------------------------------------------\n\n",
815 pThis->szInst);
816
817 if (fNone)
818 return;
819
820 /* Show offered/unoffered, accepted/rejected features */
821 if (fAll || fFeatures)
822 {
823 virtioCorePrintDeviceFeatures(&pThis->Virtio, pHlp, s_aDevSpecificFeatures,
824 RT_ELEMENTS(s_aDevSpecificFeatures));
825 pHlp->pfnPrintf(pHlp, "\n");
826 }
827
828 /* Show queues (and associate worker info if applicable) */
829 if (fAll || fVirtqs)
830 {
831 pHlp->pfnPrintf(pHlp, "Virtq information:\n\n");
832 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
833 {
834 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
835
836 if (pVirtq->fHasWorker)
837 {
838 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
839 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
840
841 Assert((pWorker->uIdx == pVirtq->uIdx));
842 Assert((pWorkerR3->uIdx == pVirtq->uIdx));
843
844 if (pWorker->fAssigned)
845 {
846 pHlp->pfnPrintf(pHlp, " %-15s (pThread: %p %s) ",
847 pVirtq->szName,
848 pWorkerR3->pThread,
849 virtioNetThreadStateName(pWorkerR3->pThread));
850 if (pVirtq->fAttachedToVirtioCore)
851 {
852 pHlp->pfnPrintf(pHlp, "worker: ");
853 pHlp->pfnPrintf(pHlp, "%s", pWorker->fSleeping ? "blocking" : "unblocked");
854 pHlp->pfnPrintf(pHlp, "%s", pWorker->fNotified ? ", notified" : "");
855 }
856 else
857 if (pWorker->fNotified)
858 pHlp->pfnPrintf(pHlp, "not attached to virtio core");
859 }
860 }
861 else
862 {
863 pHlp->pfnPrintf(pHlp, " %-15s (INetworkDown's thread) %s", pVirtq->szName,
864 pVirtq->fAttachedToVirtioCore ? "" : "not attached to virtio core");
865 }
866 pHlp->pfnPrintf(pHlp, "\n");
867 virtioCoreR3VirtqInfo(pDevIns, pHlp, pszArgs, uVirtqNbr);
868 pHlp->pfnPrintf(pHlp, " ---------------------------------------------------------------------\n");
869 pHlp->pfnPrintf(pHlp, "\n");
870 }
871 pHlp->pfnPrintf(pHlp, "\n");
872 }
873
874 /* Show various pointers */
875 if (fAll || fPointers)
876 {
877 pHlp->pfnPrintf(pHlp, "Internal Pointers (for instance \"%s\"):\n\n", pThis->szInst);
878 pHlp->pfnPrintf(pHlp, " pDevIns ................... %p\n", pDevIns);
879 pHlp->pfnPrintf(pHlp, " PVIRTIOCORE ............... %p\n", &pThis->Virtio);
880 pHlp->pfnPrintf(pHlp, " PVIRTIONET ................ %p\n", pThis);
881 pHlp->pfnPrintf(pHlp, " PVIRTIONETCC .............. %p\n", pThisCC);
882 pHlp->pfnPrintf(pHlp, " VIRTIONETVIRTQ[] .......... %p\n", pThis->aVirtqs);
883 pHlp->pfnPrintf(pHlp, " pDrvBase .................. %p\n", pThisCC->pDrvBase);
884 pHlp->pfnPrintf(pHlp, " pDrv ...................... %p\n", pThisCC->pDrv);
885 pHlp->pfnPrintf(pHlp, "\n");
886 }
887
888 /* Show device state info */
889 if (fAll || fState)
890 {
891 pHlp->pfnPrintf(pHlp, "Device state:\n\n");
892 uint32_t fTransmitting = ASMAtomicReadU32(&pThis->uIsTransmitting);
893
894 pHlp->pfnPrintf(pHlp, " Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
895 pHlp->pfnPrintf(pHlp, "\n");
896 pHlp->pfnPrintf(pHlp, "Misc state\n");
897 pHlp->pfnPrintf(pHlp, "\n");
898 pHlp->pfnPrintf(pHlp, " fOfferLegacy .............. %d\n", pThis->fOfferLegacy);
899 pHlp->pfnPrintf(pHlp, " fVirtioReady .............. %d\n", pThis->fVirtioReady);
900 pHlp->pfnPrintf(pHlp, " fResetting ................ %d\n", pThis->fResetting);
901 pHlp->pfnPrintf(pHlp, " fGenUpdatePending ......... %d\n", pThis->Virtio.fGenUpdatePending);
902 pHlp->pfnPrintf(pHlp, " fMsiSupport ............... %d\n", pThis->Virtio.fMsiSupport);
903 pHlp->pfnPrintf(pHlp, " uConfigGeneration ......... %d\n", pThis->Virtio.uConfigGeneration);
904 pHlp->pfnPrintf(pHlp, " uDeviceStatus ............. 0x%x\n", pThis->Virtio.fDeviceStatus);
905 pHlp->pfnPrintf(pHlp, " cVirtqPairs .,............. %d\n", pThis->cVirtqPairs);
906 pHlp->pfnPrintf(pHlp, " cVirtqs .,................. %d\n", pThis->cVirtqs);
907 pHlp->pfnPrintf(pHlp, " cWorkers .................. %d\n", pThis->cWorkers);
908 pHlp->pfnPrintf(pHlp, " MMIO mapping name ......... %d\n", pThisCC->Virtio.szMmioName);
909 pHlp->pfnPrintf(pHlp, "\n");
910 }
911
912 /* Show network related information */
913 if (fAll || fNetwork)
914 {
915 pHlp->pfnPrintf(pHlp, "Network configuration:\n\n");
916 pHlp->pfnPrintf(pHlp, " MAC: ...................... %RTmac\n", &pThis->macConfigured);
917 pHlp->pfnPrintf(pHlp, "\n");
918 pHlp->pfnPrintf(pHlp, " Cable: .................... %s\n", pThis->fCableConnected ? "connected" : "disconnected");
919 pHlp->pfnPrintf(pHlp, " Link-up delay: ............ %d ms\n", pThis->cMsLinkUpDelay);
920 pHlp->pfnPrintf(pHlp, "\n");
921 pHlp->pfnPrintf(pHlp, " Accept all multicast: ..... %s\n", pThis->fAllMulticast ? "true" : "false");
922 pHlp->pfnPrintf(pHlp, " Suppress broadcast: ....... %s\n", pThis->fNoBroadcast ? "true" : "false");
923 pHlp->pfnPrintf(pHlp, " Suppress unicast: ......... %s\n", pThis->fNoUnicast ? "true" : "false");
924 pHlp->pfnPrintf(pHlp, " Suppress multicast: ....... %s\n", pThis->fNoMulticast ? "true" : "false");
925 pHlp->pfnPrintf(pHlp, " Promiscuous: .............. %s\n", pThis->fPromiscuous ? "true" : "false");
926 pHlp->pfnPrintf(pHlp, "\n");
927 pHlp->pfnPrintf(pHlp, " Default Rx MAC filter: .... %RTmac\n", pThis->rxFilterMacDefault);
928 pHlp->pfnPrintf(pHlp, "\n");
929
930 pHlp->pfnPrintf(pHlp, " Unicast filter MACs:\n");
931
932 if (!pThis->cUnicastFilterMacs)
933 pHlp->pfnPrintf(pHlp, " <none>\n");
934
935 for (uint32_t i = 0; i < pThis->cUnicastFilterMacs; i++)
936 pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacUnicastFilter[i]);
937
938 pHlp->pfnPrintf(pHlp, "\n Multicast filter MACs:\n");
939
940 if (!pThis->cMulticastFilterMacs)
941 pHlp->pfnPrintf(pHlp, " <none>\n");
942
943 for (uint32_t i = 0; i < pThis->cMulticastFilterMacs; i++)
944 pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacMulticastFilter[i]);
945
946 pHlp->pfnPrintf(pHlp, "\n\n");
947 pHlp->pfnPrintf(pHlp, " Leaf starved: ............. %s\n", pThis->fLeafWantsEmptyRxBufs ? "true" : "false");
948 pHlp->pfnPrintf(pHlp, "\n");
949 }
950 /** @todo implement this
951 * pHlp->pfnPrintf(pHlp, "\n");
952 * virtioCoreR3Info(pDevIns, pHlp, pszArgs);
953 */
954 pHlp->pfnPrintf(pHlp, "\n");
955}
956
957/**
958 * Checks whether certain mutually dependent negotiated features are clustered in required combinations.
959 *
960 * @note See VirtIO 1.0 spec, Section 5.1.3.1
961 *
962 * @param fFeatures Bitmask of negotiated features to evaluate
963 *
964 * @returns true if valid feature combination(s) found.
965 * false if non-valid feature set.
966 */
967DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
968{
969 uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
970 || fFeatures & VIRTIONET_F_GUEST_TSO6
971 || fFeatures & VIRTIONET_F_GUEST_UFO;
972
973 uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
974 || fFeatures & VIRTIONET_F_HOST_TSO6
975 || fFeatures & VIRTIONET_F_HOST_UFO;
976
977 uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
978 || fFeatures & VIRTIONET_F_CTRL_VLAN
979 || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
980 || fFeatures & VIRTIONET_F_MQ
981 || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
982
983 if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
984 return false;
985
986 if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
987 return false;
988
989 if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
990 return false;
991
992 if ( fFeatures & VIRTIONET_F_GUEST_ECN
993 && !( fFeatures & VIRTIONET_F_GUEST_TSO4
994 || fFeatures & VIRTIONET_F_GUEST_TSO6))
995 return false;
996
997 if ( fFeatures & VIRTIONET_F_HOST_ECN
998 && !( fFeatures & VIRTIONET_F_HOST_TSO4
999 || fFeatures & VIRTIONET_F_HOST_TSO6))
1000 return false;
1001 return true;
1002}
1003
1004/**
1005 * Read or write device-specific configuration parameters.
1006 * This is called by VirtIO core code a guest-initiated MMIO access is made to access device-specific
1007 * configuration
1008 *
1009 * @note See VirtIO 1.0 spec, 2.3 Device Configuration Space
1010 *
1011 * @param pThis Pointer to device-specific state
1012 * @param uOffsetOfAccess Offset (within VIRTIONET_CONFIG_T)
1013 * @param pv Pointer to data to read or write
1014 * @param cb Number of bytes to read or write
1015 * @param fWrite True if writing, false if reading
1016 *
1017 * @returns VINF_SUCCESS if successful, or VINF_IOM_MMIO_UNUSED if fails (bad offset or size)
1018 */
1019static int virtioNetR3DevCfgAccess(PVIRTIONET pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
1020{
1021 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
1022
1023 if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess))
1024 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
1025#if FEATURE_OFFERED(STATUS)
1026 else
1027 if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess))
1028 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
1029#endif
1030#if FEATURE_OFFERED(MQ)
1031 else
1032 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess))
1033 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
1034#endif
1035 else
1036 {
1037 LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n",
1038 pThis->szInst, uOffsetOfAccess, uOffsetOfAccess, cb));
1039 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
1040 }
1041 return VINF_SUCCESS;
1042}
1043
1044/**
1045 * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
1046 */
1047static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
1048{
1049 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1050
1051 RT_NOREF(pThis);
1052 return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
1053}
1054
1055/**
1056 * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
1057 */
1058static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1059{
1060 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1061
1062 Log10Func(("[%s] uOffset: %u, cb: %u: %.*Rhxs\n", pThis->szInst, uOffset, cb, cb, pv));
1063 RT_NOREF(pThis);
1064 return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
1065}
1066
1067static int virtioNetR3VirtqDestroy(PVIRTIOCORE pVirtio, PVIRTIONETVIRTQ pVirtq)
1068{
1069 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
1070 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pVirtio->pDevInsR3, PVIRTIONETCC);
1071 PVIRTIONETWORKER pWorker = &pThis->aWorkers[pVirtq->uIdx];
1072 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[pVirtq->uIdx];
1073
1074 int rc = VINF_SUCCESS, rcThread;
1075 Log10Func(("[%s] Destroying \"%s\"", pThis->szInst, pVirtq->szName));
1076 if (pVirtq->fHasWorker)
1077 {
1078 Log10((" and its worker"));
1079 rc = PDMDevHlpSUPSemEventClose(pVirtio->pDevInsR3, pWorker->hEvtProcess);
1080 AssertRCReturn(rc, rc);
1081 pWorker->hEvtProcess = 0;
1082 rc = PDMDevHlpThreadDestroy(pVirtio->pDevInsR3, pWorkerR3->pThread, &rcThread);
1083 AssertRCReturn(rc, rc);
1084 pWorkerR3->pThread = 0;
1085 pVirtq->fHasWorker = false;
1086 }
1087 pWorker->fAssigned = false;
1088 pVirtq->fCtlVirtq = false;
1089 Log10(("\n"));
1090 return rc;
1091}
1092
1093/**
1094 * Takes down the link temporarily if its current status is up.
1095 *
1096 * This is used during restore and when replumbing the network link.
1097 *
1098 * The temporary link outage is supposed to indicate to the OS that all network
1099 * connections have been lost and that it for instance is appropriate to
1100 * renegotiate any DHCP lease.
1101 *
1102 * @param pDevIns The device instance.
1103 * @param pThis The virtio-net shared instance data.
1104 * @param pThisCC The virtio-net ring-3 instance data.
1105 */
1106static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
1107{
1108 if (IS_LINK_UP(pThis))
1109 {
1110 SET_LINK_DOWN(pThis);
1111
1112 /* Re-establish link in 5 seconds. */
1113 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
1114 AssertRC(rc);
1115
1116 LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
1117 }
1118}
1119
1120
1121static void virtioNetConfigurePktHdr(PVIRTIONET pThis, uint32_t fLegacy)
1122{
1123 /* Calculate network packet header type and size based on what we know now */
1124 pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
1125 if (!fLegacy)
1126 /* Modern (e.g. >= VirtIO 1.0) device specification's pkt size rules */
1127 if (FEATURE_ENABLED(MRG_RXBUF))
1128 pThis->ePktHdrType = kVirtioNetModernPktHdrWithMrgRx;
1129 else /* Modern guest driver with MRG_RX feature disabled */
1130 pThis->ePktHdrType = kVirtioNetModernPktHdrWithoutMrgRx;
1131 else
1132 {
1133 /* Legacy (e.g. < VirtIO 1.0) device specification's pkt size rules */
1134 if (FEATURE_ENABLED(MRG_RXBUF))
1135 pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithMrgRx;
1136 else /* Legacy guest with MRG_RX feature disabled */
1137 {
1138 pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
1139 pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
1140 }
1141 }
1142}
1143
1144
1145/*********************************************************************************************************************************
1146* Saved state *
1147*********************************************************************************************************************************/
1148
1149/**
1150 * @callback_method_impl{FNSSMDEVLOADEXEC}
1151 *
1152 * @note: This is included to accept and migrate VMs that had used the original VirtualBox legacy-only virtio-net (network card)
1153 * controller device emulator ("DevVirtioNet.cpp") to work with this superset of VirtIO compatibility known
1154 * as a transitional device (see PDM-invoked device constructor comments for more information)
1155 */
1156static DECLCALLBACK(int) virtioNetR3LegacyDeviceLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass,
1157 RTMAC uMacLoaded)
1158{
1159 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1160 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1161 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1162 int rc;
1163
1164 Log7Func(("[%s] LOAD EXEC (LEGACY)!!\n", pThis->szInst));
1165
1166 if ( memcmp(&uMacLoaded.au8, &pThis->macConfigured.au8, sizeof(uMacLoaded))
1167 && ( uPass == 0
1168 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1169 LogRelFunc(("[%s]: The mac address differs: config=%RTmac saved=%RTmac\n",
1170 pThis->szInst, &pThis->macConfigured, &uMacLoaded));
1171
1172 if (uPass == SSM_PASS_FINAL)
1173 {
1174 /* Call the virtio core to have it load legacy device state */
1175 rc = virtioCoreR3LegacyDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion, VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY);
1176 AssertRCReturn(rc, rc);
1177 /*
1178 * Scan constructor-determined virtqs to determine if they are all valid-as-restored.
1179 * If so, nudge them with a signal, otherwise destroy the unusable queue(s)
1180 * to avoid tripping up the other queue processing logic.
1181 */
1182 int cVirtqsToRemove = 0;
1183 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1184 {
1185 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
1186 if (pVirtq->fHasWorker)
1187 {
1188 if (!virtioCoreR3VirtqIsEnabled(&pThis->Virtio, uVirtqNbr))
1189 {
1190 virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
1191 ++cVirtqsToRemove;
1192 }
1193 else
1194 {
1195 if (virtioCoreR3VirtqIsAttached(&pThis->Virtio, uVirtqNbr))
1196 {
1197 Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
1198 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[pVirtq->uIdx].hEvtProcess);
1199 AssertRCReturn(rc, rc);
1200 }
1201 }
1202 }
1203 }
1204 AssertMsg(cVirtqsToRemove < 2, ("Multiple unusable queues in saved state unexpected\n"));
1205 pThis->cVirtqs -= cVirtqsToRemove;
1206
1207 pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus;
1208 pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
1209
1210 rc = pHlp->pfnSSMGetMem(pSSM, pThis->virtioNetConfig.uMacAddress.au8, sizeof(pThis->virtioNetConfig.uMacAddress));
1211 AssertRCReturn(rc, rc);
1212
1213 if (uVersion > VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY)
1214 {
1215 /* Zero-out the the Unicast/Multicast filter table */
1216 memset(&pThis->aMacUnicastFilter[0], 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1217
1218 rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
1219 AssertRCReturn(rc, rc);
1220 rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
1221 AssertRCReturn(rc, rc);
1222 /*
1223 * The 0.95 legacy virtio spec defines a control queue command VIRTIO_NET_CTRL_MAC_TABLE_SET,
1224 * wherein guest driver configures two variable length mac filter tables: A unicast filter,
1225 * and a multicast filter. However original VBox virtio-net saved both sets of filter entries
1226 * in a single table, abandoning the distinction between unicast and multicast filters. It preserved
1227 * only *one* filter's table length, leaving no way to separate table back out into respective unicast
1228 * and multicast tables this device implementation preserves. Deduced from legacy code, the original
1229 * assumption was that the both MAC filters are whitelists that can be processed identically
1230 * (from the standpoint of a *single* host receiver), such that the distinction between unicast and
1231 * multicast doesn't matter in any one VM's context. Little choice here but to save the undifferentiated
1232 * unicast & multicast MACs to the unicast filter table and leave multicast table empty/unused.
1233 */
1234 uint32_t cCombinedUnicastMulticastEntries;
1235 rc = pHlp->pfnSSMGetU32(pSSM, &cCombinedUnicastMulticastEntries);
1236 AssertRCReturn(rc, rc);
1237 AssertReturn(cCombinedUnicastMulticastEntries <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1238 pThis->cUnicastFilterMacs = cCombinedUnicastMulticastEntries;
1239 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aMacUnicastFilter, cCombinedUnicastMulticastEntries * sizeof(RTMAC));
1240 AssertRCReturn(rc, rc);
1241 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1242 AssertRCReturn(rc, rc);
1243 }
1244 else
1245 {
1246 pThis->fAllMulticast = false;
1247 pThis->cUnicastFilterMacs = 0;
1248 memset(&pThis->aMacUnicastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1249
1250 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
1251
1252 pThis->fPromiscuous = true;
1253 if (pThisCC->pDrv)
1254 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
1255 }
1256
1257 /*
1258 * Log the restored VirtIO feature selection.
1259 */
1260 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
1261 /** @todo shouldn't we update the virtio header size here? it depends on the negotiated features. */
1262 virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
1263
1264 /*
1265 * Configure remaining transitional device parameters presumably or deductively
1266 * as these weren't part of the legacy device code thus it didn't save them to SSM
1267 */
1268 pThis->fCableConnected = 1;
1269 pThis->fAllUnicast = 0;
1270 pThis->fNoMulticast = 0;
1271 pThis->fNoUnicast = 0;
1272 pThis->fNoBroadcast = 0;
1273
1274 /* Zero out the multicast table and count, all MAC filters, if any, are in the unicast filter table */
1275 pThis->cMulticastFilterMacs = 0;
1276 memset(&pThis->aMacMulticastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1277 }
1278 return VINF_SUCCESS;
1279}
1280
1281/**
1282 * @callback_method_impl{FNSSMDEVLOADEXEC}
1283 *
1284 * @note: This loads state saved by a Modern (VirtIO 1.0+) device, of which this transitional device is one,
1285 * and thus supports both legacy and modern guest virtio drivers.
1286 */
1287static DECLCALLBACK(int) virtioNetR3ModernLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1288{
1289 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1290 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1291 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1292 int rc;
1293
1294 RT_NOREF(pThisCC);
1295
1296 RTMAC uMacLoaded, uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
1297 rc = pHlp->pfnSSMGetMem(pSSM, &uMacLoaded.au8, sizeof(uMacLoaded.au8));
1298 AssertRCReturn(rc, rc);
1299 if (memcmp(&uMacLoaded.au8, uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8)))
1300 {
1301 rc = virtioNetR3LegacyDeviceLoadExec(pDevIns, pSSM, uVersion, uPass, uMacLoaded);
1302 return rc;
1303 }
1304
1305 Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
1306
1307 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
1308 AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVEDSTATE_VERSION,
1309 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1310
1311 virtioNetR3SetVirtqNames(pThis, false /* fLegacy */);
1312
1313 pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
1314
1315 pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtqs);
1316 AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2) + 1, VERR_OUT_OF_RANGE);
1317 pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
1318 AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS , VERR_OUT_OF_RANGE);
1319
1320 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1321 pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1322
1323 /* Config checks */
1324 RTMAC macConfigured;
1325 rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
1326 AssertRCReturn(rc, rc);
1327 if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
1328 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1329 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
1330 pThis->szInst, &pThis->macConfigured, &macConfigured));
1331 memcpy(pThis->virtioNetConfig.uMacAddress.au8, macConfigured.au8, sizeof(macConfigured.au8));
1332
1333#if FEATURE_OFFERED(STATUS)
1334 uint16_t fChkStatus;
1335 pHlp->pfnSSMGetU16( pSSM, &fChkStatus);
1336 if (fChkStatus == 0xffff)
1337 {
1338 /* Dummy value in saved state because status feature wasn't enabled at the time */
1339 pThis->virtioNetConfig.uStatus = 0; /* VIRTIO_NET_S_ANNOUNCE disabled */
1340 pThis->virtioNetConfig.uStatus = !!IS_LINK_UP(pThis); /* VIRTIO_NET_IS_LINK_UP (bit 0) */
1341 }
1342 else
1343 pThis->virtioNetConfig.uStatus = fChkStatus;
1344#else
1345 uint16_t fDiscard;
1346 pHlp->pfnSSMGetU16( pSSM, &fDiscard);
1347#endif
1348
1349#if FEATURE_OFFERED(MQ)
1350 uint16_t uCheckMaxVirtqPairs;
1351 pHlp->pfnSSMGetU16( pSSM, &uCheckMaxVirtqPairs);
1352 if (uCheckMaxVirtqPairs)
1353 pThis->virtioNetConfig.uMaxVirtqPairs = uCheckMaxVirtqPairs;
1354 else
1355 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_CTRL_MQ_VQ_PAIRS;
1356#else
1357 uint16_t fDiscard;
1358 pHlp->pfnSSMGetU16( pSSM, &fDiscard);
1359#endif
1360
1361 /* Save device-specific part */
1362 pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
1363 pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
1364 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
1365 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
1366 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
1367 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
1368 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
1369
1370 pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
1371 AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1372 pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1373
1374 if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1375 memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
1376 (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
1377
1378 pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
1379 AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1380 pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1381
1382 if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1383 memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
1384 (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
1385
1386 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1387 AssertRCReturn(rc, rc);
1388 /*
1389 * Call the virtio core to let it load its state.
1390 */
1391 rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion,
1392 VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
1393 AssertRCReturn(rc, rc);
1394 /*
1395 * Since the control queue is created proactively in the constructor to accomodate worst-case
1396 * legacy guests, even though the queue may have been deducted from queue count while saving state,
1397 * we must explicitly remove queue and associated worker thread and context at this point,
1398 * or presence of bogus control queue will confuse operations.
1399 */
1400 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[CTRLQIDX];
1401 if (FEATURE_DISABLED(CTRL_VQ) || !virtioCoreIsVirtqEnabled(&pThis->Virtio, CTRLQIDX))
1402 {
1403 virtioCoreR3VirtqDetach(&pThis->Virtio, CTRLQIDX);
1404 virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
1405 pVirtq->fAttachedToVirtioCore = false;
1406 --pThis->cWorkers;
1407 }
1408 /*
1409 * Nudge queue workers
1410 */
1411 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1412 {
1413 pVirtq = &pThis->aVirtqs[uVirtqNbr];
1414 if (pVirtq->fAttachedToVirtioCore)
1415 {
1416 if (pVirtq->fHasWorker)
1417 {
1418 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
1419 Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
1420 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
1421 AssertRCReturn(rc, rc);
1422 }
1423 }
1424 }
1425 pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus; /* reflects state to guest driver */
1426 pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
1427 virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
1428 return rc;
1429}
1430
1431/**
1432 * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
1433 * loading.}
1434 */
1435static DECLCALLBACK(int) virtioNetR3ModernLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1436{
1437 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1438 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1439 RT_NOREF(pSSM);
1440
1441 if (pThisCC->pDrv)
1442 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulticast));
1443
1444 /*
1445 * Indicate link down to the guest OS that all network connections have
1446 * been lost, unless we've been teleported here.
1447 */
1448 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1449 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
1450
1451 return VINF_SUCCESS;
1452}
1453
1454/**
1455 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1456 */
1457static DECLCALLBACK(int) virtioNetR3ModernSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1458{
1459 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1460 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1461 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1462
1463 RT_NOREF(pThisCC);
1464 Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
1465
1466 /* Store a dummy MAC address that would never be actually assigned to a NIC
1467 * so that when load exec handler is called it can be easily determined
1468 * whether saved state is modern or legacy. This works because original
1469 * legacy code stored assigned NIC address as the first item of SSM state
1470 */
1471 RTMAC uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
1472 pHlp->pfnSSMPutMem(pSSM, &uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8));
1473
1474 pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
1475
1476 pHlp->pfnSSMPutU16( pSSM, pThis->cVirtqs);
1477 pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
1478
1479 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1480 pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1481 /*
1482
1483 * Save device config area (accessed via MMIO)
1484 */
1485 pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
1486 sizeof(pThis->virtioNetConfig.uMacAddress.au8));
1487#if FEATURE_OFFERED(STATUS)
1488 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uStatus);
1489#else
1490 /*
1491 * Relevant values are lower bits. Forcing this to 0xffff let's loadExec know this
1492 * feature was not enabled in saved state. VirtIO 1.0, 5.1.4
1493 */
1494 pHlp->pfnSSMPutU16( pSSM, 0xffff);
1495
1496#endif
1497#if FEATURE_OFFERED(MQ)
1498 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
1499#else
1500 /*
1501 * Legal values for max_virtqueue_pairs are 0x1 -> 0x8000 *. Forcing zero let's loadExec know this
1502 * feature was not enabled in saved state. VirtIO 1.0, 5.1.4.1
1503 */
1504 pHlp->pfnSSMPutU16( pSSM, 0);
1505#endif
1506
1507 /* Save device-specific part */
1508 pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
1509 pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
1510 pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
1511 pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
1512 pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
1513 pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
1514 pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
1515
1516 pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
1517 pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1518
1519 pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
1520 pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1521
1522 int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1523 AssertRCReturn(rc, rc);
1524
1525 /*
1526 * Call the virtio core to let it save its state.
1527 */
1528 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
1529}
1530
1531
1532/*********************************************************************************************************************************
1533* Device interface. *
1534*********************************************************************************************************************************/
1535
1536#ifdef IN_RING3
1537
1538/**
1539 * Perform 16-bit 1's compliment checksum on provided packet in accordance with VirtIO specification,
1540 * pertinent to VIRTIO_NET_F_CSUM feature, which 'offloads' the Checksum feature from the driver
1541 * to save processor cycles, which is ironic in our case, where the controller device ('network card')
1542 * is emulated on the virtualization host.
1543 *
1544 * @note See VirtIO 1.0 spec, 5.1.6.2 Packet Transmission
1545 *
1546 * @param pBuf Pointer to r/w buffer with any portion to calculate checksum for
1547 * @param cbSize Number of bytes to checksum
1548 * @param uStart Where to start the checksum within the buffer
1549 * @param uOffset Offset past uStart point in the buffer to store checksum result
1550 *
1551 */
1552DECLINLINE(void) virtioNetR3Calc16BitChecksum(uint8_t *pBuf, size_t cb, uint16_t uStart, uint16_t uOffset)
1553{
1554 AssertReturnVoid(uStart < cb);
1555 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cb);
1556
1557 uint32_t chksum = 0;
1558 uint16_t *pu = (uint16_t *)(pBuf + uStart);
1559
1560 cb -= uStart;
1561 while (cb > 1)
1562 {
1563 chksum += *pu++;
1564 cb -= 2;
1565 }
1566 if (cb)
1567 chksum += *(uint8_t *)pu;
1568 while (chksum >> 16)
1569 chksum = (chksum >> 16) + (chksum & 0xFFFF);
1570
1571 /* Store 1's compliment of calculated sum */
1572 *(uint16_t *)(pBuf + uStart + uOffset) = ~chksum;
1573}
1574
1575/**
1576 * Turns on/off the read status LED.
1577 *
1578 * @returns VBox status code.
1579 * @param pThis Pointer to the device state structure.
1580 * @param fOn New LED state.
1581 */
1582static void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
1583{
1584 if (fOn)
1585 pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
1586 else
1587 pThisR3->led.Actual.s.fReading = fOn;
1588}
1589
1590/**
1591 * Turns on/off the write status LED.
1592 *
1593 * @returns VBox status code.
1594 * @param pThis Pointer to the device state structure.
1595 * @param fOn New LED state.
1596 */
1597static void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
1598{
1599 if (fOn)
1600 pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
1601 else
1602 pThisR3->led.Actual.s.fWriting = fOn;
1603}
1604
1605/**
1606 * Check that the core is setup and ready and co-configured with guest virtio driver,
1607 * and verifies that the VM is running.
1608 *
1609 * @returns true if VirtIO core and device are in a running and operational state
1610 */
1611DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
1612{
1613 if (RT_LIKELY(pThis->fVirtioReady))
1614 {
1615 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1616 if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
1617 return true;
1618 }
1619 return false;
1620}
1621
1622/**
1623 * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
1624 * available. This must be called before the pfnRecieve() method is called.
1625 *
1626 * @remarks As a side effect this function enables queue notification
1627 * if it cannot receive because the queue is empty.
1628 * It disables notification if it can receive.
1629 *
1630 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1631 * @thread RX
1632 */
1633static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
1634{
1635 int rc = VERR_INVALID_STATE;
1636 Log8Func(("[%s] ", pThis->szInst));
1637 if (!virtioNetIsOperational(pThis, pDevIns))
1638 Log8(("No Rx bufs available. (VirtIO core not ready)\n"));
1639
1640 else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
1641 Log8(("[No Rx bufs available. (%s not enabled)\n", pRxVirtq->szName));
1642
1643 else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->uIdx))
1644 Log8(("No Rx bufs available. (%s empty)\n", pRxVirtq->szName));
1645
1646 else
1647 {
1648 Log8(("%s has %d empty guest bufs in avail ring\n", pRxVirtq->szName,
1649 virtioCoreVirtqAvailBufCount(pDevIns, &pThis->Virtio, pRxVirtq->uIdx)));
1650 rc = VINF_SUCCESS;
1651 }
1652 virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
1653 return rc;
1654}
1655
1656/**
1657 * Find an Rx queue that has Rx packets in it, if *any* do.
1658 *
1659 * @todo When multiqueue (MQ) mode is fully supported and tested, some kind of round-robin
1660 * or randomization scheme should probably be incorporated here.
1661 *
1662 * @returns true if Rx pkts avail on queue and sets pRxVirtq to point to queue w/pkts found
1663 * @thread RX
1664 *
1665 */
1666static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
1667{
1668 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
1669 {
1670 PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
1671 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
1672 {
1673 if (pRxVirtq)
1674 *pRxVirtq = pThisRxVirtq;
1675 return true;
1676 }
1677 }
1678 return false;
1679}
1680
1681/**
1682 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
1683 */
1684static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
1685{
1686 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1687 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1688 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1689
1690 if (!virtioNetIsOperational(pThis, pDevIns))
1691 return VERR_INTERRUPTED;
1692
1693 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1694 {
1695 Log10Func(("[%s] Rx bufs available, releasing waiter...\n", pThis->szInst));
1696 return VINF_SUCCESS;
1697 }
1698 if (!timeoutMs)
1699 return VERR_NET_NO_BUFFER_SPACE;
1700
1701 LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
1702
1703 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
1704 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
1705
1706 do {
1707 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1708 {
1709 Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
1710 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1711 return VINF_SUCCESS;
1712 }
1713 Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
1714
1715 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
1716
1717 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
1718 {
1719 LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
1720
1721 if (!virtioNetIsOperational(pThis, pDevIns))
1722 break;
1723
1724 continue;
1725 }
1726 if (RT_FAILURE(rc)) {
1727 LogFunc(("Waken due to failure %Rrc\n", rc));
1728 RTThreadSleep(1);
1729 }
1730 } while (virtioNetIsOperational(pThis, pDevIns));
1731
1732 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
1733 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1734
1735 Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
1736 return VERR_INTERRUPTED;
1737}
1738
1739/**
1740 * Sets up the GSO context according to the Virtio header.
1741 *
1742 * @param pGso The GSO context to setup.
1743 * @param pCtx The context descriptor.
1744 */
1745DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
1746{
1747 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1748
1749 if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
1750 {
1751 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
1752 return NULL;
1753 }
1754 switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
1755 {
1756 case VIRTIONET_HDR_GSO_TCPV4:
1757 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
1758 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1759 break;
1760 case VIRTIONET_HDR_GSO_TCPV6:
1761 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
1762 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1763 break;
1764 case VIRTIONET_HDR_GSO_UDP:
1765 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
1766 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
1767 break;
1768 default:
1769 return NULL;
1770 }
1771 if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
1772 pGso->offHdr2 = pPktHdr->uChksumStart;
1773 else
1774 {
1775 AssertMsgFailed(("GSO without checksum offloading!\n"));
1776 return NULL;
1777 }
1778 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1779 pGso->cbHdrsTotal = pPktHdr->uHdrLen;
1780 pGso->cbMaxSeg = pPktHdr->uGsoSize;
1781 /* Mark GSO frames with zero MSS as PDMNETWORKGSOTYPE_INVALID, so they will be ignored by send. */
1782 if (pPktHdr->uGsoType != VIRTIONET_HDR_GSO_NONE && pPktHdr->uGsoSize == 0)
1783 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1784 return pGso;
1785}
1786
1787/**
1788 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
1789 */
1790static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1791{
1792 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
1793 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
1794 memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
1795 return VINF_SUCCESS;
1796}
1797
1798/**
1799 * Returns true if it is a broadcast packet.
1800 *
1801 * @returns true if destination address indicates broadcast.
1802 * @param pvBuf The ethernet packet.
1803 */
1804DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
1805{
1806 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1807 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
1808}
1809
1810/**
1811 * Returns true if it is a multicast packet.
1812 *
1813 * @remarks returns true for broadcast packets as well.
1814 * @returns true if destination address indicates multicast.
1815 * @param pvBuf The ethernet packet.
1816 */
1817DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
1818{
1819 return (*(char*)pvBuf) & 1;
1820}
1821
1822/**
1823 * Determines if the packet is to be delivered to upper layer.
1824 *
1825 * @returns true if packet is intended for this node.
1826 * @param pThis Pointer to the state structure.
1827 * @param pvBuf The ethernet packet.
1828 * @param cb Number of bytes available in the packet.
1829 */
1830static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
1831{
1832
1833RT_NOREF(cb);
1834
1835#ifdef LOG_ENABLED
1836 if (LogIs11Enabled())
1837 {
1838 char *pszType;
1839 if (virtioNetR3IsMulticast(pvBuf))
1840 pszType = (char *)"mcast";
1841 else if (virtioNetR3IsBroadcast(pvBuf))
1842 pszType = (char *)"bcast";
1843 else
1844 pszType = (char *)"ucast";
1845
1846 LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
1847 pThis->virtioNetConfig.uMacAddress.au8,
1848 pThis->fPromiscuous ? " promisc" : "",
1849 pThis->fAllMulticast ? " all-mcast" : "",
1850 pvBuf, pszType));
1851 }
1852#endif
1853
1854 if (pThis->fPromiscuous) {
1855 Log11(("\n"));
1856 return true;
1857 }
1858
1859 /* Ignore everything outside of our VLANs */
1860 uint16_t *uPtr = (uint16_t *)pvBuf;
1861
1862 /* Compare TPID with VLAN Ether Type */
1863 if ( uPtr[6] == RT_H2BE_U16(0x8100)
1864 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
1865 {
1866 Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
1867 return false;
1868 }
1869
1870 if (virtioNetR3IsBroadcast(pvBuf))
1871 {
1872 Log11(("acpt (bcast)\n"));
1873#ifdef LOG_ENABLED
1874 if (LogIs12Enabled())
1875 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1876#endif
1877 return true;
1878 }
1879 if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
1880 {
1881 Log11(("acpt (all-mcast)\n"));
1882#ifdef LOG_ENABLED
1883 if (LogIs12Enabled())
1884 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1885#endif
1886 return true;
1887 }
1888
1889 if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
1890 {
1891 Log11(("acpt (to-node)\n"));
1892#ifdef LOG_ENABLED
1893 if (LogIs12Enabled())
1894 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1895#endif
1896 return true;
1897 }
1898
1899 for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
1900 {
1901 if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
1902 {
1903 Log11(("acpt (mcast whitelist)\n"));
1904#ifdef LOG_ENABLED
1905 if (LogIs12Enabled())
1906 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1907#endif
1908 return true;
1909 }
1910 }
1911
1912 for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
1913 if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
1914 {
1915 Log11(("acpt (ucast whitelist)\n"));
1916 return true;
1917 }
1918#ifdef LOG_ENABLED
1919 if (LogIs11Enabled())
1920 Log(("... reject\n"));
1921#endif
1922
1923 return false;
1924}
1925
1926
1927/**
1928 * This handles the case where Rx packet must be transfered to guest driver via multiple buffers using
1929 * copy tactics slower than preferred method using a single virtq buf. Yet this is an available option
1930 * for guests. Although cited in the spec it's to accomodate guest that perhaps have memory constraints
1931 * wherein guest may benefit from smaller buffers (see MRG_RXBUF feature), in practice it is seen
1932 * that without MRG_RXBUF the linux guest enqueues 'huge' multi-segment buffers so that the largest
1933 * conceivable Rx packet can be contained in a single buffer, where for most transactions most of that
1934 * memory will be unfilled, so it is typically both wasteful and *slower* to avoid MRG_RXBUF.
1935 *
1936 * As an optimization, this multi-buffer copy is only used when:
1937 *
1938 * A. Guest has negotiated MRG_RXBUF
1939 * B. Next packet in the Rx avail queue isn't big enough to contain Rx pkt hdr+data.
1940 *
1941 * Architecture is defined in VirtIO 1.1 5.1.6 (Device Operations), which has improved
1942 * wording over the VirtIO 1.0 specification, but, as an implementation note, there is one
1943 * ambiguity that needs clarification:
1944 *
1945 * The VirtIO 1.1, 5.1.6.4 explains something in a potentially misleading way. And note,
1946 * the VirtIO spec makes a document-wide assertion that the distinction between
1947 * "SHOULD" and "MUST" is to be taken quite literally.
1948 *
1949 * The confusion is that VirtIO 1.1, 5.1.6.3.1 essentially says guest driver "SHOULD" populate
1950 * Rx queue with buffers large enough to accomodate full pkt hdr + data. That's a grammatical
1951 * error (dangling participle).
1952 *
1953 * In practice we MUST assume "SHOULD" strictly applies to the word *populate*, -not- to buffer
1954 * size, because ultimately buffer minimum size is predicated on configuration parameters,
1955 * specifically, when MRG_RXBUF feature is disabled, the driver *MUST* provide Rx bufs
1956 * (if and when it can provide them), that are *large enough* to hold pkt hdr + payload.
1957 *
1958 * Therefore, proper interpretation of 5.1.6.3.1 is, the guest *should* (ideally) keep Rx virtq
1959 * populated with appropriately sized buffers to *prevent starvation* (i.e. starvation may be
1960 * unavoidable thus can't be prohibited). As it would be a ludicrous to presume 5.1.6.3.1 is
1961 * giving guests leeway to violate MRG_RXBUF feature buf size constraints.
1962 *
1963 * @param pDevIns PDM instance
1964 * @param pThis Device instance
1965 * @param pvBuf Pointer to incoming GSO Rx data from downstream device
1966 * @param cb Amount of data given
1967 * @param rxPktHdr Rx pkt Header that's been massaged into VirtIO semantics
1968 * @param pRxVirtq Pointer to Rx virtq
1969 * @param pVirtqBuf Initial virtq buffer to start copying Rx hdr/pkt to guest into
1970 *
1971 */
1972static int virtioNetR3RxPktMultibufXfer(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint8_t *pvPktBuf, size_t cb,
1973 PVIRTIONETPKTHDR pRxPktHdr, PVIRTIONETVIRTQ pRxVirtq, PVIRTQBUF pVirtqBuf)
1974{
1975
1976 size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
1977 size_t cbPktHdr = pThis->cbPktHdr;
1978
1979 AssertMsgReturn(cbBufRemaining >= pThis->cbPktHdr,
1980 ("guest-provided Rx buf not large enough to store pkt hdr"), VERR_INTERNAL_ERROR);
1981
1982 Log7Func((" Sending packet header to guest...\n"));
1983
1984 /* Copy packet header to rx buf provided by caller. */
1985 size_t cbHdrEnqueued = pVirtqBuf->cbPhysReturn == cbPktHdr ? cbPktHdr : 0;
1986 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, cbHdrEnqueued);
1987
1988 /* Cache address of uNumBuffers field of pkthdr to update ex post facto */
1989 RTGCPHYS GCPhysNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
1990 uint16_t cVirtqBufsUsed = 0;
1991 cbBufRemaining -= cbPktHdr;
1992 /*
1993 * Copy packet to guest using as many buffers as necessary, tracking and handling whether
1994 * the buf containing the packet header was already written to the Rx queue's used buffer ring.
1995 */
1996 uint64_t uPktOffset = 0;
1997 while(uPktOffset < cb)
1998 {
1999 Log7Func((" Sending packet data (in buffer #%d) to guest...\n", cVirtqBufsUsed));
2000 size_t cbBounded = RT_MIN(cbBufRemaining, cb - uPktOffset);
2001 (void) virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbBounded,
2002 pvPktBuf + uPktOffset, pVirtqBuf, cbBounded + (cbPktHdr - cbHdrEnqueued) /* cbEnqueue */);
2003 ++cVirtqBufsUsed;
2004 cbBufRemaining -= cbBounded;
2005 uPktOffset += cbBounded;
2006 if (uPktOffset < cb)
2007 {
2008 cbHdrEnqueued = cbPktHdr;
2009#ifdef VIRTIO_VBUF_ON_STACK
2010 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
2011#else /* !VIRTIO_VBUF_ON_STACK */
2012 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
2013 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
2014#endif /* !VIRTIO_VBUF_ON_STACK */
2015
2016 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
2017
2018#ifdef VIRTIO_VBUF_ON_STACK
2019 AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
2020 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
2021 VERR_INTERNAL_ERROR);
2022#else /* !VIRTIO_VBUF_ON_STACK */
2023 AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
2024 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
2025 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
2026 VERR_INTERNAL_ERROR);
2027#endif /* !VIRTIO_VBUF_ON_STACK */
2028 cbBufRemaining = pVirtqBuf->cbPhysReturn;
2029 }
2030 }
2031
2032 /* Fix-up pkthdr (in guest phys. memory) with number of buffers (descriptors) that were processed */
2033 int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysNumBuffers, &cVirtqBufsUsed, sizeof(cVirtqBufsUsed));
2034 AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
2035
2036#ifndef VIRTIO_VBUF_ON_STACK
2037 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
2038#endif /* !VIRTIO_VBUF_ON_STACK */
2039 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
2040 Log7(("\n"));
2041 return rc;
2042}
2043
2044/**
2045 * Pad and store received packet.
2046 *
2047 * @remarks Make sure that the packet appears to upper layer as one coming
2048 * from real Ethernet: pad it and insert FCS.
2049 *
2050 * @returns VBox status code.
2051 * @param pDevIns The device instance.
2052 * @param pThis The virtio-net shared instance data.
2053 * @param pvBuf The available data.
2054 * @param cb Number of bytes available in the buffer.
2055 * @param pGso Pointer to Global Segmentation Offload structure
2056 * @param pRxVirtq Pointer to Rx virtqueue
2057 * @thread RX
2058 */
2059
2060static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, const void *pvBuf, size_t cb,
2061 PVIRTIONETPKTHDR pRxPktHdr, uint8_t cbPktHdr, PVIRTIONETVIRTQ pRxVirtq)
2062{
2063 RT_NOREF(pThisCC);
2064#ifdef VIRTIO_VBUF_ON_STACK
2065 VIRTQBUF_T VirtqBuf;
2066
2067 VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
2068 VirtqBuf.cRefs = 1;
2069
2070 PVIRTQBUF pVirtqBuf = &VirtqBuf;
2071 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
2072#else /* !VIRTIO_VBUF_ON_STACK */
2073 PVIRTQBUF pVirtqBuf;
2074 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
2075#endif /* !VIRTIO_VBUF_ON_STACK */
2076
2077 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
2078
2079#ifdef VIRTIO_VBUF_ON_STACK
2080 AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
2081 ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
2082 VERR_INTERNAL_ERROR);
2083#else /* !VIRTIO_VBUF_ON_STACK */
2084 AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
2085 ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
2086 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
2087 VERR_INTERNAL_ERROR);
2088#endif /* !VIRTIO_VBUF_ON_STACK */
2089 /*
2090 * Try to do fast (e.g. single-buffer) copy to guest, even if MRG_RXBUF feature is enabled
2091 */
2092 STAM_PROFILE_START(&pThis->StatReceiveStore, a);
2093 if (RT_LIKELY(FEATURE_DISABLED(MRG_RXBUF))
2094 || RT_LIKELY(pVirtqBuf->cbPhysReturn > cb + cbPktHdr))
2095 {
2096 Log7Func(("Send Rx packet header and data to guest (single-buffer copy)...\n"));
2097 pRxPktHdr->uNumBuffers = 1;
2098 rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, 0 /* cbEnqueue */);
2099 if (rc == VINF_SUCCESS)
2100 rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cb, pvBuf, pVirtqBuf, cbPktHdr + cb /* cbEnqueue */);
2101#ifndef VIRTIO_VBUF_ON_STACK
2102 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
2103#endif /* !VIRTIO_VBUF_ON_STACK */
2104 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
2105 AssertMsgReturn(rc == VINF_SUCCESS, ("%Rrc\n", rc), rc);
2106 }
2107 else
2108 {
2109 Log7Func(("Send Rx pkt to guest (merged-buffer copy [MRG_RXBUF feature])...\n"));
2110 rc = virtioNetR3RxPktMultibufXfer(pDevIns, pThis, (uint8_t *)pvBuf, cb, pRxPktHdr, pRxVirtq, pVirtqBuf);
2111 return rc;
2112 }
2113 STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
2114 return VINF_SUCCESS;
2115}
2116
2117/**
2118 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
2119 */
2120static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb,
2121 PCPDMNETWORKGSO pGso)
2122{
2123 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2124 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2125 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2126 VIRTIONETPKTHDR rxPktHdr = { 0, VIRTIONET_HDR_GSO_NONE, 0, 0, 0, 0, 0 };
2127
2128 if (!pThis->fVirtioReady)
2129 {
2130 LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
2131 return VERR_INTERRUPTED;
2132 }
2133 /*
2134 * If GSO (Global Segment Offloading) was received from downstream PDM network device, massage the
2135 * PDM-provided GSO parameters into VirtIO semantics, which get passed to guest virtio-net via
2136 * Rx pkt header. See VirtIO 1.1, 5.1.6 Device Operation for more information.
2137 */
2138 if (pGso)
2139 {
2140 LogFunc(("[%s] (%RTmac) \n", pThis->szInst, pvBuf));
2141
2142 rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
2143 rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
2144 rxPktHdr.uGsoSize = pGso->cbMaxSeg;
2145 rxPktHdr.uChksumStart = pGso->offHdr2;
2146
2147 switch (pGso->u8Type)
2148 {
2149 case PDMNETWORKGSOTYPE_IPV4_TCP:
2150 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
2151 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
2152 break;
2153 case PDMNETWORKGSOTYPE_IPV6_TCP:
2154 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
2155 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
2156 break;
2157 case PDMNETWORKGSOTYPE_IPV4_UDP:
2158 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
2159 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
2160 break;
2161 default:
2162 LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
2163 return VERR_NOT_SUPPORTED;
2164 }
2165 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
2166 Log2Func(("[%s] gso type=%#x, cbHdrsTotal=%u cbHdrsSeg=%u mss=%u offHdr1=%#x offHdr2=%#x\n",
2167 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2168 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2169 }
2170
2171 /*
2172 * Find a virtq with Rx bufs on avail ring, if any, and copy the packet to the guest's Rx buffer.
2173 * @todo pk: PROBABLY NOT A SOPHISTICATED ENOUGH QUEUE SELECTION ALGORTITH FOR OPTIMAL MQ (FEATURE) SUPPORT
2174 */
2175 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
2176 {
2177 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
2178 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
2179 {
2180 int rc = VINF_SUCCESS;
2181 STAM_PROFILE_START(&pThis->StatReceive, a);
2182 virtioNetR3SetReadLed(pThisCC, true);
2183 if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
2184 {
2185 /* rxPktHdr is local stack variable that should not go out of scope in this use */
2186 rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pThisCC, pvBuf, cb, &rxPktHdr, pThis->cbPktHdr, pRxVirtq);
2187 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
2188 }
2189 virtioNetR3SetReadLed(pThisCC, false);
2190 STAM_PROFILE_STOP(&pThis->StatReceive, a);
2191 return rc;
2192 }
2193 }
2194 return VERR_INTERRUPTED;
2195}
2196
2197/**
2198 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
2199 */
2200static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
2201{
2202
2203#ifdef LOG_ENABLED
2204 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2205 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2206 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2207 LogFunc(("[%s] (%RTmac)\n", pThis->szInst, pvBuf));
2208#endif
2209
2210 return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
2211}
2212
2213/*
2214 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's Rx packet receive filtering.
2215 * See VirtIO 1.0, 5.1.6.5.1
2216 *
2217 * @param pThis virtio-net instance
2218 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2219 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2220 */
2221static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2222 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2223{
2224
2225#define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
2226
2227 LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
2228 switch(pCtrlPktHdr->uCmd)
2229 {
2230 case VIRTIONET_CTRL_RX_PROMISC:
2231 break;
2232 case VIRTIONET_CTRL_RX_ALLMULTI:
2233 break;
2234 case VIRTIONET_CTRL_RX_ALLUNI:
2235 /* fallthrough */
2236 case VIRTIONET_CTRL_RX_NOMULTI:
2237 /* fallthrough */
2238 case VIRTIONET_CTRL_RX_NOUNI:
2239 /* fallthrough */
2240 case VIRTIONET_CTRL_RX_NOBCAST:
2241 AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
2242 ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
2243 VIRTIONET_ERROR);
2244 /* fall out */
2245 }
2246
2247 uint8_t fOn, fPromiscChanged = false;
2248 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
2249
2250 switch(pCtrlPktHdr->uCmd)
2251 {
2252 case VIRTIONET_CTRL_RX_PROMISC:
2253 pThis->fPromiscuous = RT_BOOL(fOn);
2254 fPromiscChanged = true;
2255 LOG_VIRTIONET_FLAG(fPromiscuous);
2256 break;
2257 case VIRTIONET_CTRL_RX_ALLMULTI:
2258 pThis->fAllMulticast = RT_BOOL(fOn);
2259 fPromiscChanged = true;
2260 LOG_VIRTIONET_FLAG(fAllMulticast);
2261 break;
2262 case VIRTIONET_CTRL_RX_ALLUNI:
2263 pThis->fAllUnicast = RT_BOOL(fOn);
2264 LOG_VIRTIONET_FLAG(fAllUnicast);
2265 break;
2266 case VIRTIONET_CTRL_RX_NOMULTI:
2267 pThis->fNoMulticast = RT_BOOL(fOn);
2268 LOG_VIRTIONET_FLAG(fNoMulticast);
2269 break;
2270 case VIRTIONET_CTRL_RX_NOUNI:
2271 pThis->fNoUnicast = RT_BOOL(fOn);
2272 LOG_VIRTIONET_FLAG(fNoUnicast);
2273 break;
2274 case VIRTIONET_CTRL_RX_NOBCAST:
2275 pThis->fNoBroadcast = RT_BOOL(fOn);
2276 LOG_VIRTIONET_FLAG(fNoBroadcast);
2277 break;
2278 }
2279
2280 if (pThisCC->pDrv && fPromiscChanged)
2281 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
2282
2283 return VIRTIONET_OK;
2284}
2285
2286/*
2287 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MAC filter tables
2288 * See VirtIO 1.0, 5.1.6.5.2
2289 *
2290 * @param pThis virtio-net instance
2291 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2292 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2293 */
2294static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2295{
2296 LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
2297
2298
2299 AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
2300 ("insufficient descriptor space for ctrl pkt hdr"),
2301 VIRTIONET_ERROR);
2302
2303 size_t cbRemaining = pVirtqBuf->cbPhysSend;
2304 switch(pCtrlPktHdr->uCmd)
2305 {
2306 case VIRTIONET_CTRL_MAC_ADDR_SET:
2307 {
2308 /* Set default Rx filter MAC */
2309 AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
2310 ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
2311
2312 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(RTMAC));
2313 break;
2314 }
2315 case VIRTIONET_CTRL_MAC_TABLE_SET:
2316 {
2317 VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
2318
2319 /* Load unicast MAC filter table */
2320 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
2321 ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2322
2323 /* Fetch count of unicast filter MACs from guest buffer */
2324 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
2325 cbRemaining -= sizeof(cMacs);
2326
2327 Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
2328
2329 if (cMacs)
2330 {
2331 uint32_t cbMacs = cMacs * sizeof(RTMAC);
2332
2333 AssertMsgReturn(cbMacs <= sizeof(pThis->aMacUnicastFilter) / sizeof(RTMAC),
2334 ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
2335
2336 AssertMsgReturn(cbRemaining >= cbMacs,
2337 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2338
2339
2340 /* Fetch unicast table contents from guest buffer */
2341 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
2342 cbRemaining -= cbMacs;
2343 }
2344 pThis->cUnicastFilterMacs = cMacs;
2345
2346 /* Load multicast MAC filter table */
2347 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
2348 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2349
2350 /* Fetch count of multicast filter MACs from guest buffer */
2351 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
2352 cbRemaining -= sizeof(cMacs);
2353
2354 Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
2355
2356 if (cMacs)
2357 {
2358 uint32_t cbMacs = cMacs * sizeof(RTMAC);
2359
2360 AssertMsgReturn(cbMacs <= sizeof(pThis->aMacMulticastFilter) / sizeof(RTMAC),
2361 ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
2362
2363 AssertMsgReturn(cbRemaining >= cbMacs,
2364 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2365
2366 /* Fetch multicast table contents from guest buffer */
2367 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
2368 cbRemaining -= cbMacs;
2369 }
2370 pThis->cMulticastFilterMacs = cMacs;
2371
2372#ifdef LOG_ENABLED
2373 LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
2374 for(unsigned i = 0; i < pThis->cUnicastFilterMacs; i++)
2375 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
2376
2377 LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
2378 for(unsigned i = 0; i < pThis->cMulticastFilterMacs; i++)
2379 LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
2380#endif
2381 break;
2382 }
2383 default:
2384 LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
2385 return VIRTIONET_ERROR;
2386 }
2387 return VIRTIONET_OK;
2388}
2389
2390/*
2391 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MQ (multiqueue) operations.
2392 * See VirtIO 1.0, 5.1.6.5.5
2393 *
2394 * @param pThis virtio-net instance
2395 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2396 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2397 */
2398static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2399{
2400 LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
2401
2402 uint16_t cVirtqPairs;
2403 switch(pCtrlPktHdr->uCmd)
2404 {
2405 case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
2406 {
2407 size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
2408
2409 AssertMsgReturn(cbRemaining > sizeof(cVirtqPairs),
2410 ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
2411
2412 /* Fetch number of virtq pairs from guest buffer */
2413 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
2414
2415 AssertMsgReturn(cVirtqPairs > VIRTIONET_MAX_QPAIRS,
2416 ("[%s] Guest CTRL MQ virtq pair count out of range [%d])\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
2417
2418 LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
2419 pThis->cVirtqPairs = cVirtqPairs;
2420 break;
2421 }
2422 default:
2423 LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
2424 return VIRTIONET_ERROR;
2425 }
2426
2427 /*
2428 * The MQ control function is invoked by the guest in an RPC like manner to change
2429 * the Rx/Tx queue pair count. If the new value exceeds the number of queues
2430 * (and associated workers) already initialized initialize only the new queues and
2431 * respective workers.
2432 */
2433 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
2434 {
2435 virtioNetR3SetVirtqNames(pThis, virtioCoreIsLegacyMode(&pThis->Virtio));
2436 int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
2437 if (RT_FAILURE(rc))
2438 {
2439 LogRelFunc(("Failed to create worker threads\n"));
2440 return VIRTIONET_ERROR;
2441 }
2442 }
2443 return VIRTIONET_OK;
2444}
2445
2446/*
2447 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's VLAN filtering.
2448 * See VirtIO 1.0, 5.1.6.5.3
2449 *
2450 * @param pThis virtio-net instance
2451 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2452 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2453 */
2454static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2455{
2456 LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
2457
2458 uint16_t uVlanId;
2459 size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
2460
2461 AssertMsgReturn(cbRemaining > sizeof(uVlanId),
2462 ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
2463
2464 /* Fetch VLAN ID from guest buffer */
2465 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
2466
2467 AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
2468 ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
2469
2470 LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
2471
2472 switch (pCtrlPktHdr->uCmd)
2473 {
2474 case VIRTIONET_CTRL_VLAN_ADD:
2475 ASMBitSet(pThis->aVlanFilter, uVlanId);
2476 break;
2477 case VIRTIONET_CTRL_VLAN_DEL:
2478 ASMBitClear(pThis->aVlanFilter, uVlanId);
2479 break;
2480 default:
2481 LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
2482 return VIRTIONET_ERROR;
2483 }
2484 return VIRTIONET_OK;
2485}
2486
2487/**
2488 * Processes control command from guest.
2489 * See VirtIO 1.0 spec, 5.1.6 "Device Operation" and 5.1.6.5 "Control Virtqueue".
2490 *
2491 * The control command is contained in a virtio buffer pulled from the virtio-net defined control queue (ctrlq).
2492 * Command type is parsed is dispatched to a command-specific device-configuration handler function (e.g. RX, MAC, VLAN, MQ
2493 * and ANNOUNCE).
2494 *
2495 * This function handles all parts of the host-side of the ctrlq round-trip buffer processing.
2496 *
2497 * Invoked by worker for virtio-net control queue to process a queued control command buffer.
2498 *
2499 * @param pDevIns PDM device instance
2500 * @param pThis virtio-net device instance
2501 * @param pThisCC virtio-net device instance
2502 * @param pVirtqBuf pointer to buffer pulled from virtq (input to this function)
2503 */
2504static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2505 PVIRTQBUF pVirtqBuf)
2506{
2507 if (!(pThis->fNegotiatedFeatures & VIRTIONET_F_CTRL_VQ))
2508 LogFunc(("[%s] WARNING: Guest using CTRL queue w/o negotiating VIRTIONET_F_CTRL_VQ feature\n", pThis->szInst));
2509
2510 LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
2511
2512 if (pVirtqBuf->cbPhysSend < 2)
2513 {
2514 LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
2515 return;
2516 }
2517 else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
2518 {
2519 LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
2520 return;
2521 }
2522
2523 /*
2524 * Allocate buffer and read in the control command
2525 */
2526 AssertMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(VIRTIONET_CTRL_HDR_T),
2527 ("DESC chain too small for CTRL pkt header"));
2528
2529 VIRTIONET_CTRL_HDR_T CtrlPktHdr; RT_ZERO(CtrlPktHdr);
2530 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &CtrlPktHdr,
2531 RT_MIN(pVirtqBuf->cbPhysSend, sizeof(CtrlPktHdr)));
2532
2533 Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, CtrlPktHdr.uClass, CtrlPktHdr.uCmd));
2534
2535 uint8_t uAck;
2536 switch (CtrlPktHdr.uClass)
2537 {
2538 case VIRTIONET_CTRL_RX:
2539 uAck = virtioNetR3CtrlRx(pThis, pThisCC, &CtrlPktHdr, pVirtqBuf);
2540 break;
2541 case VIRTIONET_CTRL_MAC:
2542 uAck = virtioNetR3CtrlMac(pThis, &CtrlPktHdr, pVirtqBuf);
2543 break;
2544 case VIRTIONET_CTRL_VLAN:
2545 uAck = virtioNetR3CtrlVlan(pThis, &CtrlPktHdr, pVirtqBuf);
2546 break;
2547 case VIRTIONET_CTRL_MQ:
2548 uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, &CtrlPktHdr, pVirtqBuf);
2549 break;
2550 case VIRTIONET_CTRL_ANNOUNCE:
2551 uAck = VIRTIONET_OK;
2552 if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
2553 {
2554 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
2555 "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
2556 break;
2557 }
2558 if (CtrlPktHdr.uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
2559 {
2560 LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
2561 break;
2562 }
2563#if FEATURE_OFFERED(STATUS)
2564 pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
2565#endif
2566 Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
2567 break;
2568 default:
2569 LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", CtrlPktHdr.uClass));
2570 uAck = VIRTIONET_ERROR;
2571 }
2572
2573 /* Return CTRL packet Ack byte (result code) to guest driver */
2574 RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
2575 RTSGBUF SgBuf;
2576
2577 RTSgBufInit(&SgBuf, aStaticSegs, RT_ELEMENTS(aStaticSegs));
2578 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, &SgBuf, pVirtqBuf, true /* fFence */);
2579 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
2580
2581 LogFunc(("%s Finished processing CTRL command with status %s\n",
2582 pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
2583}
2584
2585/**
2586 * Reads virtio-net pkt header from provided Phy. addr of virtio descriptor chain
2587 * (e.g. S/G segment from guest-driver provided buffer pulled from Tx virtq)
2588 * Verifies state and supported modes, sets TCP header size.
2589 *
2590 * @param pVirtio VirtIO core instance data
2591 * @param pThis virtio-net instance
2592 * @param pDevIns PDM device instance
2593 * @param GCPhys Phys. Address from where to read virtio-net pkt header
2594 * @param pPktHdr Where to store read Tx pkt hdr (virtio pkt hdr size is determined from instance configuration)
2595 * @param cbFrame Total pkt frame size to inform bounds check
2596 */
2597static int virtioNetR3ReadVirtioTxPktHdr(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
2598{
2599 int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
2600 if (RT_FAILURE(rc))
2601 return rc;
2602
2603 LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
2604 pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
2605 pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
2606
2607 if (pPktHdr->uGsoType)
2608 {
2609 /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
2610 AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2611 && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
2612 ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
2613
2614 uint32_t uTcpHdrSize;
2615 switch (pPktHdr->uGsoType)
2616 {
2617 case VIRTIONET_HDR_GSO_TCPV4:
2618 case VIRTIONET_HDR_GSO_TCPV6:
2619 uTcpHdrSize = sizeof(RTNETTCP);
2620 break;
2621 case VIRTIONET_HDR_GSO_UDP:
2622 uTcpHdrSize = 0;
2623 break;
2624 default:
2625 LogFunc(("Bad GSO type in packet header\n"));
2626 return VERR_INVALID_PARAMETER;
2627 }
2628 /* Header + MSS must not exceed the packet size. */
2629 AssertMsgReturn(RT_LIKELY(uTcpHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
2630 ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
2631 }
2632
2633 AssertMsgReturn( !(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2634 || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
2635 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
2636 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
2637 VERR_BUFFER_OVERFLOW);
2638
2639 return VINF_SUCCESS;
2640}
2641
2642/**
2643 * Transmits single GSO frame via PDM framework to downstream PDM device, to emit from virtual NIC.
2644 *
2645 * This does final prep of GSO parameters including checksum calculation if configured
2646 * (e.g. if VIRTIONET_HDR_F_NEEDS_CSUM flag is set).
2647 *
2648 * @param pThis virtio-net instance
2649 * @param pThisCC virtio-net instance
2650 * @param pSgBuf PDM S/G buffer containing pkt and hdr to transmit
2651 * @param pGso GSO parameters used for the packet
2652 * @param pPktHdr virtio-net pkt header to adapt to PDM semantics
2653 */
2654static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
2655 PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
2656{
2657
2658 virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
2659 if (pGso)
2660 {
2661 /* Some guests (RHEL) may report HdrLen excluding transport layer header!
2662 * Thus cannot use cdHdrs provided by the guest because of different ways
2663 * it gets filled out by different versions of kernels. */
2664 Log4Func(("%s HdrLen before adjustment %d.\n", pThis->szInst, pGso->cbHdrsTotal));
2665 switch (pGso->u8Type)
2666 {
2667 case PDMNETWORKGSOTYPE_IPV4_TCP:
2668 case PDMNETWORKGSOTYPE_IPV6_TCP:
2669 pGso->cbHdrsTotal = pPktHdr->uChksumStart +
2670 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
2671 AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
2672 ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
2673 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
2674 break;
2675 case PDMNETWORKGSOTYPE_IPV4_UDP:
2676 pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
2677 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
2678 break;
2679 case PDMNETWORKGSOTYPE_INVALID:
2680 LogFunc(("%s ignoring invalid GSO frame\n", pThis->szInst));
2681 return VERR_INVALID_PARAMETER;
2682 }
2683 /* Update GSO structure embedded into the frame */
2684 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
2685 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
2686 Log4Func(("%s adjusted HdrLen to %d.\n",
2687 pThis->szInst, pGso->cbHdrsTotal));
2688 Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
2689 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2690 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2691 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
2692 }
2693 else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2694 {
2695 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
2696 /*
2697 * This is not GSO frame but checksum offloading is requested.
2698 */
2699 virtioNetR3Calc16BitChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
2700 pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
2701 }
2702
2703 return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
2704}
2705
2706/**
2707 * Non-reentrant function transmits all available packets from specified Tx virtq to downstream
2708 * PDM device (if cable is connected). For each Tx pkt, virtio-net pkt header is converted
2709 * to required GSO information (VBox host network stack semantics)
2710 *
2711 * @param pDevIns PDM device instance
2712 * @param pThis virtio-net device instance
2713 * @param pThisCC virtio-net device instance
2714 * @param pTxVirtq Address of transmit virtq
2715 * @param fOnWorkerThread Flag to PDM whether to use caller's or or PDM transmit worker's thread.
2716 */
2717static int virtioNetR3TransmitPkts(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2718 PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
2719{
2720 PVIRTIOCORE pVirtio = &pThis->Virtio;
2721
2722
2723 if (!pThis->fVirtioReady)
2724 {
2725 LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
2726 pThis->szInst, pThis->virtioNetConfig.uStatus));
2727 return VERR_IGNORED;
2728 }
2729
2730 if (!pThis->fCableConnected)
2731 {
2732 Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
2733 return VERR_IGNORED;
2734 }
2735
2736 /*
2737 * Only one thread is allowed to transmit at a time, others should skip transmission as the packets
2738 * will be picked up by the transmitting thread.
2739 */
2740 if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
2741 return VERR_IGNORED;
2742
2743 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2744 if (pDrv)
2745 {
2746 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
2747 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
2748 if (rc == VERR_TRY_AGAIN)
2749 {
2750 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2751 return VERR_TRY_AGAIN;
2752 }
2753 }
2754 int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2755 if (!cPkts)
2756 {
2757 LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
2758
2759 if (pDrv)
2760 pDrv->pfnEndXmit(pDrv);
2761
2762 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2763 return VERR_MISSING;
2764 }
2765 LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
2766
2767 virtioNetR3SetWriteLed(pThisCC, true);
2768
2769 /* Disable notifications until all available descriptors have been processed */
2770 if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX))
2771 virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, false /* fEnable */);
2772
2773 int rc;
2774#ifdef VIRTIO_VBUF_ON_STACK
2775 VIRTQBUF_T VirtqBuf;
2776
2777 VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
2778 VirtqBuf.cRefs = 1;
2779
2780 PVIRTQBUF pVirtqBuf = &VirtqBuf;
2781 while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, pVirtqBuf)) == VINF_SUCCESS)
2782#else /* !VIRTIO_VBUF_ON_STACK */
2783 PVIRTQBUF pVirtqBuf = NULL;
2784 while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, &pVirtqBuf)) == VINF_SUCCESS)
2785#endif /* !VIRTIO_VBUF_ON_STACK */
2786 {
2787 Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
2788
2789 PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
2790 PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
2791 uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
2792 size_t uFrameSize = 0;
2793
2794 AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
2795 ("Desc chain's first seg has insufficient space for pkt header!\n"),
2796 VERR_INTERNAL_ERROR);
2797
2798#ifdef VIRTIO_VBUF_ON_STACK
2799 VIRTIONETPKTHDR PktHdr;
2800 PVIRTIONETPKTHDR pPktHdr = &PktHdr;
2801#else /* !VIRTIO_VBUF_ON_STACK */
2802 PVIRTIONETPKTHDR pPktHdr = (PVIRTIONETPKTHDR)RTMemAllocZ(pThis->cbPktHdr);
2803 AssertMsgReturn(pPktHdr, ("Out of Memory\n"), VERR_NO_MEMORY);
2804#endif /* !VIRTIO_VBUF_ON_STACK */
2805
2806 /* Compute total frame size from guest (including virtio-net pkt hdr) */
2807 for (unsigned i = 0; i < cSegsFromGuest && uFrameSize < VIRTIONET_MAX_FRAME_SIZE; i++)
2808 uFrameSize += paSegsFromGuest[i].cbSeg;
2809
2810 Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uFrameSize));
2811 Assert(uFrameSize <= VIRTIONET_MAX_FRAME_SIZE);
2812
2813 /* Truncate oversized frames. */
2814 if (uFrameSize > VIRTIONET_MAX_FRAME_SIZE)
2815 uFrameSize = VIRTIONET_MAX_FRAME_SIZE;
2816
2817 if (pThisCC->pDrv)
2818 {
2819 uFrameSize -= pThis->cbPktHdr;
2820 /*
2821 * Peel off pkt header and convert to PDM/GSO semantics.
2822 */
2823 rc = virtioNetR3ReadVirtioTxPktHdr(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uFrameSize /* cbFrame */);
2824 if (RT_FAILURE(rc))
2825 return rc;
2826 virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
2827
2828 PDMNETWORKGSO Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
2829
2830 /* Allocate PDM transmit buffer to send guest provided network frame from to VBox network leaf device */
2831 PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
2832 rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uFrameSize, pGso, &pSgBufToPdmLeafDevice);
2833
2834 /*
2835 * Copy virtio-net guest S/G buffer to PDM leaf driver S/G buffer
2836 * converting from GCphys to virt memory at the same time
2837 */
2838 if (RT_SUCCESS(rc))
2839 {
2840 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
2841 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
2842
2843 size_t cbCopied = 0;
2844 size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uFrameSize;
2845 uint64_t uOffset = 0;
2846 while (cbRemain)
2847 {
2848 PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
2849 uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
2850 uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
2851 uint64_t srcSgCur = (uint64_t)pSgPhysSend->GCPhysCur;
2852 cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
2853 virtioCoreGCPhysRead(pVirtio, pDevIns,
2854 (RTGCPHYS)pSgPhysSend->GCPhysCur,
2855 ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
2856 virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
2857 cbRemain -= cbCopied;
2858 uOffset += cbCopied;
2859 }
2860
2861 LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
2862 uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
2863
2864 rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
2865 if (RT_FAILURE(rc))
2866 {
2867 LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
2868 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2869 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
2870 pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
2871 }
2872 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2873 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
2874 }
2875 else
2876 {
2877 Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
2878 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
2879#ifndef VIRTIO_VBUF_ON_STACK
2880 virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
2881#endif /* !VIRTIO_VBUF_ON_STACK */
2882 break;
2883 }
2884
2885 virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
2886
2887 /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
2888 virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
2889 virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2890 }
2891
2892#ifndef VIRTIO_VBUF_ON_STACK
2893 virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
2894 pVirtqBuf = NULL;
2895#endif /* !VIRTIO_VBUF_ON_STACK */
2896 /* Before we break the loop we need to check if the queue is empty,
2897 * re-enable notifications, and then re-check again to avoid missing
2898 * a notification for the descriptor that is added to the queue
2899 * after we have checked it on being empty, but before we re-enabled
2900 * notifications.
2901 */
2902 if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
2903 && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
2904 virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
2905 }
2906 virtioNetR3SetWriteLed(pThisCC, false);
2907
2908 if (pDrv)
2909 pDrv->pfnEndXmit(pDrv);
2910
2911 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2912 return VINF_SUCCESS;
2913}
2914
2915/**
2916 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2917 */
2918static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
2919{
2920 LogFunc(("\n"));
2921 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2922 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2923 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2924 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(0)];
2925 STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
2926
2927 (void)virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
2928}
2929
2930/**
2931 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
2932 */
2933static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
2934{
2935 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2936 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2937
2938 SET_LINK_UP(pThis);
2939 virtioNetWakeupRxBufWaiter(pDevIns);
2940
2941 if (pThisCC->pDrv)
2942 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
2943
2944 LogFunc(("[%s] Link is up\n", pThis->szInst));
2945 RT_NOREF(hTimer, pvUser);
2946}
2947
2948/**
2949 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
2950 */
2951static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
2952{
2953 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2954 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2955 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2956
2957 bool fRequestedLinkStateIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
2958
2959#ifdef LOG_ENABLED
2960 if (LogIs7Enabled())
2961 {
2962 LogFunc(("[%s]", pThis->szInst));
2963 switch(enmState)
2964 {
2965 case PDMNETWORKLINKSTATE_UP:
2966 Log(("UP\n"));
2967 break;
2968 case PDMNETWORKLINKSTATE_DOWN:
2969 Log(("DOWN\n"));
2970 break;
2971 case PDMNETWORKLINKSTATE_DOWN_RESUME:
2972 Log(("DOWN (RESUME)\n"));
2973 break;
2974 default:
2975 Log(("UNKNOWN)\n"));
2976 }
2977 }
2978#endif
2979
2980 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
2981 {
2982 if (IS_LINK_UP(pThis))
2983 {
2984 /*
2985 * We bother to bring the link down only if it was up previously. The UP link state
2986 * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
2987 */
2988 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
2989 if (pThisCC->pDrv)
2990 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2991 }
2992 }
2993 else if (fRequestedLinkStateIsUp != IS_LINK_UP(pThis))
2994 {
2995 if (fRequestedLinkStateIsUp)
2996 {
2997 Log(("[%s] Link is up\n", pThis->szInst));
2998 pThis->fCableConnected = true;
2999 SET_LINK_UP(pThis);
3000 }
3001 else /* Link requested to be brought down */
3002 {
3003 /* The link was brought down explicitly, make sure it won't come up by timer. */
3004 PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
3005 Log(("[%s] Link is down\n", pThis->szInst));
3006 pThis->fCableConnected = false;
3007 SET_LINK_DOWN(pThis);
3008 }
3009 if (pThisCC->pDrv)
3010 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
3011 }
3012 return VINF_SUCCESS;
3013}
3014/**
3015 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
3016 */
3017static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
3018{
3019 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
3020 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
3021
3022 return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
3023}
3024
3025static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
3026{
3027 Log10Func(("[%s]\n", pThis->szInst));
3028 int rc = VINF_SUCCESS;
3029 for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
3030 {
3031 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uIdxWorker];
3032 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
3033
3034 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
3035 {
3036 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
3037 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
3038 }
3039 if (pWorkerR3->pThread)
3040 {
3041 int rcThread;
3042 rc = PDMDevHlpThreadDestroy(pDevIns, pWorkerR3->pThread, &rcThread);
3043 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
3044 AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
3045 pWorkerR3->pThread = NULL;
3046 }
3047 }
3048 return rc;
3049}
3050
3051/**
3052 * Creates a worker for specified queue, along with semaphore to throttle the worker.
3053 *
3054 * @param pDevIns - PDM device instance
3055 * @param pThis - virtio-net instance
3056 * @param pWorker - Pointer to worker state
3057 * @param pWorkerR3 - Pointer to worker state
3058 * @param pVirtq - Pointer to virtq
3059 */
3060static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis,
3061 PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
3062 PVIRTIONETVIRTQ pVirtq)
3063{
3064 Log10Func(("[%s]\n", pThis->szInst));
3065 RT_NOREF(pThis);
3066
3067 int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess);
3068
3069 if (RT_FAILURE(rc))
3070 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
3071 N_("DevVirtioNET: Failed to create SUP event semaphore"));
3072
3073 LogFunc(("creating thread for queue %s\n", pVirtq->szName));
3074
3075 rc = PDMDevHlpThreadCreate(pDevIns, &pWorkerR3->pThread,
3076 (void *)pWorker, virtioNetR3WorkerThread,
3077 virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, pVirtq->szName);
3078 if (RT_FAILURE(rc))
3079 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
3080 N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
3081
3082 pWorker->fAssigned = true; /* Because worker's state in fixed-size array initialized w/empty slots */
3083
3084 LogFunc(("%s pThread: %p\n", pVirtq->szName, pWorkerR3->pThread));
3085
3086 return rc;
3087}
3088
3089static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
3090{
3091 Log10Func(("[%s]\n", pThis->szInst));
3092 int rc;
3093
3094 /* Create the Control Queue worker anyway whether or not it is feature-negotiated or utilized by the guest.
3095 * See related comment for queue construction in the device constructor function for more context.
3096 */
3097
3098 PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
3099 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis,
3100 &pThis->aWorkers[CTRLQIDX], &pThisCC->aWorkers[CTRLQIDX], pCtlVirtq);
3101 AssertRCReturn(rc, rc);
3102
3103 pCtlVirtq->fHasWorker = true;
3104
3105 for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
3106 {
3107 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
3108 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
3109
3110 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, &pThis->aWorkers[TXQIDX(uVirtqPair)],
3111 &pThisCC->aWorkers[TXQIDX(uVirtqPair)], pTxVirtq);
3112 AssertRCReturn(rc, rc);
3113
3114 pTxVirtq->fHasWorker = true;
3115 pRxVirtq->fHasWorker = false;
3116 }
3117
3118 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
3119 pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
3120
3121 pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
3122
3123 return rc;
3124}
3125
3126
3127/**
3128 * @callback_method_impl{FNPDMTHREADDEV}
3129 */
3130static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3131{
3132 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3133 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3134 PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
3135 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[pWorker->uIdx];
3136 uint16_t uIdx = pWorker->uIdx;
3137
3138 ASMAtomicWriteBool(&pWorker->fSleeping, false);
3139
3140 Assert(pWorker->uIdx == pVirtq->uIdx);
3141
3142 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
3143 return VINF_SUCCESS;
3144
3145 LogFunc(("[%s] worker thread idx=%d started for %s (virtq idx=%d)\n", pThis->szInst, pWorker->uIdx, pVirtq->szName, pVirtq->uIdx));
3146
3147 /** @todo Race w/guest enabling/disabling guest notifications cyclically.
3148 See BugRef #8651, Comment #82 */
3149 virtioCoreVirtqEnableNotify(&pThis->Virtio, uIdx, true /* fEnable */);
3150
3151 while ( pThread->enmState != PDMTHREADSTATE_TERMINATING
3152 && pThread->enmState != PDMTHREADSTATE_TERMINATED)
3153 {
3154 if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pVirtq->uIdx))
3155 {
3156 /* Precisely coordinated atomic interlocks avoid a race condition that results in hung thread
3157 * wherein a sloppily coordinated wake-up notification during a transition into or out
3158 * of sleep leaves notifier and target mutually confused about actual & intended state.
3159 */
3160 ASMAtomicWriteBool(&pWorker->fSleeping, true);
3161 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
3162 if (!fNotificationSent)
3163 {
3164 Log10Func(("[%s] %s worker sleeping...\n\n", pThis->szInst, pVirtq->szName));
3165 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
3166
3167 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
3168 STAM_COUNTER_INC(&pThis->StatTransmitByThread);
3169 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
3170 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
3171 return VINF_SUCCESS;
3172 if (rc == VERR_INTERRUPTED)
3173 continue;
3174 ASMAtomicWriteBool(&pWorker->fNotified, false);
3175 }
3176 ASMAtomicWriteBool(&pWorker->fSleeping, false);
3177 }
3178 /*
3179 * Dispatch to the handler for the queue this worker is set up to drive
3180 */
3181 if (pVirtq->fCtlVirtq)
3182 {
3183 Log10Func(("[%s] %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
3184#ifdef VIRTIO_VBUF_ON_STACK
3185 VIRTQBUF_T VirtqBuf;
3186 PVIRTQBUF pVirtqBuf = &VirtqBuf;
3187 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, pVirtqBuf, true);
3188#else /* !VIRTIO_VBUF_ON_STACK */
3189 PVIRTQBUF pVirtqBuf = NULL;
3190 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, &pVirtqBuf, true);
3191#endif /* !VIRTIO_VBUF_ON_STACK */
3192 if (rc == VERR_NOT_AVAILABLE)
3193 {
3194 Log10Func(("[%s] %s worker woken. Nothing found in queue\n", pThis->szInst, pVirtq->szName));
3195 continue;
3196 }
3197 virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
3198#ifndef VIRTIO_VBUF_ON_STACK
3199 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
3200#endif /* !VIRTIO_VBUF_ON_STACK */
3201 }
3202 else /* Must be Tx queue */
3203 {
3204 Log10Func(("[%s] %s worker woken. Virtq has data to transmit\n", pThis->szInst, pVirtq->szName));
3205 virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
3206 }
3207 /* Note: Surprise! Rx queues aren't handled by local worker threads. Instead, the PDM network leaf driver
3208 * invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback, which waits until woken by virtioNetVirtqNotified()
3209 * indicating that guest IN buffers have been added to Rx virt queue.
3210 */
3211 }
3212 Log10(("[%s] %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
3213 return VINF_SUCCESS;
3214}
3215
3216/**
3217 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
3218 *
3219 * Called back by the core code when VirtIO's ready state has changed.
3220 */
3221static DECLCALLBACK(void) virtioNetR3StatusChg(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
3222{
3223 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
3224 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
3225
3226 pThis->fVirtioReady = fVirtioReady;
3227
3228 if (fVirtioReady)
3229 {
3230#ifdef LOG_ENABLED
3231 Log(("\n%-23s: %s *** VirtIO Ready ***\n\n", __FUNCTION__, pThis->szInst));
3232 virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
3233#endif
3234 pThis->fResetting = false;
3235 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(pVirtio);
3236 /* Now we can properly figure out the size of virtio header! */
3237 virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
3238 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
3239
3240 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3241 {
3242 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
3243 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
3244
3245 Assert(pWorker->uIdx == uVirtqNbr);
3246 RT_NOREF(pWorker);
3247
3248 Assert(pVirtq->uIdx == pWorker->uIdx);
3249
3250 (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
3251 pVirtq->fAttachedToVirtioCore = true;
3252 if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
3253 virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
3254 }
3255
3256 virtioNetWakeupRxBufWaiter(pThisCC->pDevIns);
3257 }
3258 else
3259 {
3260 Log(("\n%-23s: %s VirtIO is resetting ***\n", __FUNCTION__, pThis->szInst));
3261
3262 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
3263 Log7(("%-23s: %s Link is %s\n", __FUNCTION__, pThis->szInst, pThis->fCableConnected ? "up" : "down"));
3264
3265 pThis->fPromiscuous = true;
3266 pThis->fAllMulticast = false;
3267 pThis->fAllUnicast = false;
3268 pThis->fNoMulticast = false;
3269 pThis->fNoUnicast = false;
3270 pThis->fNoBroadcast = false;
3271 pThis->uIsTransmitting = 0;
3272 pThis->cUnicastFilterMacs = 0;
3273 pThis->cMulticastFilterMacs = 0;
3274
3275 memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
3276 memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
3277 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
3278
3279 if (pThisCC->pDrv)
3280 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
3281
3282 for (uint16_t uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3283 {
3284 virtioCoreR3VirtqDetach(&pThis->Virtio, uVirtqNbr);
3285 pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore = false;
3286 }
3287 }
3288}
3289
3290/**
3291 * @callback_method_impl{VIRTIOCORER3,pfnFeatureNegotiationComplete}
3292 */
3293static DECLCALLBACK(void) pfnFeatureNegotiationComplete(PVIRTIOCORE pVirtio, uint64_t fDriverFeatures, uint32_t fLegacy)
3294{
3295 PVIRTIONET pThis = PDMDEVINS_2_DATA(pVirtio->pDevInsR3, PVIRTIONET);
3296
3297 LogFunc(("[Feature Negotiation Complete] Guest Driver version is: %s\n", fLegacy ? "legacy" : "modern"));
3298 virtioNetConfigurePktHdr(pThis, fLegacy);
3299 virtioNetR3SetVirtqNames(pThis, fLegacy);
3300
3301 /* Senseless for modern guest to use control queue in this case. (See Note 1 in PDM-invoked device constructor) */
3302 if (!fLegacy && !(fDriverFeatures & VIRTIONET_F_CTRL_VQ))
3303 virtioNetR3VirtqDestroy(pVirtio, &pThis->aVirtqs[CTRLQIDX]);
3304}
3305
3306#endif /* IN_RING3 */
3307
3308/**
3309 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
3310 *
3311 * The VM is suspended at this point.
3312 */
3313static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3314{
3315 RT_NOREF(fFlags);
3316
3317 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3318 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3319
3320 Log7Func(("[%s]\n", pThis->szInst));
3321 RT_NOREF(pThis);
3322
3323 AssertLogRelReturnVoid(iLUN == 0);
3324
3325 pThisCC->pDrvBase = NULL;
3326 pThisCC->pDrv = NULL;
3327}
3328
3329/**
3330 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
3331 *
3332 * This is called when we change block driver.
3333 */
3334static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3335{
3336 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3337 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3338
3339 Log7Func(("[%s]", pThis->szInst));
3340 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
3341
3342 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
3343 if (RT_SUCCESS(rc))
3344 {
3345 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
3346 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
3347 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
3348 }
3349 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
3350 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
3351 {
3352 /* This should never happen because this function is not called
3353 * if there is no driver to attach! */
3354 Log(("[%s] No attached driver!\n", pThis->szInst));
3355 }
3356
3357 RT_NOREF2(pThis, fFlags);
3358 return rc;
3359}
3360
3361/**
3362 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
3363 */
3364static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
3365{
3366 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
3367 if (iLUN)
3368 return VERR_PDM_LUN_NOT_FOUND;
3369 *ppLed = &pThisR3->led;
3370 return VINF_SUCCESS;
3371}
3372
3373/**
3374 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3375 */
3376static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
3377{
3378 PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
3379 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
3380 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
3381 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
3382 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
3383 return NULL;
3384}
3385
3386/**
3387 * @interface_method_impl{PDMDEVREGR3,pfnReset}
3388 */
3389static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
3390{
3391 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3392 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3393
3394 virtioCoreR3ResetDevice(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
3395}
3396
3397/**
3398 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
3399 */
3400static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
3401{
3402 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
3403
3404 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3405 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3406
3407 Log(("[%s] Destroying instance\n", pThis->szInst));
3408 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
3409 {
3410 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
3411 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
3412 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
3413 }
3414
3415 virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
3416 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
3417 return VINF_SUCCESS;
3418}
3419
3420/**
3421 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
3422 *
3423 * Notes about revising originally VirtIO 1.0+ only virtio-net device emulator to be "transitional",
3424 * a VirtIO term meaning this now interoperates with both "legacy" (e.g. pre-1.0) and "modern" (1.0+)
3425 * guest virtio-net drivers. The changes include migrating VMs saved using prior DevVirtioNet.cpp (0.95)
3426 * saveExec/loadExec semantics to use 1.0 save/load semantics.
3427 *
3428 * Regardless of the 1.0 spec's overall helpful guidance for implementing transitional devices,
3429 * A bit is left to the imagination, e.g. some things have to be determined deductively
3430 * (AKA "the hard way").
3431 *
3432 * Case in point: According to VirtIO 0.95 ("legacy") specification, section 2.2.1, "historically"
3433 * drivers may start driving prior to feature negotiation and prior to drivers setting DRIVER_OK
3434 * status, "provided driver doesn't use features that alter early use of this device". Interpreted
3435 * here to mean a virtio-net driver must respect default settings (such as implicit pkt header default
3436 * size, as determined per Note 1 below).
3437 *
3438 * ----------------------------------------------------------------------------------------------
3439 * Transitional device initialization Note 1: Identifying default value for network Rx pkt hdr size.
3440 * (VirtIO 1.0 specification section 5.1.6.1)
3441 *
3442 * Guest virtio legacy drivers may begin operations prematurely, regardless of early spec's
3443 * initialization sequence (see note 2 below). Legacy drivers implicitly default to using the
3444 * (historically) shortest-length network packet header *unless* VIRTIONET_F_MRG_RXBUF feature is
3445 * negotiated. If feature negotiation phase is [optionally] enacted by a legacy guest (i.e. we strictly
3446 * enforce full initialization protocol for modern guests), virtioNetConfigurePktHdr() is invoked again to
3447 * finalize device's network packet header size. Best-guess at default packet header size is deduced, e.g.
3448 * isn't documented, as follows: A legacy guest with VIRTIONET_F_MRG_RXBUF not-yet-negotiated is the only
3449 * case where network I/O could possibly occur with any reasonable assumption about packet type/size,
3450 * because logically other permutations couldn't possibly be inferred until feature negotiation
3451 * is complete. Specifically, those cases are:
3452 *
3453 * 1. A modern driver (detected only when VIRTIONET_F_VERSION_1 feature is ack'd by guest, and,
3454 * simultaneously, VIRTIONET_F_MRG_RXBUF feature is accepted or declined (determining network receive-packet
3455 * processing behavior).
3456 *
3457 * 2. A legacy driver that has agreed to use VIRTIONET_F_MRG_RXBUF feature, resulting in a two-byte larger pkt hdr,
3458 * (as well as deciding Rx packet processing behavior).
3459 *
3460 * ----------------------------------------------------------------------------------------------
3461 * Transitional device initialization Note 2: Creating unnegotiated control queue.
3462 * (VirtIO 1.0 spec, sections 5.1.5 and 5.1.6.5)
3463 *
3464 * Create all queues immediately, prior to feature negotiation, including control queue (irrespective
3465 * of the fact it's too early in initialization for control feature to be approved by guest). This
3466 * transitional device must deal with legacy guests which *can* (and on linux have been seen to) use
3467 * the control queue prior to feature negotiation.
3468 *
3469 * The initial assumption is *modern" guest virtio-net drivers out in the wild could never reasonably
3470 * attempt something as obviously risky as using ctrlq without first acking VIRTIO_NET_F_CTRL_VQ
3471 * feature to establish it. For now, we create the control queue proactively to accomodate a potentially
3472 * badly behaved but officially sanctioned legacy virtio-net driver, but *destroy* that same queue
3473 * if a driver announces as 'modern' during feature finalization yet leaves VIRTIO_NET_F_CTRL_VQ un-ack'd.
3474 */
3475static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3476{
3477 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3478 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3479 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3480 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3481
3482 /*
3483 * Quickly initialize state data to ensure destructor always works.
3484 */
3485 Log7Func(("PDM device instance: %d\n", iInstance));
3486 RTStrPrintf(pThis->szInst, sizeof(pThis->szInst), "virtio-net #%d", iInstance);
3487
3488 pThisCC->pDevIns = pDevIns;
3489 pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
3490 pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
3491 pThisCC->led.u32Magic = PDMLED_MAGIC;
3492
3493 /* Interfaces */
3494 pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
3495 pThisCC->INetworkDown.pfnReceive = virtioNetR3NetworkDown_Receive;
3496 pThisCC->INetworkDown.pfnReceiveGso = virtioNetR3NetworkDown_ReceiveGso;
3497 pThisCC->INetworkDown.pfnXmitPending = virtioNetR3NetworkDown_XmitPending;
3498 pThisCC->INetworkConfig.pfnGetMac = virtioNetR3NetworkConfig_GetMac;
3499 pThisCC->INetworkConfig.pfnGetLinkState = virtioNetR3NetworkConfig_GetLinkState;
3500 pThisCC->INetworkConfig.pfnSetLinkState = virtioNetR3NetworkConfig_SetLinkState;
3501
3502 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
3503
3504 /*
3505 * Validate configuration.
3506 */
3507 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo|Legacy", "");
3508
3509 /* Get config params */
3510 int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
3511 if (RT_FAILURE(rc))
3512 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
3513
3514 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
3515 if (RT_FAILURE(rc))
3516 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
3517
3518 uint32_t uStatNo = iInstance;
3519 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
3520 if (RT_FAILURE(rc))
3521 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
3522
3523 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
3524 if (RT_FAILURE(rc))
3525 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
3526
3527 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
3528
3529 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
3530 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
3531 pThis->szInst, pThis->cMsLinkUpDelay / 1000));
3532
3533 Log(("[%s] Link up delay is set to %u seconds\n", pThis->szInst, pThis->cMsLinkUpDelay / 1000));
3534
3535 /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
3536 memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
3537
3538 Log(("Using MAC address for %s: %2x:%2x:%2x:%2x:%2x:%2x\n", pThis->szInst,
3539 pThis->macConfigured.au8[0], pThis->macConfigured.au8[1], pThis->macConfigured.au8[2],
3540 pThis->macConfigured.au8[3], pThis->macConfigured.au8[4], pThis->macConfigured.au8[5]));
3541
3542 LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
3543
3544 /*
3545 * Configure Virtio core (generic Virtio queue and infrastructure management) parameters.
3546 */
3547# if FEATURE_OFFERED(STATUS)
3548 pThis->virtioNetConfig.uStatus = 0;
3549# endif
3550
3551 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
3552 pThisCC->Virtio.pfnFeatureNegotiationComplete = pfnFeatureNegotiationComplete;
3553 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3554 pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChg;
3555 pThisCC->Virtio.pfnDevCapRead = virtioNetR3DevCapRead;
3556 pThisCC->Virtio.pfnDevCapWrite = virtioNetR3DevCapWrite;
3557
3558 VIRTIOPCIPARAMS VirtioPciParams;
3559 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
3560 VirtioPciParams.uClassBase = PCI_CLASS_BASE_NETWORK_CONTROLLER;
3561 VirtioPciParams.uClassSub = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
3562 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
3563 VirtioPciParams.uSubsystemId = DEVICE_PCI_NETWORK_SUBSYSTEM; /* VirtIO 1.0 allows PCI Device ID here */
3564 VirtioPciParams.uInterruptLine = 0x00;
3565 VirtioPciParams.uInterruptPin = 0x01;
3566
3567 /* Create semaphore used to synchronize/throttle the downstream LUN's Rx waiter thread. */
3568 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
3569 if (RT_FAILURE(rc))
3570 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
3571
3572 pThis->fOfferLegacy = VIRTIONET_TRANSITIONAL_ENABLE_FLAG;
3573 virtioNetConfigurePktHdr(pThis, pThis->fOfferLegacy); /* set defaults */
3574
3575 /* Initialize VirtIO core. (*pfnStatusChanged)() callback occurs when both host VirtIO core & guest driver are ready) */
3576 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
3577 VIRTIONET_HOST_FEATURES_OFFERED, pThis->fOfferLegacy,
3578 &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
3579 if (RT_FAILURE(rc))
3580 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
3581
3582 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
3583 /** @todo validating features at this point is most probably pointless, as the negotiation hasn't started yet. */
3584 if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
3585 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
3586 pThis->cVirtqPairs = pThis->virtioNetConfig.uMaxVirtqPairs;
3587 pThis->cVirtqs += pThis->cVirtqPairs * 2 + 1;
3588 pThis->aVirtqs[CTRLQIDX].fCtlVirtq = true;
3589
3590 virtioNetR3SetVirtqNames(pThis, pThis->fOfferLegacy);
3591 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3592 {
3593 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
3594 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
3595 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
3596 pVirtq->uIdx = pWorker->uIdx = pWorkerR3->uIdx = uVirtqNbr;
3597 }
3598 /*
3599 * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
3600 */
3601 rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
3602 if (RT_FAILURE(rc))
3603 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
3604
3605 /* Create Link Up Timer */
3606 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL,
3607 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
3608 "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
3609 /*
3610 * Attach network driver instance
3611 */
3612 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
3613 if (RT_SUCCESS(rc))
3614 {
3615 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
3616 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
3617 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
3618 }
3619 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
3620 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
3621 {
3622 /* No error! */
3623 Log(("[%s] No attached driver!\n", pThis->szInst));
3624 }
3625 else
3626 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
3627 /*
3628 * Status driver
3629 */
3630 PPDMIBASE pUpBase;
3631 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
3632 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
3633 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
3634
3635 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
3636 /*
3637 * Register saved state.
3638 */
3639 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIONET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
3640 NULL, NULL, NULL, /** @todo r=aeichner Teleportation? */
3641 NULL, virtioNetR3ModernSaveExec, NULL,
3642 NULL, virtioNetR3ModernLoadExec, virtioNetR3ModernLoadDone);
3643 AssertRCReturn(rc, rc);
3644 /*
3645 * Statistics and debug stuff.
3646 * The /Public/ bits are official and used by session info in the GUI.
3647 */
3648 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3649 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
3650 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3651 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
3652 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
3653 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
3654
3655 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
3656 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
3657 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
3658 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
3659 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
3660 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
3661# ifdef VBOX_WITH_STATISTICS
3662 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
3663 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
3664 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
3665 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
3666 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
3667 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
3668 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
3669 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
3670# endif
3671 /*
3672 * Register the debugger info callback (ignore errors).
3673 */
3674 char szTmp[128];
3675 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "virtio-net", "Display virtio-net info (help, net, features, state, pointers, queues, all)", virtioNetR3Info);
3676 if (RT_FAILURE(rc))
3677 LogRel(("Failed to register DBGF info for device %s\n", szTmp));
3678 return rc;
3679}
3680
3681#else /* !IN_RING3 */
3682
3683/**
3684 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3685 */
3686static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
3687{
3688 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3689 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3690 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3691 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3692 return virtioCoreRZInit(pDevIns, &pThis->Virtio);
3693}
3694
3695#endif /* !IN_RING3 */
3696
3697/**
3698 * The device registration structure.
3699 */
3700const PDMDEVREG g_DeviceVirtioNet =
3701{
3702 /* .uVersion = */ PDM_DEVREG_VERSION,
3703 /* .uReserved0 = */ 0,
3704 /* .szName = */ "virtio-net",
3705 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
3706 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
3707 /* .cMaxInstances = */ ~0U,
3708 /* .uSharedVersion = */ 42,
3709 /* .cbInstanceShared = */ sizeof(VIRTIONET),
3710 /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
3711 /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
3712 /* .cMaxPciDevices = */ 1,
3713 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
3714 /* .pszDescription = */ "Virtio Host NET.\n",
3715#if defined(IN_RING3)
3716 /* .pszRCMod = */ "VBoxDDRC.rc",
3717 /* .pszR0Mod = */ "VBoxDDR0.r0",
3718 /* .pfnConstruct = */ virtioNetR3Construct,
3719 /* .pfnDestruct = */ virtioNetR3Destruct,
3720 /* .pfnRelocate = */ NULL,
3721 /* .pfnMemSetup = */ NULL,
3722 /* .pfnPowerOn = */ NULL,
3723 /* .pfnReset = */ virtioNetR3Reset,
3724 /* .pfnSuspend = */ virtioNetWakeupRxBufWaiter,
3725 /* .pfnResume = */ NULL,
3726 /* .pfnAttach = */ virtioNetR3Attach,
3727 /* .pfnDetach = */ virtioNetR3Detach,
3728 /* .pfnQueryInterface = */ NULL,
3729 /* .pfnInitComplete = */ NULL,
3730 /* .pfnPowerOff = */ virtioNetWakeupRxBufWaiter,
3731 /* .pfnSoftReset = */ NULL,
3732 /* .pfnReserved0 = */ NULL,
3733 /* .pfnReserved1 = */ NULL,
3734 /* .pfnReserved2 = */ NULL,
3735 /* .pfnReserved3 = */ NULL,
3736 /* .pfnReserved4 = */ NULL,
3737 /* .pfnReserved5 = */ NULL,
3738 /* .pfnReserved6 = */ NULL,
3739 /* .pfnReserved7 = */ NULL,
3740#elif defined(IN_RING0)
3741 /* .pfnEarlyConstruct = */ NULL,
3742 /* .pfnConstruct = */ virtioNetRZConstruct,
3743 /* .pfnDestruct = */ NULL,
3744 /* .pfnFinalDestruct = */ NULL,
3745 /* .pfnRequest = */ NULL,
3746 /* .pfnReserved0 = */ NULL,
3747 /* .pfnReserved1 = */ NULL,
3748 /* .pfnReserved2 = */ NULL,
3749 /* .pfnReserved3 = */ NULL,
3750 /* .pfnReserved4 = */ NULL,
3751 /* .pfnReserved5 = */ NULL,
3752 /* .pfnReserved6 = */ NULL,
3753 /* .pfnReserved7 = */ NULL,
3754#elif defined(IN_RC)
3755 /* .pfnConstruct = */ virtioNetRZConstruct,
3756 /* .pfnReserved0 = */ NULL,
3757 /* .pfnReserved1 = */ NULL,
3758 /* .pfnReserved2 = */ NULL,
3759 /* .pfnReserved3 = */ NULL,
3760 /* .pfnReserved4 = */ NULL,
3761 /* .pfnReserved5 = */ NULL,
3762 /* .pfnReserved6 = */ NULL,
3763 /* .pfnReserved7 = */ NULL,
3764#else
3765# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3766#endif
3767 /* .uVersionEnd = */ PDM_DEVREG_VERSION
3768};
3769
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette