VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevVirtioNet_1_0.cpp@ 92091

Last change on this file since 92091 was 92091, checked in by vboxsync, 3 years ago

Optimize how legacy/modern driver is determined to avoid polling, and eliminate code duplication for feature logging

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 132.8 KB
Line 
1/* $Id: DevVirtioNet_1_0.cpp 92091 2021-10-27 05:55:32Z vboxsync $ $Revision: 92091 $ $Date: 2021-10-27 05:55:32 +0000 (Wed, 27 Oct 2021) $ $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-2020 Oracle Corporation
16 *
17 * This file is part of VirtualBox Open Source Edition (OSE), as
18 * available from http://www.virtualbox.org. This file is free software;
19 * you can redistribute it and/or modify it under the terms of the GNU
20 * General Public License (GPL) as published by the Free Software
21 * Foundation, in version 2 as it comes in the "COPYING" file of the
22 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30//#define LOG_GROUP LOG_GROUP_DRV_NET
31#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
32#define VIRTIONET_WITH_GSO
33
34#include <iprt/types.h>
35#include <iprt/errcore.h>
36#include <iprt/assert.h>
37#include <iprt/string.h>
38
39#include <VBox/sup.h>
40#include <VBox/vmm/pdmdev.h>
41#include <VBox/vmm/stam.h>
42#include <VBox/vmm/pdmcritsect.h>
43#include <VBox/vmm/pdmnetifs.h>
44#include <VBox/msi.h>
45#include <VBox/version.h>
46#include <VBox/log.h>
47
48#ifdef IN_RING3
49# include <VBox/VBoxPktDmp.h>
50# include <iprt/alloc.h>
51# include <iprt/memcache.h>
52# include <iprt/semaphore.h>
53# include <iprt/sg.h>
54# include <iprt/param.h>
55# include <iprt/uuid.h>
56#endif
57#include "../VirtIO/VirtioCore.h"
58
59#include "VBoxDD.h"
60
61#define LUN0 0
62
63/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
64 * @{ */
65#define VIRTIONET_F_CSUM RT_BIT_64(0) /**< Handle packets with partial checksum */
66#define VIRTIONET_F_GUEST_CSUM RT_BIT_64(1) /**< Handles packets with partial checksum */
67#define VIRTIONET_F_CTRL_GUEST_OFFLOADS RT_BIT_64(2) /**< Control channel offloads reconfig support */
68#define VIRTIONET_F_MAC RT_BIT_64(5) /**< Device has given MAC address */
69#define VIRTIONET_F_GUEST_TSO4 RT_BIT_64(7) /**< Driver can receive TSOv4 */
70#define VIRTIONET_F_GUEST_TSO6 RT_BIT_64(8) /**< Driver can receive TSOv6 */
71#define VIRTIONET_F_GUEST_ECN RT_BIT_64(9) /**< Driver can receive TSO with ECN */
72#define VIRTIONET_F_GUEST_UFO RT_BIT_64(10) /**< Driver can receive UFO */
73#define VIRTIONET_F_HOST_TSO4 RT_BIT_64(11) /**< Device can receive TSOv4 */
74#define VIRTIONET_F_HOST_TSO6 RT_BIT_64(12) /**< Device can receive TSOv6 */
75#define VIRTIONET_F_HOST_ECN RT_BIT_64(13) /**< Device can receive TSO with ECN */
76#define VIRTIONET_F_HOST_UFO RT_BIT_64(14) /**< Device can receive UFO */
77#define VIRTIONET_F_MRG_RXBUF RT_BIT_64(15) /**< Driver can merge receive buffers */
78#define VIRTIONET_F_STATUS RT_BIT_64(16) /**< Config status field is available */
79#define VIRTIONET_F_CTRL_VQ RT_BIT_64(17) /**< Control channel is available */
80#define VIRTIONET_F_CTRL_RX RT_BIT_64(18) /**< Control channel RX mode + MAC addr filtering */
81#define VIRTIONET_F_CTRL_VLAN RT_BIT_64(19) /**< Control channel VLAN filtering */
82#define VIRTIONET_F_CTRL_RX_EXTRA RT_BIT_64(20) /**< Control channel RX mode extra functions */
83#define VIRTIONET_F_GUEST_ANNOUNCE RT_BIT_64(21) /**< Driver can send gratuitous packets */
84#define VIRTIONET_F_MQ RT_BIT_64(22) /**< Support ultiqueue with auto receive steering */
85#define VIRTIONET_F_CTRL_MAC_ADDR RT_BIT_64(23) /**< Set MAC address through control channel */
86/** @} */
87
88static const VIRTIO_FEATURES_LIST s_aDevSpecificFeatures[] =
89{
90 { VIRTIONET_F_CSUM, " CSUM Host handles packets with partial checksum.\n" },
91 { VIRTIONET_F_GUEST_CSUM, " GUEST_CSUM Guest handles packets with partial checksum.\n" },
92 { VIRTIONET_F_CTRL_GUEST_OFFLOADS, " CTRL_GUEST_OFFLOADS Control channel offloads reconfiguration support.\n" },
93 { VIRTIONET_F_MAC, " MAC Host has given MAC address.\n" },
94 { VIRTIONET_F_GUEST_TSO4, " GUEST_TSO4 Guest can receive TSOv4.\n" },
95 { VIRTIONET_F_GUEST_TSO6, " GUEST_TSO6 Guest can receive TSOv6.\n" },
96 { VIRTIONET_F_GUEST_ECN, " GUEST_ECN Guest can receive TSO with ECN.\n" },
97 { VIRTIONET_F_GUEST_UFO, " GUEST_UFO Guest can receive UFO.\n" },
98 { VIRTIONET_F_HOST_TSO4, " HOST_TSO4 Host can receive TSOv4.\n" },
99 { VIRTIONET_F_HOST_TSO6, " HOST_TSO6 Host can receive TSOv6.\n" },
100 { VIRTIONET_F_HOST_ECN, " HOST_ECN Host can receive TSO with ECN.\n" },
101 { VIRTIONET_F_HOST_UFO, " HOST_UFO Host can receive UFO.\n" },
102 { VIRTIONET_F_MRG_RXBUF, " MRG_RXBUF Guest can merge receive buffers.\n" },
103 { VIRTIONET_F_STATUS, " STATUS Configuration status field is available.\n" },
104 { VIRTIONET_F_CTRL_VQ, " CTRL_VQ Control channel is available.\n" },
105 { VIRTIONET_F_CTRL_RX, " CTRL_RX Control channel RX mode support.\n" },
106 { VIRTIONET_F_CTRL_VLAN, " CTRL_VLAN Control channel VLAN filtering.\n" },
107 { VIRTIONET_F_GUEST_ANNOUNCE, " GUEST_ANNOUNCE Guest can send gratuitous packets.\n" },
108 { VIRTIONET_F_MQ, " MQ Host supports multiqueue with automatic receive steering.\n" },
109 { VIRTIONET_F_CTRL_MAC_ADDR, " CTRL_MAC_ADDR Set MAC address through control channel.\n" },
110};
111
112
113#ifdef VIRTIONET_WITH_GSO
114# define VIRTIONET_HOST_FEATURES_GSO \
115 VIRTIONET_F_CSUM \
116 | VIRTIONET_F_HOST_TSO4 \
117 | VIRTIONET_F_HOST_TSO6 \
118 | VIRTIONET_F_HOST_UFO \
119 | VIRTIONET_F_GUEST_TSO4 \
120 | VIRTIONET_F_GUEST_TSO6 \
121 | VIRTIONET_F_GUEST_UFO \
122 | VIRTIONET_F_GUEST_CSUM /* @bugref(4796) Guest must handle partial chksums */
123#else
124# define VIRTIONET_HOST_FEATURES_GSO
125#endif
126
127#define VIRTIONET_HOST_FEATURES_OFFERED \
128 VIRTIONET_F_STATUS \
129 | VIRTIONET_F_GUEST_ANNOUNCE \
130 | VIRTIONET_F_MAC \
131 | VIRTIONET_F_CTRL_VQ \
132 | VIRTIONET_F_CTRL_RX \
133 | VIRTIONET_F_CTRL_VLAN \
134 | VIRTIONET_HOST_FEATURES_GSO \
135 | VIRTIONET_F_MRG_RXBUF
136
137/*
138 * Glossary of networking acronyms used in the previous bit definitions:
139 *
140 * GSO = Generic Segmentation Offload
141 * TSO = TCP Segmentation Offload
142 * UFO = UDP Fragmentation Offload
143 * ECN = Explicit Congestion Notification
144 */
145
146#define FEATURE_ENABLED(feature) RT_BOOL(!!(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature))
147#define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
148#define FEATURE_OFFERED(feature) VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
149
150#define VIRTIONET_SAVED_STATE_VERSION UINT32_C(1)
151
152#if FEATURE_OFFERED(MQ)
153# define VIRTIONET_MAX_QPAIRS 1000 /* Instance data doesn't allow an array with VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX entries */
154#else
155# define VIRTIONET_MAX_QPAIRS VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN
156#endif
157
158#define VIRTIONET_MAX_WORKERS VIRTIONET_MAX_QPAIRS
159#define VIRTIONET_MAX_VIRTQS (VIRTIONET_MAX_QPAIRS * 2 + 1)
160#define VIRTIONET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP pkt size + Eth. header w/VLAN tag */
161#define VIRTIONET_MAC_FILTER_LEN 32
162#define VIRTIONET_MAX_VLAN_ID (1 << 12)
163#define VIRTIONET_RX_SEG_COUNT 32
164
165#define VIRTQNAME(uVirtqNbr) (pThis->aVirtqs[uVirtqNbr]->szName)
166#define CBVIRTQNAME(uVirtqNbr) RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
167
168/* Macros to calculate queue specific index number VirtIO 1.0, 5.1.2 */
169#define IS_TX_VIRTQ(n) ((n) != CTRLQIDX && ((n) & 1))
170#define IS_RX_VIRTQ(n) ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
171#define IS_CTRL_VIRTQ(n) ((n) == CTRLQIDX)
172#define RXQIDX(qPairIdx) (qPairIdx * 2)
173#define TXQIDX(qPairIdx) (RXQIDX(qPairIdx) + 1)
174#define CTRLQIDX (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
175
176#define IS_LINK_UP(pState) !!(pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
177#define IS_LINK_DOWN(pState) !IS_LINK_UP(pState)
178
179#define SET_LINK_UP(pState) \
180 LogFunc(("SET_LINK_UP\n")); \
181 pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
182 virtioCoreNotifyConfigChanged(&pThis->Virtio)
183
184#define SET_LINK_DOWN(pState) \
185 LogFunc(("SET_LINK_DOWN\n")); \
186 pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
187 virtioCoreNotifyConfigChanged(&pThis->Virtio)
188
189#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
190 (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
191
192
193#define PCI_DEVICE_ID_VIRTIONET_HOST 0x1000 /**< VirtIO transitional device ID for network card */
194#define PCI_CLASS_BASE_NETWORK_CONTROLLER 0x0200 /**< PCI Network device class */
195#define PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER 0x00 /**< PCI NET Controller subclass */
196#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
197#define VIRTIONET_PCI_CLASS 0x01 /**< Base class Mass Storage? */
198
199/**
200 * Virtio Net Host Device device-specific configuration (VirtIO 1.0, 5.1.4)
201 * VBox VirtIO core issues callback to this VirtIO device-specific implementation to handle
202 * MMIO accesses to device-specific configuration parameters.
203 */
204#pragma pack(1)
205
206 typedef struct virtio_net_config
207 {
208 RTMAC uMacAddress; /**< mac */
209
210#if FEATURE_OFFERED(STATUS)
211 uint16_t uStatus; /**< status */
212#endif
213
214#if FEATURE_OFFERED(MQ)
215 uint16_t uMaxVirtqPairs; /**< max_virtq_pairs */
216#endif
217
218 } VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
219
220#pragma pack()
221
222#define VIRTIONET_F_LINK_UP 1 /**< config status: Link is up */
223#define VIRTIONET_F_ANNOUNCE 2 /**< config status: Announce */
224
225/** @name VirtIO 1.0 NET Host Device device specific control types
226 * @{ */
227#define VIRTIONET_HDR_F_NEEDS_CSUM 1 /**< flags: Packet needs checksum */
228#define VIRTIONET_HDR_GSO_NONE 0 /**< gso_type: No Global Segmentation Offset */
229#define VIRTIONET_HDR_GSO_TCPV4 1 /**< gso_type: Global Segment Offset for TCPV4 */
230#define VIRTIONET_HDR_GSO_UDP 3 /**< gso_type: Global Segment Offset for UDP */
231#define VIRTIONET_HDR_GSO_TCPV6 4 /**< gso_type: Global Segment Offset for TCPV6 */
232#define VIRTIONET_HDR_GSO_ECN 0x80 /**< gso_type: Explicit Congestion Notification */
233/** @} */
234
235/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
236#pragma pack(1)
237struct virtio_net_pkt_hdr {
238 uint8_t uFlags; /**< flags */
239 uint8_t uGsoType; /**< gso_type */
240 uint16_t uHdrLen; /**< hdr_len */
241 uint16_t uGsoSize; /**< gso_size */
242 uint16_t uChksumStart; /**< Chksum_start */
243 uint16_t uChksumOffset; /**< Chksum_offset */
244 uint16_t uNumBuffers; /**< num_buffers */
245};
246#pragma pack()
247typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
248AssertCompileSize(VIRTIONETPKTHDR, 12);
249
250/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
251#pragma pack(1)
252struct virtio_net_ctrl_hdr {
253 uint8_t uClass; /**< class */
254 uint8_t uCmd; /**< command */
255};
256#pragma pack()
257typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
258
259typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
260
261/* Command entry fAck values */
262#define VIRTIONET_OK 0
263#define VIRTIONET_ERROR 1
264
265/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
266 * @{ */
267#define VIRTIONET_CTRL_RX 0 /**< Control class: Receive filtering */
268#define VIRTIONET_CTRL_RX_PROMISC 0 /**< Promiscuous mode */
269#define VIRTIONET_CTRL_RX_ALLMULTI 1 /**< All-multicast receive */
270#define VIRTIONET_CTRL_RX_ALLUNI 2 /**< All-unicast receive */
271#define VIRTIONET_CTRL_RX_NOMULTI 3 /**< No multicast receive */
272#define VIRTIONET_CTRL_RX_NOUNI 4 /**< No unicast receive */
273#define VIRTIONET_CTRL_RX_NOBCAST 5 /**< No broadcast receive */
274/** @} */
275
276typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
277typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
278typedef uint8_t VIRTIONET_CTRL_MAC_ENTRIES[][6];
279
280/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
281 * @{ */
282#define VIRTIONET_CTRL_MAC 1 /**< Control class: MAC address filtering */
283#define VIRTIONET_CTRL_MAC_TABLE_SET 0 /**< Set MAC table */
284#define VIRTIONET_CTRL_MAC_ADDR_SET 1 /**< Set default MAC address */
285/** @} */
286
287/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
288 * @{ */
289#define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */
290#define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */
291#define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */
292/** @} */
293
294/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
295 * @{ */
296#define VIRTIONET_CTRL_ANNOUNCE 3 /**< Control class: Gratuitous Packet Sending */
297#define VIRTIONET_CTRL_ANNOUNCE_ACK 0 /**< Gratuitous Packet Sending ACK */
298/** @} */
299
300struct virtio_net_ctrl_mq {
301 uint16_t uVirtqueuePairs; /**< virtqueue_pairs */
302};
303
304/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
305 * @{ */
306#define VIRTIONET_CTRL_MQ 4 /**< Control class: Receive steering */
307#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET 0 /**< Set number of TX/RX queues */
308#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN 1 /**< Minimum number of TX/RX queues */
309#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 /**< Maximum number of TX/RX queues */
310/** @} */
311
312uint64_t uOffloads; /**< offloads */
313
314/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
315 * @{ */
316#define VIRTIONET_CTRL_GUEST_OFFLOADS 5 /**< Control class: Offloads state configuration */
317#define VIRTIONET_CTRL_GUEST_OFFLOADS_SET 0 /** Apply new offloads configuration */
318/** @} */
319
320typedef enum VIRTIONETPKTHDRTYPE
321{
322 kVirtioNetModernPktHdr_1_0 = 0,
323 kVirtioNetLegacyPktHdr = 1,
324 kVirtioNetLegacyPktHdrWithoutMrgRx = 2,
325 kVirtioNetFor32BitHack = 0x7fffffff
326} VIRTIONETPKTHDRTYPE;
327
328/**
329 * device-specific queue info
330 */
331struct VIRTIONETWORKER;
332struct VIRTIONETWORKERR3;
333
334typedef struct VIRTIONETVIRTQ
335{
336 uint16_t uIdx; /**< Index of this queue */
337 uint16_t align;
338 char szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name */
339 bool fCtlVirtq; /**< If set this queue is the control queue */
340 bool fHasWorker; /**< If set this queue has an associated worker */
341 bool fAttachedToVirtioCore; /**< Set if queue attached to virtio core */
342 uint8_t pad;
343} VIRTIONETVIRTQ, *PVIRTIONETVIRTQ;
344
345/**
346 * Worker thread context, shared state.
347 */
348typedef struct VIRTIONETWORKER
349{
350 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
351 uint16_t uIdx; /**< Index of this worker */
352 bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
353 bool volatile fNotified; /**< Flags whether worker thread notified */
354 bool fAssigned; /**< Flags whether worker thread has been set up */
355 uint8_t pad;
356} VIRTIONETWORKER;
357/** Pointer to a virtio net worker. */
358typedef VIRTIONETWORKER *PVIRTIONETWORKER;
359
360/**
361 * Worker thread context, ring-3 state.
362 */
363typedef struct VIRTIONETWORKERR3
364{
365 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
366 uint16_t uIdx; /**< Index of this worker */
367 uint16_t pad;
368} VIRTIONETWORKERR3;
369/** Pointer to a virtio net worker. */
370typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
371
372/**
373 * VirtIO Host NET device state, shared edition.
374 *
375 * @extends VIRTIOCORE
376 */
377typedef struct VIRTIONET
378{
379 /** The core virtio state. */
380 VIRTIOCORE Virtio;
381
382 /** Virtio device-specific configuration */
383 VIRTIONET_CONFIG_T virtioNetConfig;
384
385 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
386 VIRTIONETWORKER aWorkers[VIRTIONET_MAX_VIRTQS];
387
388 /** Track which VirtIO queues we've attached to */
389 VIRTIONETVIRTQ aVirtqs[VIRTIONET_MAX_VIRTQS];
390
391 /** PDM device Instance name */
392 char szInst[16];
393
394 /** VirtIO features negotiated with the guest, including generic core and device specific */
395 uint64_t fNegotiatedFeatures;
396
397 /** Number of Rx/Tx queue pairs (only one if MQ feature not negotiated */
398 uint16_t cVirtqPairs;
399
400 /** Number of Rx/Tx queue pairs that have already been initialized */
401 uint16_t cInitializedVirtqPairs;
402
403 /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
404 uint16_t cVirtqs;
405
406 /** Number of worker threads (one for the control queue and one for each Tx queue) */
407 uint16_t cWorkers;
408
409 /** Alighnment */
410 uint16_t alignment;
411
412 /** Indicates transmission in progress -- only one thread is allowed. */
413 uint32_t uIsTransmitting;
414
415 /** virtio-net-1-dot-0 (in milliseconds). */
416 uint32_t cMsLinkUpDelay;
417
418 /** The number of actually used slots in aMacMulticastFilter. */
419 uint32_t cMulticastFilterMacs;
420
421 /** The number of actually used slots in aMacUniicastFilter. */
422 uint32_t cUnicastFilterMacs;
423
424 /** Semaphore leaf device's thread waits on until guest driver sends empty Rx bufs */
425 SUPSEMEVENT hEventRxDescAvail;
426
427 /** Array of MAC multicast addresses accepted by RX filter. */
428 RTMAC aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
429
430 /** Array of MAC unicast addresses accepted by RX filter. */
431 RTMAC aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
432
433 /** Default MAC address which rx filtering accepts */
434 RTMAC rxFilterMacDefault;
435
436 /** MAC address obtained from the configuration. */
437 RTMAC macConfigured;
438
439 /** Bit array of VLAN filter, one bit per VLAN ID. */
440 uint8_t aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
441
442 /** Set if PDM leaf device at the network interface is starved for Rx buffers */
443 bool volatile fLeafWantsEmptyRxBufs;
444
445 /** Number of packet being sent/received to show in debug log. */
446 uint32_t uPktNo;
447
448 /** Flags whether VirtIO core is in ready state */
449 uint8_t fVirtioReady;
450
451 /** Resetting flag */
452 uint8_t fResetting;
453
454 /** Promiscuous mode -- RX filter accepts all packets. */
455 uint8_t fPromiscuous;
456
457 /** All multicast mode -- RX filter accepts all multicast packets. */
458 uint8_t fAllMulticast;
459
460 /** All unicast mode -- RX filter accepts all unicast packets. */
461 uint8_t fAllUnicast;
462
463 /** No multicast mode - Supresses multicast receive */
464 uint8_t fNoMulticast;
465
466 /** No unicast mode - Suppresses unicast receive */
467 uint8_t fNoUnicast;
468
469 /** No broadcast mode - Supresses broadcast receive */
470 uint8_t fNoBroadcast;
471
472 /** Type of network pkt header based on guest driver version/features */
473 VIRTIONETPKTHDRTYPE ePktHdrType;
474
475 /** Size of network pkt header based on guest driver version/features */
476 uint16_t cbPktHdr;
477
478 /** True if physical cable is attached in configuration. */
479 bool fCableConnected;
480
481 /** True if guest has not reported modern virtio driver */
482 bool fIsLegacy;
483 /** @name Statistic
484 * @{ */
485 STAMCOUNTER StatReceiveBytes;
486 STAMCOUNTER StatTransmitBytes;
487 STAMCOUNTER StatReceiveGSO;
488 STAMCOUNTER StatTransmitPackets;
489 STAMCOUNTER StatTransmitGSO;
490 STAMCOUNTER StatTransmitCSum;
491#ifdef VBOX_WITH_STATISTICS
492 STAMPROFILE StatReceive;
493 STAMPROFILE StatReceiveStore;
494 STAMPROFILEADV StatTransmit;
495 STAMPROFILE StatTransmitSend;
496 STAMPROFILE StatRxOverflow;
497 STAMCOUNTER StatRxOverflowWakeup;
498 STAMCOUNTER StatTransmitByNetwork;
499 STAMCOUNTER StatTransmitByThread;
500 /** @} */
501#endif
502} VIRTIONET;
503/** Pointer to the shared state of the VirtIO Host NET device. */
504typedef VIRTIONET *PVIRTIONET;
505
506/**
507 * VirtIO Host NET device state, ring-3 edition.
508 *
509 * @extends VIRTIOCORER3
510 */
511typedef struct VIRTIONETR3
512{
513 /** The core virtio ring-3 state. */
514 VIRTIOCORER3 Virtio;
515
516 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
517 VIRTIONETWORKERR3 aWorkers[VIRTIONET_MAX_VIRTQS];
518
519 /** The device instance.
520 * @note This is _only_ for use whxen dealing with interface callbacks. */
521 PPDMDEVINSR3 pDevIns;
522
523 /** Status LUN: Base interface. */
524 PDMIBASE IBase;
525
526 /** Status LUN: LED port interface. */
527 PDMILEDPORTS ILeds;
528
529 /** Status LUN: LED connector (peer). */
530 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
531
532 /** Status: LED */
533 PDMLED led;
534
535 /** Attached network driver. */
536 R3PTRTYPE(PPDMIBASE) pDrvBase;
537
538 /** Network port interface (down) */
539 PDMINETWORKDOWN INetworkDown;
540
541 /** Network config port interface (main). */
542 PDMINETWORKCONFIG INetworkConfig;
543
544 /** Connector of attached network driver. */
545 R3PTRTYPE(PPDMINETWORKUP) pDrv;
546
547 /** Link Up(/Restore) Timer. */
548 TMTIMERHANDLE hLinkUpTimer;
549
550} VIRTIONETR3;
551
552
553/** Pointer to the ring-3 state of the VirtIO Host NET device. */
554typedef VIRTIONETR3 *PVIRTIONETR3;
555
556/**
557 * VirtIO Host NET device state, ring-0 edition.
558 */
559typedef struct VIRTIONETR0
560{
561 /** The core virtio ring-0 state. */
562 VIRTIOCORER0 Virtio;
563} VIRTIONETR0;
564/** Pointer to the ring-0 state of the VirtIO Host NET device. */
565typedef VIRTIONETR0 *PVIRTIONETR0;
566
567
568/**
569 * VirtIO Host NET device state, raw-mode edition.
570 */
571typedef struct VIRTIONETRC
572{
573 /** The core virtio raw-mode state. */
574 VIRTIOCORERC Virtio;
575} VIRTIONETRC;
576/** Pointer to the ring-0 state of the VirtIO Host NET device. */
577typedef VIRTIONETRC *PVIRTIONETRC;
578
579
580/** @typedef VIRTIONETCC
581 * The instance data for the current context. */
582typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
583
584/** @typedef PVIRTIONETCC
585 * Pointer to the instance data for the current context. */
586typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
587
588#ifdef IN_RING3
589static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
590static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
591
592DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
593{
594 if (!pThread)
595 return "<null>";
596
597 switch(pThread->enmState)
598 {
599 case PDMTHREADSTATE_INVALID:
600 return "invalid state";
601 case PDMTHREADSTATE_INITIALIZING:
602 return "initializing";
603 case PDMTHREADSTATE_SUSPENDING:
604 return "suspending";
605 case PDMTHREADSTATE_SUSPENDED:
606 return "suspended";
607 case PDMTHREADSTATE_RESUMING:
608 return "resuming";
609 case PDMTHREADSTATE_RUNNING:
610 return "running";
611 case PDMTHREADSTATE_TERMINATING:
612 return "terminating";
613 case PDMTHREADSTATE_TERMINATED:
614 return "terminated";
615 default:
616 return "unknown state";
617 }
618}
619#endif
620
621/**
622 * Wakeup the RX thread.
623 */
624static void virtioNetWakeupRxBufWaiter(PPDMDEVINS pDevIns)
625{
626 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
627
628 AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
629
630 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
631 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
632 {
633 Log10Func(("[%s] Waking downstream device's Rx buf waiter thread\n", pThis->szInst));
634 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
635 AssertRC(rc);
636 }
637}
638
639/**
640 * @callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
641 */
642static DECLCALLBACK(void) virtioNetVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
643{
644 RT_NOREF(pVirtio);
645 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
646
647 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
648 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
649
650#if defined (IN_RING3) && defined (LOG_ENABLED)
651 RTLogFlush(NULL);
652#endif
653 if (IS_RX_VIRTQ(uVirtqNbr))
654 {
655 uint16_t cBufsAvailable = virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr);
656
657 if (cBufsAvailable)
658 {
659 Log10Func(("%s %u empty bufs added to %s by guest (notifying leaf device)\n",
660 pThis->szInst, cBufsAvailable, pVirtq->szName));
661 virtioNetWakeupRxBufWaiter(pDevIns);
662 }
663 else
664 Log10Func(("%s \n\n***WARNING: %s notified but no empty bufs added by guest! (skip notifying of leaf device)\n\n",
665 pThis->szInst, pVirtq->szName));
666 }
667 else if (IS_TX_VIRTQ(uVirtqNbr) || IS_CTRL_VIRTQ(uVirtqNbr))
668 {
669 /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
670 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
671 {
672 if (ASMAtomicReadBool(&pWorker->fSleeping))
673 {
674 Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
675
676 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
677 AssertRC(rc);
678 }
679 else
680 {
681 Log10Func(("[%s] %s has available buffers - worker already awake\n", pThis->szInst, pVirtq->szName));
682 }
683 }
684 else
685 {
686 Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
687 }
688 }
689 else
690 LogRelFunc(("[%s] unrecognized queue %s (idx=%d) notified\n", pThis->szInst, pVirtq->szName, uVirtqNbr));
691}
692
693#ifdef IN_RING3 /* spans most of the file, at the moment. */
694
695/**
696 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
697 */
698static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
699{
700 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
701 PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
702
703 Log10Func(("[%s]\n", pThis->szInst));
704 RT_NOREF(pThis);
705 return PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
706}
707
708
709DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis)
710{
711 RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "controlq");
712 for (uint16_t qPairIdx = pThis->cInitializedVirtqPairs; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
713 {
714 RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "receiveq<%d>", qPairIdx);
715 RTStrPrintf(pThis->aVirtqs[TXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "transmitq<%d>", qPairIdx);
716 }
717}
718
719/**
720 * Dump a packet to debug log.
721 *
722 * @param pThis The virtio-net shared instance data.
723 * @param pbPacket The packet.
724 * @param cb The size of the packet.
725 * @param pszText A string denoting direction of packet transfer.
726 */
727DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
728{
729 if (!LogIs12Enabled())
730 return;
731
732 vboxEthPacketDump(pThis->szInst, pszText, pbPacket, (uint32_t)cb);
733}
734
735
736#ifdef LOG_ENABLED
737void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONETPKTHDR pRxPktHdr,
738 uint16_t cVirtqBufs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS GCPhysRxBuf, uint8_t cbRxBuf)
739{
740 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
741 pRxPktHdr->uNumBuffers = cVirtqBufs;
742 if (pRxPktHdr)
743 {
744 LogFunc(("-------------------------------------------------------------------\n"));
745 if (!virtioCoreIsLegacyMode(&pThis->Virtio) || FEATURE_ENABLED(MRG_RXBUF))
746 LogFunc(("rxPktHdr\n"
747 " uFlags ......... %2.2x\n uGsoType ....... %2.2x\n uHdrLen ........ %4.4x\n"
748 " uGsoSize ....... %4.4x\n uChksumStart ... %4.4x\n uChksumOffset .. %4.4x\n"
749 " uNumBuffers .... %4.4x\n",
750 pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
751 pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset, pRxPktHdr->uNumBuffers));
752 else
753 LogFunc(("rxPktHdr\n"
754 " uFlags ......... %2.2x\n uGsoType ....... %2.2x\n uHdrLen ........ %4.4x\n"
755 " uGsoSize ....... %4.4x\n uChksumStart ... %4.4x\n uChksumOffset .. %4.4x\n",
756 pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
757 pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset));
758 virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONETPKTHDR), 0, "Dump of virtual rPktHdr");
759 }
760 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
761 LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
762
763 virtioCoreGCPhysHexDump(pDevIns, GCPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
764 LogFunc(("-------------------------------------------------------------------\n"));
765}
766
767#endif /* LOG_ENABLED */
768
769/**
770 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
771 */
772static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
773{
774 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
775 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
776
777 bool fNone = pszArgs && *pszArgs == '\0';
778 bool fAll = pszArgs && (*pszArgs == 'a' || *pszArgs == 'A'); /* "all" */
779 bool fNetwork = pszArgs && (*pszArgs == 'n' || *pszArgs == 'N'); /* "network" */
780 bool fFeatures = pszArgs && (*pszArgs == 'f' || *pszArgs == 'F'); /* "features" */
781 bool fState = pszArgs && (*pszArgs == 's' || *pszArgs == 'S'); /* "state" */
782 bool fPointers = pszArgs && (*pszArgs == 'p' || *pszArgs == 'P'); /* "pointers" */
783 bool fVirtqs = pszArgs && (*pszArgs == 'q' || *pszArgs == 'Q'); /* "queues */
784
785 /* Show basic information. */
786 pHlp->pfnPrintf(pHlp,
787 "\n"
788 "---------------------------------------------------------------------------\n"
789 "Debug Info: %s\n"
790 " (options: [a]ll, [n]et, [f]eatures, [s]tate, [p]ointers, [q]ueues)\n"
791 "---------------------------------------------------------------------------\n\n",
792 pThis->szInst);
793
794 if (fNone)
795 return;
796
797 /* Show offered/unoffered, accepted/rejected features */
798 if (fAll || fFeatures)
799 {
800 virtioCorePrintDeviceFeatures(&pThis->Virtio, pHlp, s_aDevSpecificFeatures,
801 RT_ELEMENTS(s_aDevSpecificFeatures));
802 pHlp->pfnPrintf(pHlp, "\n");
803 }
804
805 /* Show queues (and associate worker info if applicable) */
806 if (fAll || fVirtqs)
807 {
808 pHlp->pfnPrintf(pHlp, "Virtq information:\n\n");
809 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
810 {
811 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
812
813 if (pVirtq->fHasWorker)
814 {
815 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
816 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
817
818 Assert((pWorker->uIdx == pVirtq->uIdx));
819 Assert((pWorkerR3->uIdx == pVirtq->uIdx));
820
821 if (pWorker->fAssigned)
822 {
823 pHlp->pfnPrintf(pHlp, " %-15s (pThread: %p %s) ",
824 pVirtq->szName,
825 pWorkerR3->pThread,
826 virtioNetThreadStateName(pWorkerR3->pThread));
827 if (pVirtq->fAttachedToVirtioCore)
828 {
829 pHlp->pfnPrintf(pHlp, "worker: ");
830 pHlp->pfnPrintf(pHlp, "%s", pWorker->fSleeping ? "blocking" : "unblocked");
831 pHlp->pfnPrintf(pHlp, "%s", pWorker->fNotified ? ", notified" : "");
832 }
833 else
834 if (pWorker->fNotified)
835 pHlp->pfnPrintf(pHlp, "not attached to virtio core");
836 }
837 }
838 else
839 {
840 pHlp->pfnPrintf(pHlp, " %-15s (INetworkDown's thread) %s", pVirtq->szName,
841 pVirtq->fAttachedToVirtioCore ? "" : "not attached to virtio core");
842 }
843 pHlp->pfnPrintf(pHlp, "\n");
844 virtioCoreR3VirtqInfo(pDevIns, pHlp, pszArgs, uVirtqNbr);
845 pHlp->pfnPrintf(pHlp, " ---------------------------------------------------------------------\n");
846 pHlp->pfnPrintf(pHlp, "\n");
847 }
848 pHlp->pfnPrintf(pHlp, "\n");
849 }
850
851 /* Show various pointers */
852 if (fAll || fPointers)
853 {
854 pHlp->pfnPrintf(pHlp, "Internal Pointers:\n\n");
855 pHlp->pfnPrintf(pHlp, " pDevIns ................... %p\n", pDevIns);
856 pHlp->pfnPrintf(pHlp, " PVIRTIONET ................ %p\n", pThis);
857 pHlp->pfnPrintf(pHlp, " PVIRTIONETCC .............. %p\n", pThisCC);
858 pHlp->pfnPrintf(pHlp, " pDrvBase .................. %p\n", pThisCC->pDrvBase);
859 pHlp->pfnPrintf(pHlp, " pDrv ...................... %p\n", pThisCC->pDrv);
860 pHlp->pfnPrintf(pHlp, " pDrv ...................... %p\n", pThisCC->pDrv);
861 pHlp->pfnPrintf(pHlp, "\n");
862 }
863
864 /* Show device state info */
865 if (fAll || fState)
866 {
867 pHlp->pfnPrintf(pHlp, "Device state:\n\n");
868 uint32_t fTransmitting = ASMAtomicReadU32(&pThis->uIsTransmitting);
869
870 pHlp->pfnPrintf(pHlp, " Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
871 pHlp->pfnPrintf(pHlp, "\n");
872 pHlp->pfnPrintf(pHlp, "Misc state\n");
873 pHlp->pfnPrintf(pHlp, "\n");
874 pHlp->pfnPrintf(pHlp, " fVirtioReady .............. %d\n", pThis->fVirtioReady);
875 pHlp->pfnPrintf(pHlp, " fGenUpdatePending ......... %d\n", pThis->Virtio.fGenUpdatePending);
876 pHlp->pfnPrintf(pHlp, " fMsiSupport ............... %d\n", pThis->Virtio.fMsiSupport);
877 pHlp->pfnPrintf(pHlp, " uConfigGeneration ......... %d\n", pThis->Virtio.uConfigGeneration);
878 pHlp->pfnPrintf(pHlp, " uDeviceStatus ............. 0x%x\n", pThis->Virtio.fDeviceStatus);
879 pHlp->pfnPrintf(pHlp, " cVirtqPairs .,............. %d\n", pThis->cVirtqPairs);
880 pHlp->pfnPrintf(pHlp, " cVirtqs .,................. %d\n", pThis->cVirtqs);
881 pHlp->pfnPrintf(pHlp, " cWorkers .................. %d\n", pThis->cWorkers);
882 pHlp->pfnPrintf(pHlp, " MMIO mapping name ......... %d\n", pThisCC->Virtio.pcszMmioName);
883 pHlp->pfnPrintf(pHlp, "\n");
884 }
885
886 /* Show network related information */
887 if (fAll || fNetwork)
888 {
889 pHlp->pfnPrintf(pHlp, "Network configuration:\n\n");
890 pHlp->pfnPrintf(pHlp, " MAC: ...................... %RTmac\n", &pThis->macConfigured);
891 pHlp->pfnPrintf(pHlp, "\n");
892 pHlp->pfnPrintf(pHlp, " Cable: .................... %s\n", pThis->fCableConnected ? "connected" : "disconnected");
893 pHlp->pfnPrintf(pHlp, " Link-up delay: ............ %d ms\n", pThis->cMsLinkUpDelay);
894 pHlp->pfnPrintf(pHlp, "\n");
895 pHlp->pfnPrintf(pHlp, " Accept all multicast: ..... %s\n", pThis->fAllMulticast ? "true" : "false");
896 pHlp->pfnPrintf(pHlp, " Suppress broadcast: ....... %s\n", pThis->fNoBroadcast ? "true" : "false");
897 pHlp->pfnPrintf(pHlp, " Suppress unicast: ......... %s\n", pThis->fNoUnicast ? "true" : "false");
898 pHlp->pfnPrintf(pHlp, " Suppress multicast: ....... %s\n", pThis->fNoMulticast ? "true" : "false");
899 pHlp->pfnPrintf(pHlp, " Promiscuous: .............. %s\n", pThis->fPromiscuous ? "true" : "false");
900 pHlp->pfnPrintf(pHlp, "\n");
901 pHlp->pfnPrintf(pHlp, " Default Rx MAC filter: .... %RTmac\n", pThis->rxFilterMacDefault);
902 pHlp->pfnPrintf(pHlp, "\n");
903
904 pHlp->pfnPrintf(pHlp, " Unicast filter MACs:\n");
905
906 if (!pThis->cUnicastFilterMacs)
907 pHlp->pfnPrintf(pHlp, " <none>\n");
908
909 for (uint32_t i = 0; i < pThis->cUnicastFilterMacs; i++)
910 pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacUnicastFilter[i]);
911
912 pHlp->pfnPrintf(pHlp, "\n Multicast filter MACs:\n");
913
914 if (!pThis->cMulticastFilterMacs)
915 pHlp->pfnPrintf(pHlp, " <none>\n");
916
917 for (uint32_t i = 0; i < pThis->cMulticastFilterMacs; i++)
918 pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacMulticastFilter[i]);
919
920 pHlp->pfnPrintf(pHlp, "\n\n");
921 pHlp->pfnPrintf(pHlp, " Leaf starved: ............. %s\n", pThis->fLeafWantsEmptyRxBufs ? "true" : "false");
922 pHlp->pfnPrintf(pHlp, "\n");
923 }
924 /** @todo implement this
925 * pHlp->pfnPrintf(pHlp, "\n");
926 * virtioCoreR3Info(pDevIns, pHlp, pszArgs);
927 */
928 pHlp->pfnPrintf(pHlp, "\n");
929}
930
931/*
932 * Checks whether negotiated features have required flag combinations.
933 * See VirtIO 1.0 specification, Section 5.1.3.1 */
934DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
935{
936 uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
937 || fFeatures & VIRTIONET_F_GUEST_TSO6
938 || fFeatures & VIRTIONET_F_GUEST_UFO;
939
940 uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
941 || fFeatures & VIRTIONET_F_HOST_TSO6
942 || fFeatures & VIRTIONET_F_HOST_UFO;
943
944 uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
945 || fFeatures & VIRTIONET_F_CTRL_VLAN
946 || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
947 || fFeatures & VIRTIONET_F_MQ
948 || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
949
950 if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
951 return false;
952
953 if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
954 return false;
955
956 if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
957 return false;
958
959 if ( fFeatures & VIRTIONET_F_GUEST_ECN
960 && !( fFeatures & VIRTIONET_F_GUEST_TSO4
961 || fFeatures & VIRTIONET_F_GUEST_TSO6))
962 return false;
963
964 if ( fFeatures & VIRTIONET_F_HOST_ECN
965 && !( fFeatures & VIRTIONET_F_HOST_TSO4
966 || fFeatures & VIRTIONET_F_HOST_TSO6))
967 return false;
968 return true;
969}
970
971static int virtioNetR3DevCfgAccess(PVIRTIONET pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
972{
973 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
974
975 if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess))
976 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
977#if FEATURE_OFFERED(STATUS)
978 else
979 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess))
980 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
981#endif
982#if FEATURE_OFFERED(MQ)
983 else
984 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess))
985 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
986#endif
987 else
988 {
989 LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n",
990 pThis->szInst, uOffsetOfAccess, uOffsetOfAccess, cb));
991 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
992 }
993 return VINF_SUCCESS;
994}
995
996/**
997 * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
998 */
999static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
1000{
1001 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1002
1003 RT_NOREF(pThis);
1004 return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
1005}
1006
1007/**
1008 * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
1009 */
1010static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1011{
1012 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1013
1014 Log10Func(("[%s] uOffset: %d, cb: %d: %.*Rhxs\n", pThis->szInst, uOffset, cb, RT_MAX(cb, 8) , pv));
1015 RT_NOREF(pThis);
1016 return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
1017}
1018
1019
1020/*********************************************************************************************************************************
1021* Saved state *
1022*********************************************************************************************************************************/
1023
1024/**
1025 * @callback_method_impl{FNSSMDEVLOADEXEC}
1026 */
1027static DECLCALLBACK(int) virtioNetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1028{
1029 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1030 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1031 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1032
1033 RT_NOREF(pThisCC);
1034 Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
1035
1036 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
1037 AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVED_STATE_VERSION,
1038 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1039
1040 virtioNetR3SetVirtqNames(pThis);
1041
1042 pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
1043
1044 pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtqs);
1045 AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2), VERR_OUT_OF_RANGE);
1046
1047 pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
1048 AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS, VERR_OUT_OF_RANGE);
1049
1050
1051 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1052 pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1053
1054 int rc;
1055
1056 if (uPass == SSM_PASS_FINAL)
1057 {
1058
1059 /* Load config area */
1060#if FEATURE_OFFERED(STATUS)
1061 /* config checks */
1062 RTMAC macConfigured;
1063 rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
1064 AssertRCReturn(rc, rc);
1065 if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
1066 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1067 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
1068 pThis->szInst, &pThis->macConfigured, &macConfigured));
1069#endif
1070
1071#if FEATURE_OFFERED(MQ)
1072 pHlp->pfnSSMGetU16( pSSM, &pThis->virtioNetConfig.uMaxVirtqPairs);
1073#endif
1074 /* Save device-specific part */
1075 pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
1076 pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
1077 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
1078 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
1079 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
1080 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
1081 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
1082
1083 pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
1084 AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1085 pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1086
1087 if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1088 memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
1089 (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
1090
1091 pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
1092 AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1093 pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1094
1095 if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1096 memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
1097 (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
1098
1099 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1100 AssertRCReturn(rc, rc);
1101 }
1102
1103 /*
1104 * Call the virtio core to let it load its state.
1105 */
1106 rc = virtioCoreR3LoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
1107
1108 /*
1109 * Nudge queue workers
1110 */
1111 for (int uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
1112 {
1113 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uIdxWorker];
1114 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uIdxWorker];
1115 if (pVirtq->fAttachedToVirtioCore)
1116 {
1117 Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
1118 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
1119 AssertRCReturn(rc, rc);
1120 }
1121 }
1122 return rc;
1123}
1124
1125/**
1126 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1127 */
1128static DECLCALLBACK(int) virtioNetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1129{
1130 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1131 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1132 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1133
1134 RT_NOREF(pThisCC);
1135 Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
1136
1137 pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
1138
1139 pHlp->pfnSSMPutU16( pSSM, pThis->cVirtqs);
1140 pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
1141
1142 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1143 pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1144
1145 /* Save config area */
1146#if FEATURE_OFFERED(STATUS)
1147 pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
1148 sizeof(pThis->virtioNetConfig.uMacAddress.au8));
1149#endif
1150#if FEATURE_OFFERED(MQ)
1151 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
1152#endif
1153
1154 /* Save device-specific part */
1155 pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
1156 pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
1157 pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
1158 pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
1159 pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
1160 pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
1161 pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
1162
1163 pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
1164 pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1165
1166 pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
1167 pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1168
1169 int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1170 AssertRCReturn(rc, rc);
1171
1172 /*
1173 * Call the virtio core to let it save its state.
1174 */
1175 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
1176}
1177
1178
1179/*********************************************************************************************************************************
1180* Device interface. *
1181*********************************************************************************************************************************/
1182
1183#ifdef IN_RING3
1184
1185DECLINLINE(uint16_t) virtioNetR3Checkum16(const void *pvBuf, size_t cb)
1186{
1187 uint32_t chksum = 0;
1188 uint16_t *pu = (uint16_t *)pvBuf;
1189
1190 while (cb > 1)
1191 {
1192 chksum += *pu++;
1193 cb -= 2;
1194 }
1195 if (cb)
1196 chksum += *(uint8_t*)pu;
1197 while (chksum >> 16)
1198 chksum = (chksum >> 16) + (chksum & 0xFFFF);
1199 return ~chksum;
1200}
1201
1202DECLINLINE(void) virtioNetR3CompleteChecksum(uint8_t *pBuf, size_t cbSize, uint16_t uStart, uint16_t uOffset)
1203{
1204 AssertReturnVoid(uStart < cbSize);
1205 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cbSize);
1206 *(uint16_t *)(pBuf + uStart + uOffset) = virtioNetR3Checkum16(pBuf + uStart, cbSize - uStart);
1207}
1208
1209/**
1210 * Turns on/off the read status LED.
1211 *
1212 * @returns VBox status code.
1213 * @param pThis Pointer to the device state structure.
1214 * @param fOn New LED state.
1215 */
1216void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
1217{
1218 if (fOn)
1219 pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
1220 else
1221 pThisR3->led.Actual.s.fReading = fOn;
1222}
1223
1224/**
1225 * Turns on/off the write status LED.
1226 *
1227 * @returns VBox status code.
1228 * @param pThis Pointer to the device state structure.
1229 * @param fOn New LED state.
1230 */
1231void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
1232{
1233 if (fOn)
1234 pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
1235 else
1236 pThisR3->led.Actual.s.fWriting = fOn;
1237}
1238
1239/*
1240 * Returns true if VirtIO core and device are in a running and operational state
1241 */
1242DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
1243{
1244 if (RT_LIKELY(pThis->fVirtioReady))
1245 {
1246 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1247 if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
1248 return true;
1249 }
1250 return false;
1251}
1252
1253/**
1254 * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
1255 * available. This must be called before the pfnRecieve() method is called.
1256 *
1257 * @remarks As a side effect this function enables queue notification
1258 * if it cannot receive because the queue is empty.
1259 * It disables notification if it can receive.
1260 *
1261 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1262 * @thread RX
1263 */
1264static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
1265{
1266 int rc = VERR_INVALID_STATE;
1267
1268 if (!virtioNetIsOperational(pThis, pDevIns))
1269 Log8Func(("[%s] No Rx bufs available. (VirtIO core not ready)\n", pThis->szInst));
1270
1271 else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
1272 Log8Func(("[%s] No Rx bufs available. (%s not enabled)\n", pThis->szInst, pRxVirtq->szName));
1273
1274 else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->uIdx))
1275 Log8Func(("[%s] No Rx bufs available. (%s empty)\n", pThis->szInst, pRxVirtq->szName));
1276
1277 else
1278 {
1279 Log8Func(("[%s] %s has empty guest bufs avail\n", pThis->szInst, pRxVirtq->szName));
1280 rc = VINF_SUCCESS;
1281 }
1282 virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
1283 return rc;
1284}
1285
1286static bool virtioNetR3RxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
1287{
1288 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
1289 {
1290 PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
1291 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
1292 {
1293 if (pRxVirtq)
1294 *pRxVirtq = pThisRxVirtq;
1295 return true;
1296 }
1297 }
1298 return false;
1299}
1300
1301/**
1302 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
1303 */
1304static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
1305{
1306 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1307 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1308 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1309
1310 if (!virtioNetIsOperational(pThis, pDevIns))
1311 return VERR_INTERRUPTED;
1312
1313 if (virtioNetR3RxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1314 {
1315 Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
1316 return VINF_SUCCESS;
1317 }
1318 if (!timeoutMs)
1319 return VERR_NET_NO_BUFFER_SPACE;
1320
1321 LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
1322
1323
1324 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
1325 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
1326
1327 do {
1328 if (virtioNetR3RxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1329 {
1330 Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
1331 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1332 return VINF_SUCCESS;
1333 }
1334 Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
1335
1336 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
1337
1338 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
1339 {
1340 LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
1341
1342 if (!virtioNetIsOperational(pThis, pDevIns))
1343 break;
1344
1345 continue;
1346 }
1347 if (RT_FAILURE(rc)) {
1348 LogFunc(("Waken due to failure %Rrc\n", rc));
1349 RTThreadSleep(1);
1350 }
1351 } while (virtioNetIsOperational(pThis, pDevIns));
1352
1353 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
1354 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1355
1356 Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
1357 return VERR_INTERRUPTED;
1358}
1359
1360
1361/**
1362 * Sets up the GSO context according to the Virtio header.
1363 *
1364 * @param pGso The GSO context to setup.
1365 * @param pCtx The context descriptor.
1366 */
1367DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
1368{
1369 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1370
1371 if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
1372 {
1373 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
1374 return NULL;
1375 }
1376 switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
1377 {
1378 case VIRTIONET_HDR_GSO_TCPV4:
1379 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
1380 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1381 break;
1382 case VIRTIONET_HDR_GSO_TCPV6:
1383 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
1384 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1385 break;
1386 case VIRTIONET_HDR_GSO_UDP:
1387 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
1388 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
1389 break;
1390 default:
1391 return NULL;
1392 }
1393 if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
1394 pGso->offHdr2 = pPktHdr->uChksumStart;
1395 else
1396 {
1397 AssertMsgFailed(("GSO without checksum offloading!\n"));
1398 return NULL;
1399 }
1400 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1401 pGso->cbHdrsTotal = pPktHdr->uHdrLen;
1402 pGso->cbMaxSeg = pPktHdr->uGsoSize;
1403 return pGso;
1404}
1405
1406/**
1407 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
1408 */
1409static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1410{
1411 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
1412 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
1413 memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
1414 return VINF_SUCCESS;
1415}
1416
1417/**
1418 * Returns true if it is a broadcast packet.
1419 *
1420 * @returns true if destination address indicates broadcast.
1421 * @param pvBuf The ethernet packet.
1422 */
1423DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
1424{
1425 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1426 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
1427}
1428
1429/**
1430 * Returns true if it is a multicast packet.
1431 *
1432 * @remarks returns true for broadcast packets as well.
1433 * @returns true if destination address indicates multicast.
1434 * @param pvBuf The ethernet packet.
1435 */
1436DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
1437{
1438 return (*(char*)pvBuf) & 1;
1439}
1440/**
1441 * Determines if the packet is to be delivered to upper layer.
1442 *
1443 * @returns true if packet is intended for this node.
1444 * @param pThis Pointer to the state structure.
1445 * @param pvBuf The ethernet packet.
1446 * @param cb Number of bytes available in the packet.
1447 */
1448static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
1449{
1450
1451 if (LogIs11Enabled())
1452 {
1453 char *pszType;
1454 if (virtioNetR3IsMulticast(pvBuf))
1455 pszType = (char *)"mcast";
1456 else if (virtioNetR3IsBroadcast(pvBuf))
1457 pszType = (char *)"bcast";
1458 else
1459 pszType = (char *)"ucast";
1460
1461 LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
1462 pThis->virtioNetConfig.uMacAddress.au8,
1463 pThis->fPromiscuous ? " promisc" : "",
1464 pThis->fAllMulticast ? " all-mcast" : "",
1465 pvBuf, pszType));
1466 }
1467
1468 if (pThis->fPromiscuous) {
1469 Log11(("\n"));
1470 return true;
1471 }
1472
1473 /* Ignore everything outside of our VLANs */
1474 uint16_t *uPtr = (uint16_t *)pvBuf;
1475
1476 /* Compare TPID with VLAN Ether Type */
1477 if ( uPtr[6] == RT_H2BE_U16(0x8100)
1478 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
1479 {
1480 Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
1481 return false;
1482 }
1483
1484 if (virtioNetR3IsBroadcast(pvBuf))
1485 {
1486 Log11(("acpt (bcast)\n"));
1487 if (LogIs12Enabled())
1488 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1489 return true;
1490 }
1491 if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
1492 {
1493 Log11(("acpt (all-mcast)\n"));
1494 if (LogIs12Enabled())
1495 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1496 return true;
1497 }
1498
1499 if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
1500 {
1501 Log11(("acpt (to-node)\n"));
1502 if (LogIs12Enabled())
1503 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1504 return true;
1505 }
1506
1507 for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
1508 {
1509 if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
1510 {
1511 Log11(("acpt (mcast whitelist)\n"));
1512 if (LogIs12Enabled())
1513 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1514 return true;
1515 }
1516 }
1517
1518 for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
1519 if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
1520 {
1521 Log11(("acpt (ucast whitelist)\n"));
1522 return true;
1523 }
1524
1525 if (LogIs11Enabled())
1526 Log(("... reject\n"));
1527
1528 return false;
1529}
1530
1531static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, const void *pvBuf, size_t cb,
1532 PVIRTIONETPKTHDR rxPktHdr, uint16_t cSegsAllocated,
1533 PRTSGBUF pVirtSegBufToGuest, PRTSGSEG paVirtSegsToGuest,
1534 PVIRTIONETVIRTQ pRxVirtq)
1535{
1536 RTGCPHYS GCPhysPktHdrNumBuffers = 0;
1537 uint8_t fAddPktHdr = true;
1538 uint16_t cVirtqBufs = 0;
1539 uint64_t uOffset = 0;
1540
1541 while (uOffset < cb)
1542 {
1543 PVIRTQBUF pVirtqBuf = NULL;
1544
1545 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
1546 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
1547
1548 /** @todo Find a better way to deal with this */
1549 AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
1550 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
1551 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
1552 VERR_INTERNAL_ERROR);
1553
1554 /* Length of first seg of guest Rx S/G buf should never be less than sthe packet header.
1555 * Otherwise code has to become more complicated, e.g. locate & cache seg idx & offset of
1556 * virtio_net_header.num_buffers, to facilitate deferring updating GCPhys memory.
1557 * Re-visit if needed */
1558
1559 AssertMsgReturn(pVirtqBuf->pSgPhysReturn->paSegs[0].cbSeg >= pThis->cbPktHdr,
1560 ("Out of Memory"), VERR_NO_MEMORY);
1561
1562 size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
1563
1564
1565 /* Fill the Guest Rx buffer with data received from the interface */
1566 for (uint16_t cSegs = 0; uOffset < cb && cbBufRemaining; )
1567 {
1568 if (fAddPktHdr)
1569 {
1570 /* Lead with packet header */
1571 paVirtSegsToGuest[0].cbSeg = pThis->cbPktHdr;
1572 paVirtSegsToGuest[0].pvSeg = RTMemAlloc(pThis->cbPktHdr);
1573 AssertReturn(paVirtSegsToGuest[0].pvSeg, VERR_NO_MEMORY);
1574 cbBufRemaining -= pThis->cbPktHdr;
1575
1576 memcpy(paVirtSegsToGuest[0].pvSeg, rxPktHdr, pThis->cbPktHdr);
1577
1578 if (pThis->ePktHdrType != kVirtioNetLegacyPktHdrWithoutMrgRx)
1579 {
1580 /* Calculate & cache GCPhys addr of field to update after final value is known */
1581 GCPhysPktHdrNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys
1582 + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
1583 }
1584 fAddPktHdr = false;
1585 cSegs++;
1586 }
1587
1588 if (cSegs >= cSegsAllocated)
1589 {
1590 cSegsAllocated <<= 1; /* double allocation size */
1591 paVirtSegsToGuest = (PRTSGSEG)RTMemRealloc(paVirtSegsToGuest, sizeof(RTSGSEG) * cSegsAllocated);
1592 if (!paVirtSegsToGuest)
1593 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
1594 AssertReturn(paVirtSegsToGuest, VERR_NO_MEMORY);
1595 }
1596
1597 /* Append remaining Rx pkt or as much current desc chain has room for */
1598 size_t cbLimited = RT_MIN(cb, cbBufRemaining);
1599 paVirtSegsToGuest[cSegs].cbSeg = cbLimited;
1600 paVirtSegsToGuest[cSegs].pvSeg = ((uint8_t *)pvBuf) + uOffset;
1601 cbBufRemaining -= cbLimited;
1602 uOffset += cbLimited;
1603 cVirtqBufs++;
1604 cSegs++;
1605 RTSgBufInit(pVirtSegBufToGuest, paVirtSegsToGuest, cSegs);
1606 Log7Func(("Send Rx pkt to guest...\n"));
1607 STAM_PROFILE_START(&pThis->StatReceiveStore, a);
1608 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx,
1609 pVirtSegBufToGuest, pVirtqBuf, true /* fFence */);
1610 STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
1611
1612 if (FEATURE_DISABLED(MRG_RXBUF))
1613 break;
1614 }
1615 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
1616 }
1617
1618 if (uOffset < cb)
1619 {
1620 LogFunc(("[%s] Packet did not fit into RX queue (packet size=%u)!\n", pThis->szInst, cb));
1621 return VERR_TOO_MUCH_DATA;
1622 }
1623
1624
1625 if (pThis->ePktHdrType != kVirtioNetLegacyPktHdrWithoutMrgRx)
1626 {
1627 /* Fix-up pkthdr (in guest phys. memory) with number buffers (descriptors) processed */
1628 int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysPktHdrNumBuffers, &cVirtqBufs, sizeof(cVirtqBufs));
1629 AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
1630 }
1631
1632 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
1633
1634 return VINF_SUCCESS;
1635}
1636
1637
1638/**
1639 * Pad and store received packet.
1640 *
1641 * @remarks Make sure that the packet appears to upper layer as one coming
1642 * from real Ethernet: pad it and insert FCS.
1643 *
1644 * @returns VBox status code.
1645 * @param pDevIns The device instance.
1646 * @param pThis The virtio-net shared instance data.
1647 * @param pvBuf The available data.
1648 * @param cb Number of bytes available in the buffer.
1649 * @param pGso Pointer to Global Segmentation Offload structure
1650 * @param pRxVirtq Pointer to Rx virtqueue
1651 * @thread RX
1652 */
1653static int virtioNetR3HandleRxPacket(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1654 const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso, PVIRTIONETVIRTQ pRxVirtq)
1655{
1656 RT_NOREF(pThisCC);
1657
1658 LogFunc(("[%s] (%RTmac) pGso %s\n", pThis->szInst, pvBuf, pGso ? "present" : "not present"));
1659 VIRTIONETPKTHDR rxPktHdr;
1660
1661 if (pGso)
1662 {
1663 Log2Func(("[%s] gso type=%x cbPktHdrsTotal=%u cbPktHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
1664 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal,
1665 pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1666
1667 rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
1668 switch (pGso->u8Type)
1669 {
1670 case PDMNETWORKGSOTYPE_IPV4_TCP:
1671 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
1672 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1673 break;
1674 case PDMNETWORKGSOTYPE_IPV6_TCP:
1675 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
1676 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1677 break;
1678 case PDMNETWORKGSOTYPE_IPV4_UDP:
1679 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
1680 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
1681 break;
1682 default:
1683 return VERR_INVALID_PARAMETER;
1684 }
1685 rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
1686 rxPktHdr.uGsoSize = pGso->cbMaxSeg;
1687 rxPktHdr.uChksumStart = pGso->offHdr2;
1688 rxPktHdr.uNumBuffers = 0;
1689 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
1690 }
1691 else
1692 {
1693 rxPktHdr.uFlags = 0;
1694 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_NONE;
1695 rxPktHdr.uHdrLen = 0;
1696 rxPktHdr.uGsoSize = 0;
1697 rxPktHdr.uChksumStart = 0;
1698 rxPktHdr.uChksumOffset = 0;
1699 rxPktHdr.uNumBuffers = 0;
1700 }
1701
1702 uint16_t cSegsAllocated = VIRTIONET_RX_SEG_COUNT;
1703
1704 PRTSGBUF pVirtSegBufToGuest = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
1705 AssertReturn(pVirtSegBufToGuest, VERR_NO_MEMORY);
1706
1707 PRTSGSEG paVirtSegsToGuest = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * cSegsAllocated);
1708 AssertReturnStmt(paVirtSegsToGuest, RTMemFree(pVirtSegBufToGuest), VERR_NO_MEMORY);
1709
1710 int rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pvBuf, cb, &rxPktHdr, cSegsAllocated,
1711 pVirtSegBufToGuest, paVirtSegsToGuest, pRxVirtq);
1712
1713 RTMemFree(paVirtSegsToGuest);
1714 RTMemFree(pVirtSegBufToGuest);
1715
1716 Log7(("\n"));
1717 return rc;
1718}
1719
1720/**
1721 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
1722 */
1723static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf,
1724 size_t cb, PCPDMNETWORKGSO pGso)
1725{
1726 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1727 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1728 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1729
1730 if (!pThis->fVirtioReady)
1731 {
1732 LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
1733 return VERR_INTERRUPTED;
1734 }
1735
1736 if (pGso)
1737 {
1738 uint32_t uFeatures = pThis->fNegotiatedFeatures;
1739
1740 switch (pGso->u8Type)
1741 {
1742 case PDMNETWORKGSOTYPE_IPV4_TCP:
1743 uFeatures &= VIRTIONET_F_GUEST_TSO4;
1744 break;
1745 case PDMNETWORKGSOTYPE_IPV6_TCP:
1746 uFeatures &= VIRTIONET_F_GUEST_TSO6;
1747 break;
1748 case PDMNETWORKGSOTYPE_IPV4_UDP:
1749 case PDMNETWORKGSOTYPE_IPV6_UDP:
1750 uFeatures &= VIRTIONET_F_GUEST_UFO;
1751 break;
1752 default:
1753 uFeatures = 0;
1754 break;
1755 }
1756 if (!uFeatures)
1757 {
1758 LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
1759 return VERR_NOT_SUPPORTED;
1760 }
1761 }
1762
1763 Log10Func(("[%s] pvBuf=%p cb=%3u pGso=%p ...\n", pThis->szInst, pvBuf, cb, pGso));
1764
1765 /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
1766 selection algorithm feasible or even necessary to prevent starvation? */
1767
1768 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
1769 {
1770
1771 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
1772 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
1773 {
1774 STAM_PROFILE_START(&pThis->StatReceive, a);
1775 virtioNetR3SetReadLed(pThisCC, true);
1776
1777 int rc = VINF_SUCCESS;
1778 if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
1779 {
1780 rc = virtioNetR3HandleRxPacket(pDevIns, pThis, pThisCC, pvBuf, cb, pGso, pRxVirtq);
1781 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
1782 }
1783
1784 virtioNetR3SetReadLed(pThisCC, false);
1785 STAM_PROFILE_STOP(&pThis->StatReceive, a);
1786 return rc;
1787 }
1788 }
1789 return VERR_INTERRUPTED;
1790}
1791
1792/**
1793 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
1794 */
1795static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
1796{
1797 return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
1798}
1799
1800
1801static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1802 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
1803{
1804
1805#define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
1806
1807 LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
1808 switch(pCtrlPktHdr->uCmd)
1809 {
1810 case VIRTIONET_CTRL_RX_PROMISC:
1811 break;
1812 case VIRTIONET_CTRL_RX_ALLMULTI:
1813 break;
1814 case VIRTIONET_CTRL_RX_ALLUNI:
1815 /* fallthrough */
1816 case VIRTIONET_CTRL_RX_NOMULTI:
1817 /* fallthrough */
1818 case VIRTIONET_CTRL_RX_NOUNI:
1819 /* fallthrough */
1820 case VIRTIONET_CTRL_RX_NOBCAST:
1821 AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
1822 ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
1823 VIRTIONET_ERROR);
1824 /* fall out */
1825 }
1826
1827 uint8_t fOn, fPromiscChanged = false;
1828 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
1829
1830 switch(pCtrlPktHdr->uCmd)
1831 {
1832 case VIRTIONET_CTRL_RX_PROMISC:
1833 pThis->fPromiscuous = RT_BOOL(fOn);
1834 fPromiscChanged = true;
1835 LOG_VIRTIONET_FLAG(fPromiscuous);
1836 break;
1837 case VIRTIONET_CTRL_RX_ALLMULTI:
1838 pThis->fAllMulticast = RT_BOOL(fOn);
1839 fPromiscChanged = true;
1840 LOG_VIRTIONET_FLAG(fAllMulticast);
1841 break;
1842 case VIRTIONET_CTRL_RX_ALLUNI:
1843 pThis->fAllUnicast = RT_BOOL(fOn);
1844 LOG_VIRTIONET_FLAG(fAllUnicast);
1845 break;
1846 case VIRTIONET_CTRL_RX_NOMULTI:
1847 pThis->fNoMulticast = RT_BOOL(fOn);
1848 LOG_VIRTIONET_FLAG(fNoMulticast);
1849 break;
1850 case VIRTIONET_CTRL_RX_NOUNI:
1851 pThis->fNoUnicast = RT_BOOL(fOn);
1852 LOG_VIRTIONET_FLAG(fNoUnicast);
1853 break;
1854 case VIRTIONET_CTRL_RX_NOBCAST:
1855 pThis->fNoBroadcast = RT_BOOL(fOn);
1856 LOG_VIRTIONET_FLAG(fNoBroadcast);
1857 break;
1858 }
1859
1860 if (pThisCC->pDrv && fPromiscChanged)
1861 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
1862
1863 return VIRTIONET_OK;
1864}
1865
1866static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
1867{
1868 LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
1869
1870
1871 AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
1872 ("insufficient descriptor space for ctrl pkt hdr"),
1873 VIRTIONET_ERROR);
1874
1875 size_t cbRemaining = pVirtqBuf->cbPhysSend;
1876 switch(pCtrlPktHdr->uCmd)
1877 {
1878 case VIRTIONET_CTRL_MAC_ADDR_SET:
1879 {
1880 /* Set default Rx filter MAC */
1881 AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
1882 ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
1883
1884 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(VIRTIONET_CTRL_MAC_TABLE_LEN));
1885 break;
1886 }
1887 case VIRTIONET_CTRL_MAC_TABLE_SET:
1888 {
1889 VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
1890
1891 /* Load unicast MAC filter table */
1892
1893 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
1894 ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
1895
1896 /* Fetch count of unicast filter MACs from guest buffer */
1897 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
1898
1899 cbRemaining -= sizeof(cMacs);
1900
1901 Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
1902
1903 if (cMacs)
1904 {
1905 uint32_t cbMacs = cMacs * sizeof(RTMAC);
1906
1907 AssertMsgReturn(cbRemaining >= cbMacs,
1908 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
1909
1910 /* Fetch unicast table contents from guest buffer */
1911 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
1912
1913 cbRemaining -= cbMacs;
1914 }
1915 pThis->cUnicastFilterMacs = cMacs;
1916
1917 /* Load multicast MAC filter table */
1918 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
1919 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
1920
1921 /* Fetch count of multicast filter MACs from guest buffer */
1922 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
1923
1924 cbRemaining -= sizeof(cMacs);
1925
1926 Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
1927
1928
1929 if (cMacs)
1930 {
1931 uint32_t cbMacs = cMacs * sizeof(RTMAC);
1932
1933 AssertMsgReturn(cbRemaining >= cbMacs,
1934 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
1935
1936 /* Fetch multicast table contents from guest buffer */
1937 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
1938
1939 cbRemaining -= cbMacs;
1940 }
1941 pThis->cMulticastFilterMacs = cMacs;
1942
1943#ifdef LOG_ENABLED
1944 LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
1945 for(unsigned i = 0; i < cMacs; i++)
1946 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
1947
1948 LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
1949 for(unsigned i = 0; i < cMacs; i++)
1950 LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
1951#endif
1952 break;
1953 }
1954 default:
1955 LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
1956 return VIRTIONET_ERROR;
1957 }
1958 return VIRTIONET_OK;
1959}
1960
1961static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
1962{
1963 LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
1964
1965 uint16_t cVirtqPairs;
1966 switch(pCtrlPktHdr->uCmd)
1967 {
1968 case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
1969 {
1970 size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
1971
1972 AssertMsgReturn(cbRemaining > sizeof(cVirtqPairs),
1973 ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
1974
1975 /* Fetch number of virtq pairs from guest buffer */
1976 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
1977
1978 AssertMsgReturn(cVirtqPairs > VIRTIONET_MAX_QPAIRS,
1979 ("[%s] Guest CTRL MQ virtq pair count out of range)\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
1980
1981 LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
1982 pThis->cVirtqPairs = cVirtqPairs;
1983 break;
1984 }
1985 default:
1986 LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
1987 return VIRTIONET_ERROR;
1988 }
1989
1990 /*
1991 * The MQ control function is invoked by the guest in an RPC like manner to change
1992 * the Rx/Tx queue pair count. If the new value exceeds the number of queues
1993 * (and associated workers) already initialized initialize only the new queues and
1994 * respective workers.
1995 */
1996 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
1997 {
1998 virtioNetR3SetVirtqNames(pThis);
1999 int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
2000 if (RT_FAILURE(rc))
2001 {
2002 LogRelFunc(("Failed to create worker threads\n"));
2003 return VIRTIONET_ERROR;
2004 }
2005 }
2006 return VIRTIONET_OK;
2007}
2008
2009static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2010{
2011 LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
2012
2013 uint16_t uVlanId;
2014 size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
2015
2016 AssertMsgReturn(cbRemaining > sizeof(uVlanId),
2017 ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
2018
2019 /* Fetch VLAN ID from guest buffer */
2020 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
2021
2022 AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
2023 ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
2024
2025 LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
2026
2027 switch (pCtrlPktHdr->uCmd)
2028 {
2029 case VIRTIONET_CTRL_VLAN_ADD:
2030 ASMBitSet(pThis->aVlanFilter, uVlanId);
2031 break;
2032 case VIRTIONET_CTRL_VLAN_DEL:
2033 ASMBitClear(pThis->aVlanFilter, uVlanId);
2034 break;
2035 default:
2036 LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
2037 return VIRTIONET_ERROR;
2038 }
2039 return VIRTIONET_OK;
2040}
2041
2042static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2043 PVIRTQBUF pVirtqBuf)
2044{
2045 LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
2046
2047 if (pVirtqBuf->cbPhysSend < 2)
2048 {
2049 LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
2050 return;
2051 }
2052 else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
2053 {
2054 LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
2055 return;
2056 }
2057
2058 /*
2059 * Allocate buffer and read in the control command
2060 */
2061 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr = (PVIRTIONET_CTRL_HDR_T)RTMemAllocZ(sizeof(VIRTIONET_CTRL_HDR_T));
2062
2063 AssertPtrReturnVoid(pCtrlPktHdr);
2064
2065 AssertMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(VIRTIONET_CTRL_HDR_T),
2066 ("DESC chain too small for CTRL pkt header"));
2067
2068 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, pCtrlPktHdr,
2069 RT_MIN(pVirtqBuf->cbPhysSend, sizeof(VIRTIONET_CTRL_HDR_T)));
2070
2071 Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, pCtrlPktHdr->uClass, pCtrlPktHdr->uCmd));
2072
2073 uint8_t uAck;
2074 switch (pCtrlPktHdr->uClass)
2075 {
2076 case VIRTIONET_CTRL_RX:
2077 uAck = virtioNetR3CtrlRx(pThis, pThisCC, pCtrlPktHdr, pVirtqBuf);
2078 break;
2079 case VIRTIONET_CTRL_MAC:
2080 uAck = virtioNetR3CtrlMac(pThis, pCtrlPktHdr, pVirtqBuf);
2081 break;
2082 case VIRTIONET_CTRL_VLAN:
2083 uAck = virtioNetR3CtrlVlan(pThis, pCtrlPktHdr, pVirtqBuf);
2084 break;
2085 case VIRTIONET_CTRL_MQ:
2086 uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, pCtrlPktHdr, pVirtqBuf);
2087 break;
2088 case VIRTIONET_CTRL_ANNOUNCE:
2089 uAck = VIRTIONET_OK;
2090 if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
2091 {
2092 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
2093 "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
2094 break;
2095 }
2096 if (pCtrlPktHdr->uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
2097 {
2098 LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
2099 break;
2100 }
2101 pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
2102 Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
2103 break;
2104 default:
2105 LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", pCtrlPktHdr->uClass));
2106 uAck = VIRTIONET_ERROR;
2107 }
2108
2109 /* Currently CTRL pkt header just returns ack, but keeping segment logic generic/flexible
2110 * in case that changes to make adapting more straightforward */
2111 int cSegs = 1;
2112
2113 /* Return CTRL packet Ack byte (result code) to guest driver */
2114 PRTSGSEG paReturnSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG));
2115 AssertMsgReturnVoid(paReturnSegs, ("Out of memory"));
2116
2117 RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
2118 memcpy(paReturnSegs, aStaticSegs, sizeof(RTSGSEG));
2119
2120 PRTSGBUF pReturnSegBuf = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
2121 AssertMsgReturnVoid(pReturnSegBuf, ("Out of memory"));
2122
2123 /* Copy segment data to malloc'd memory to avoid stack out-of-scope errors sanitizer doesn't detect */
2124 for (int i = 0; i < cSegs; i++)
2125 {
2126 void *pv = paReturnSegs[i].pvSeg;
2127 paReturnSegs[i].pvSeg = RTMemAlloc(aStaticSegs[i].cbSeg);
2128 AssertMsgReturnVoid(paReturnSegs[i].pvSeg, ("Out of memory"));
2129 memcpy(paReturnSegs[i].pvSeg, pv, aStaticSegs[i].cbSeg);
2130 }
2131
2132 RTSgBufInit(pReturnSegBuf, paReturnSegs, cSegs);
2133
2134 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, pReturnSegBuf, pVirtqBuf, true /* fFence */);
2135 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
2136
2137 for (int i = 0; i < cSegs; i++)
2138 RTMemFree(paReturnSegs[i].pvSeg);
2139
2140 RTMemFree(paReturnSegs);
2141 RTMemFree(pReturnSegBuf);
2142
2143 LogFunc(("%s Finished processing CTRL command with status %s\n",
2144 pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
2145}
2146
2147static int virtioNetR3ReadHeader(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
2148{
2149 int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
2150 if (RT_FAILURE(rc))
2151 return rc;
2152
2153 LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
2154 pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
2155 pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
2156
2157 if (pPktHdr->uGsoType)
2158 {
2159 uint32_t uMinHdrSize;
2160
2161 /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
2162 AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2163 && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
2164 ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
2165
2166 switch (pPktHdr->uGsoType)
2167 {
2168 case VIRTIONET_HDR_GSO_TCPV4:
2169 case VIRTIONET_HDR_GSO_TCPV6:
2170 uMinHdrSize = sizeof(RTNETTCP);
2171 break;
2172 case VIRTIONET_HDR_GSO_UDP:
2173 uMinHdrSize = 0;
2174 break;
2175 default:
2176 LogFunc(("Bad GSO type in packet header\n"));
2177 return VERR_INVALID_PARAMETER;
2178 }
2179 /* Header + MSS must not exceed the packet size. */
2180 AssertMsgReturn(RT_LIKELY(uMinHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
2181 ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
2182 }
2183
2184 AssertMsgReturn( !pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM
2185 || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
2186 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
2187 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
2188 VERR_BUFFER_OVERFLOW);
2189
2190 return VINF_SUCCESS;
2191}
2192
2193static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
2194 PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
2195{
2196 virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
2197 if (pGso)
2198 {
2199 /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
2200 /*
2201 * We cannot use cdHdrs provided by the guest because of different ways
2202 * it gets filled out by different versions of kernels.
2203 */
2204 //if (pGso->cbHdrs < pPktHdr->uCSumStart + pPktHdr->uCSumOffset + 2)
2205 {
2206 Log4Func(("%s HdrLen before adjustment %d.\n",
2207 pThis->szInst, pGso->cbHdrsTotal));
2208 switch (pGso->u8Type)
2209 {
2210 case PDMNETWORKGSOTYPE_IPV4_TCP:
2211 case PDMNETWORKGSOTYPE_IPV6_TCP:
2212 pGso->cbHdrsTotal = pPktHdr->uChksumStart +
2213 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
2214
2215 AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
2216 ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
2217
2218 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
2219 break;
2220 case PDMNETWORKGSOTYPE_IPV4_UDP:
2221 pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
2222 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
2223 break;
2224 }
2225 /* Update GSO structure embedded into the frame */
2226 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
2227 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
2228 Log4Func(("%s adjusted HdrLen to %d.\n",
2229 pThis->szInst, pGso->cbHdrsTotal));
2230 }
2231 Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
2232 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2233 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2234 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
2235 }
2236 else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2237 {
2238 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
2239 /*
2240 * This is not GSO frame but checksum offloading is requested.
2241 */
2242 virtioNetR3CompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
2243 pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
2244 }
2245
2246 return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
2247}
2248
2249static int virtioNetR3TransmitPendingPackets(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2250 PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
2251{
2252
2253 PVIRTIOCORE pVirtio = &pThis->Virtio;
2254
2255
2256 if (!pThis->fVirtioReady)
2257 {
2258 LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
2259 pThis->szInst, pThis->virtioNetConfig.uStatus));
2260 return VERR_IGNORED;
2261 }
2262
2263 if (!pThis->fCableConnected)
2264 {
2265 Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
2266 return VERR_IGNORED;
2267 }
2268
2269 /*
2270 * Only one thread is allowed to transmit at a time, others should skip
2271 * transmission as the packets will be picked up by the transmitting
2272 * thread.
2273 */
2274 if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
2275 return VERR_IGNORED;
2276
2277
2278
2279 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2280 if (pDrv)
2281 {
2282 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
2283 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
2284 if (rc == VERR_TRY_AGAIN)
2285 {
2286 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2287 return VERR_TRY_AGAIN;
2288 }
2289 }
2290
2291 int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2292 if (!cPkts)
2293 {
2294 LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
2295
2296 if (pDrv)
2297 pDrv->pfnEndXmit(pDrv);
2298
2299 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2300 return VERR_MISSING;
2301 }
2302 LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
2303
2304 virtioNetR3SetWriteLed(pThisCC, true);
2305
2306 int rc;
2307 PVIRTQBUF pVirtqBuf = NULL;
2308 while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, &pVirtqBuf)) == VINF_SUCCESS)
2309 {
2310 Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
2311
2312 PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
2313 PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
2314 uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
2315 size_t uSize = 0;
2316
2317 AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
2318 ("Desc chain's first seg has insufficient space for pkt header!\n"),
2319 VERR_INTERNAL_ERROR);
2320
2321 PVIRTIONETPKTHDR pPktHdr = (PVIRTIONETPKTHDR)RTMemAllocZ(pThis->cbPktHdr);
2322 AssertMsgReturn(pPktHdr, ("Out of Memory\n"), VERR_NO_MEMORY);
2323
2324 /* Compute total frame size. */
2325 for (unsigned i = 0; i < cSegsFromGuest && uSize < VIRTIONET_MAX_FRAME_SIZE; i++)
2326 uSize += paSegsFromGuest[i].cbSeg;
2327
2328 Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uSize));
2329 Assert(uSize <= VIRTIONET_MAX_FRAME_SIZE);
2330
2331 /* Truncate oversized frames. */
2332 if (uSize > VIRTIONET_MAX_FRAME_SIZE)
2333 uSize = VIRTIONET_MAX_FRAME_SIZE;
2334
2335 if (pThisCC->pDrv)
2336 {
2337 uint64_t uOffset;
2338
2339 uSize -= pThis->cbPktHdr;
2340 rc = virtioNetR3ReadHeader(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uSize);
2341 if (RT_FAILURE(rc))
2342 return rc;
2343 virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
2344
2345 PDMNETWORKGSO Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
2346
2347 PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
2348 rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uSize, pGso, &pSgBufToPdmLeafDevice);
2349 if (RT_SUCCESS(rc))
2350 {
2351 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
2352 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
2353
2354 size_t cbCopied = 0;
2355 size_t cbTotal = 0;
2356 size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uSize;
2357 uOffset = 0;
2358 while (cbRemain)
2359 {
2360 PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
2361 uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
2362 uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
2363 uint64_t srcSgCur = (uint64_t)pSgPhysSend->GCPhysCur;
2364 cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
2365 virtioCoreGCPhysRead(pVirtio, pDevIns,
2366 (RTGCPHYS)pSgPhysSend->GCPhysCur,
2367 ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
2368 virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
2369 cbRemain -= cbCopied;
2370 uOffset += cbCopied;
2371 cbTotal += cbCopied;
2372 }
2373
2374 LogFunc((".... Copied %lu bytes to %lu byte guest buffer, residual=%lu\n",
2375 cbTotal, pVirtqBuf->cbPhysSend, pVirtqBuf->cbPhysSend - cbTotal));
2376
2377 rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
2378 if (RT_FAILURE(rc))
2379 {
2380 LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
2381 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2382 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
2383 pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
2384 }
2385 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2386 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
2387 }
2388 else
2389 {
2390 Log4Func(("Failed to allocate S/G buffer: size=%u rc=%Rrc\n", uSize, rc));
2391 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
2392 virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
2393 break;
2394 }
2395
2396 /* Point to next descriptor chain in avail ring of virtq */
2397 virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
2398
2399 /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
2400 virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
2401
2402 /* Update used ring idx and notify guest that we've transmitted the data it sent */
2403 virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2404 }
2405
2406 virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
2407 pVirtqBuf = NULL;
2408 }
2409 virtioNetR3SetWriteLed(pThisCC, false);
2410
2411 if (pDrv)
2412 pDrv->pfnEndXmit(pDrv);
2413
2414 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2415 return VINF_SUCCESS;
2416}
2417
2418/**
2419 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2420 */
2421static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
2422{
2423 LogFunc(("\n"));
2424 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2425 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2426 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2427 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(0)];
2428 STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
2429
2430 (void)virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
2431}
2432
2433/**
2434 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
2435 */
2436static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
2437{
2438 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2439 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2440 RT_NOREF(hTimer, pvUser);
2441
2442 SET_LINK_UP(pThis);
2443
2444 virtioNetWakeupRxBufWaiter(pDevIns);
2445
2446 LogFunc(("[%s] Link is up\n", pThis->szInst));
2447
2448 if (pThisCC->pDrv)
2449 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
2450}
2451
2452/**
2453 * Takes down the link temporarily if it's current status is up.
2454 *
2455 * This is used during restore and when replumbing the network link.
2456 *
2457 * The temporary link outage is supposed to indicate to the OS that all network
2458 * connections have been lost and that it for instance is appropriate to
2459 * renegotiate any DHCP lease.
2460 *
2461 * @param pDevIns The device instance.
2462 * @param pThis The virtio-net shared instance data.
2463 * @param pThisCC The virtio-net ring-3 instance data.
2464 */
2465static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2466{
2467
2468 if (IS_LINK_UP(pThis))
2469 {
2470 SET_LINK_DOWN(pThis);
2471
2472 /* Restore the link back in 5 seconds. */
2473 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
2474 AssertRC(rc);
2475
2476 LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
2477 }
2478}
2479
2480/**
2481 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
2482 */
2483static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
2484{
2485 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2486 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2487 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2488
2489 bool fRequestedLinkStateIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
2490
2491 if (LogIs7Enabled())
2492 {
2493 LogFunc(("[%s]", pThis->szInst));
2494 switch(enmState)
2495 {
2496 case PDMNETWORKLINKSTATE_UP:
2497 Log(("UP\n"));
2498 break;
2499 case PDMNETWORKLINKSTATE_DOWN:
2500 Log(("DOWN\n"));
2501 break;
2502 case PDMNETWORKLINKSTATE_DOWN_RESUME:
2503 Log(("DOWN (RESUME)\n"));
2504 break;
2505 default:
2506 Log(("UNKNOWN)\n"));
2507 }
2508 }
2509
2510 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
2511 {
2512
2513 if (IS_LINK_UP(pThis))
2514 {
2515 /*
2516 * We bother to bring the link down only if it was up previously. The UP link state
2517 * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
2518 */
2519 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
2520 if (pThisCC->pDrv)
2521 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2522 }
2523 }
2524 else if (fRequestedLinkStateIsUp != IS_LINK_UP(pThis))
2525 {
2526 if (fRequestedLinkStateIsUp)
2527 {
2528 Log(("[%s] Link is up\n", pThis->szInst));
2529 pThis->fCableConnected = true;
2530 SET_LINK_UP(pThis);
2531 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2532 }
2533 else /* Link requested to be brought down */
2534 {
2535 /* The link was brought down explicitly, make sure it won't come up by timer. */
2536 PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
2537 Log(("[%s] Link is down\n", pThis->szInst));
2538 pThis->fCableConnected = false;
2539 SET_LINK_DOWN(pThis);
2540 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2541 }
2542 if (pThisCC->pDrv)
2543 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2544 }
2545 return VINF_SUCCESS;
2546}
2547/**
2548 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
2549 */
2550static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
2551{
2552 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2553 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2554
2555 return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
2556}
2557
2558static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2559{
2560 Log10Func(("[%s]\n", pThis->szInst));
2561 int rc = VINF_SUCCESS;
2562 for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
2563 {
2564 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uIdxWorker];
2565 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
2566
2567 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2568 {
2569 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
2570 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2571 }
2572 if (pWorkerR3->pThread)
2573 {
2574 int rcThread;
2575 rc = PDMDevHlpThreadDestroy(pDevIns, pWorkerR3->pThread, &rcThread);
2576 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
2577 AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
2578 pWorkerR3->pThread = NULL;
2579 }
2580 }
2581 return rc;
2582}
2583
2584static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis,
2585 PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
2586 PVIRTIONETVIRTQ pVirtq)
2587{
2588 Log10Func(("[%s]\n", pThis->szInst));
2589 RT_NOREF(pThis);
2590
2591 int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess);
2592 LogFunc(("PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess=%p)", &pWorker->hEvtProcess));
2593 LogFunc(("pWorker->hEvtProcess = %x\n", pWorker->hEvtProcess));
2594
2595 if (RT_FAILURE(rc))
2596 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2597 N_("DevVirtioNET: Failed to create SUP event semaphore"));
2598
2599 LogFunc(("creating thread for queue %s\n", pVirtq->szName));
2600
2601 rc = PDMDevHlpThreadCreate(pDevIns, &pWorkerR3->pThread,
2602 (void *)pWorker, virtioNetR3WorkerThread,
2603 virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, pVirtq->szName);
2604 if (RT_FAILURE(rc))
2605 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2606 N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
2607
2608 pWorker->fAssigned = true; /* Because worker's state held in fixed-size array w/empty slots */
2609
2610 LogFunc(("%s pThread: %p\n", pVirtq->szName, pWorkerR3->pThread));
2611
2612 return rc;
2613}
2614
2615static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2616{
2617 Log10Func(("[%s]\n", pThis->szInst));
2618 int rc;
2619
2620
2621 /* Create the Control Queue worker anyway whether or not it is feature-negotiated or utilized by the guest,
2622 * as it's relatively low overhead resource-wise. This is for two reasons: First, at the time of this comment
2623 * queues and workers are configured pre-feature negotiation; secondly, legacy guest drivers are allowed to start
2624 * using the device prior to feature negotiation, and we can only know we are dealing with a modern guest driver
2625 * after feature negotiation. */
2626
2627 PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
2628 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis,
2629 &pThis->aWorkers[CTRLQIDX], &pThisCC->aWorkers[CTRLQIDX], pCtlVirtq);
2630 AssertRCReturn(rc, rc);
2631
2632 pCtlVirtq->fHasWorker = true;
2633
2634 for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
2635 {
2636 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
2637 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
2638
2639 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, &pThis->aWorkers[TXQIDX(uVirtqPair)],
2640 &pThisCC->aWorkers[TXQIDX(uVirtqPair)], pTxVirtq);
2641 AssertRCReturn(rc, rc);
2642
2643 pTxVirtq->fHasWorker = true;
2644 pRxVirtq->fHasWorker = false;
2645 }
2646
2647 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
2648 pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
2649
2650 pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
2651
2652 return rc;
2653}
2654
2655/**
2656 * @callback_method_impl{FNPDMTHREADDEV}
2657 */
2658static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
2659{
2660 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2661 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2662 PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
2663 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[pWorker->uIdx];
2664 uint16_t uIdx = pWorker->uIdx;
2665
2666 ASMAtomicWriteBool(&pWorker->fSleeping, false);
2667
2668 Assert(pWorker->uIdx == pVirtq->uIdx);
2669
2670 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
2671 return VINF_SUCCESS;
2672
2673 LogFunc(("[%s] worker thread idx=%d started for %s (virtq idx=%d)\n", pThis->szInst, pWorker->uIdx, pVirtq->szName, pVirtq->uIdx));
2674
2675 /** @todo Race w/guest enabling/disabling guest notifications cyclically.
2676 See BugRef #8651, Comment #82 */
2677 virtioCoreVirtqEnableNotify(&pThis->Virtio, uIdx, true /* fEnable */);
2678
2679 while ( pThread->enmState != PDMTHREADSTATE_TERMINATING
2680 && pThread->enmState != PDMTHREADSTATE_TERMINATED)
2681 {
2682 if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pVirtq->uIdx))
2683 {
2684 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
2685 ASMAtomicWriteBool(&pWorker->fSleeping, true);
2686
2687 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
2688 if (!fNotificationSent)
2689 {
2690 Log10Func(("[%s] %s worker sleeping...\n\n", pThis->szInst, pVirtq->szName));
2691 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
2692
2693 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
2694 STAM_COUNTER_INC(&pThis->StatTransmitByThread);
2695 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
2696 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
2697 return VINF_SUCCESS;
2698 if (rc == VERR_INTERRUPTED)
2699 continue;
2700 ASMAtomicWriteBool(&pWorker->fNotified, false);
2701 }
2702 ASMAtomicWriteBool(&pWorker->fSleeping, false);
2703
2704 }
2705
2706 /* Dispatch to the handler for the queue this worker is set up to drive */
2707
2708 if (pVirtq->fCtlVirtq)
2709 {
2710 Log10Func(("[%s] %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
2711 PVIRTQBUF pVirtqBuf = NULL;
2712 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, &pVirtqBuf, true);
2713 if (rc == VERR_NOT_AVAILABLE)
2714 {
2715 Log10Func(("[%s] %s worker woken. Nothing found in queue/n", pThis->szInst, pVirtq->szName));
2716 continue;
2717 }
2718 virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
2719 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
2720 }
2721 else /* Must be Tx queue */
2722 {
2723 Log10Func(("[%s] %s worker woken. Virtq has data to transmit\n", pThis->szInst, pVirtq->szName));
2724 virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
2725 }
2726
2727 /* Rx queues aren't handled by our worker threads. Instead, the PDM network
2728 * leaf driver invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback,
2729 * which waits until woken by virtioNetVirtqNotified()
2730 * indicating that guest IN buffers have been added to Rx virt queue.
2731 */
2732 }
2733 Log10(("[%s] %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
2734 return VINF_SUCCESS;
2735}
2736
2737
2738/**
2739 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
2740 */
2741static DECLCALLBACK(void) virtioNetR3StatusChg(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
2742{
2743 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
2744 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
2745
2746 pThis->fVirtioReady = fVirtioReady;
2747
2748 if (fVirtioReady)
2749 {
2750 Log(("-----------------------------------------------------------------------------------------\n"));
2751 Log(("%-23s: %s *** VirtIO Ready ***\n-----------------------------------------------------------------------------------------\n",
2752 __FUNCTION__, pThis->szInst));
2753
2754 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(pVirtio);
2755#ifdef LOG_ENABLED
2756 virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures,
2757 RT_ELEMENTS(s_aDevSpecificFeatures));
2758#endif
2759
2760 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
2761
2762 pThis->fResetting = false;
2763
2764 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
2765 {
2766 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
2767 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
2768
2769 Assert(pWorker->uIdx == uVirtqNbr);
2770 RT_NOREF(pWorker);
2771
2772 Assert(pVirtq->uIdx == pWorker->uIdx);
2773
2774 (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
2775 pVirtq->fAttachedToVirtioCore = true;
2776 if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
2777 virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
2778 }
2779 }
2780 else
2781 {
2782 Log(("%-23s: %s VirtIO is resetting\n", __FUNCTION__, pThis->szInst));
2783
2784 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
2785 Log7(("%-23s: %s Link is %s\n", __FUNCTION__, pThis->szInst, pThis->fCableConnected ? "up" : "down"));
2786
2787 pThis->fPromiscuous = true;
2788 pThis->fAllMulticast = false;
2789 pThis->fAllUnicast = false;
2790 pThis->fNoMulticast = false;
2791 pThis->fNoUnicast = false;
2792 pThis->fNoBroadcast = false;
2793 pThis->uIsTransmitting = 0;
2794 pThis->cUnicastFilterMacs = 0;
2795 pThis->cMulticastFilterMacs = 0;
2796
2797 memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
2798 memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
2799 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
2800
2801 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
2802
2803 for (uint16_t uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
2804 pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore = false;
2805 }
2806}
2807
2808static DECLCALLBACK(void) virtioGuestVersionHandler(PVIRTIOCORE pVirtio, uint32_t fModern)
2809{
2810 LogFunc(("Guest Driver version is %s\n", fModern ? "modern" : "legacy"));
2811
2812 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
2813
2814 /* Calculate network packet header type and size based on what we know now */
2815 pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
2816
2817 if (fModern)
2818 pThis->ePktHdrType = kVirtioNetModernPktHdr_1_0;
2819 else if (FEATURE_DISABLED(MRG_RXBUF))
2820 {
2821 pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
2822 pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
2823 }
2824 else /* Legacy guest with MRG_RX feature enabled */
2825 pThis->ePktHdrType = kVirtioNetLegacyPktHdr;
2826}
2827
2828
2829#endif /* IN_RING3 */
2830
2831/**
2832 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
2833 *
2834 * One harddisk at one port has been unplugged.
2835 * The VM is suspended at this point.
2836 */
2837static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2838{
2839 RT_NOREF(fFlags);
2840
2841 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2842 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2843
2844 Log7Func(("[%s]\n", pThis->szInst));
2845 RT_NOREF(pThis);
2846
2847 AssertLogRelReturnVoid(iLUN == 0);
2848
2849 pThisCC->pDrvBase = NULL;
2850 pThisCC->pDrv = NULL;
2851}
2852
2853/**
2854 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
2855 *
2856 * This is called when we change block driver.
2857 */
2858static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2859{
2860 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2861 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2862
2863 RT_NOREF(fFlags);
2864
2865 Log7Func(("[%s]", pThis->szInst));
2866
2867 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
2868
2869 RT_NOREF(pThis);
2870
2871 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
2872 if (RT_SUCCESS(rc))
2873 {
2874 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2875 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2876 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
2877 }
2878 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2879 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2880 Log(("[%s] No attached driver!\n", pThis->szInst));
2881
2882 return rc;
2883}
2884
2885/**
2886 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
2887 */
2888static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2889{
2890 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
2891 if (iLUN)
2892 return VERR_PDM_LUN_NOT_FOUND;
2893 *ppLed = &pThisR3->led;
2894 return VINF_SUCCESS;
2895}
2896
2897
2898/**
2899 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2900 */
2901static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2902{
2903 PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
2904 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
2905 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
2906 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
2907 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
2908 return NULL;
2909}
2910
2911/**
2912 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
2913 */
2914static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
2915{
2916 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2917
2918 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2919 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2920
2921 Log(("[%s] Destroying instance\n", pThis->szInst));
2922
2923 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
2924 {
2925 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
2926 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
2927 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
2928 }
2929
2930 virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
2931
2932 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
2933
2934 return VINF_SUCCESS;
2935}
2936
2937/**
2938 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2939 */
2940static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2941{
2942 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2943 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2944 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2945 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2946
2947 /*
2948 * Quick initialization of the state data, making sure that the destructor always works.
2949 */
2950 Log7Func(("PDM device instance: %d\n", iInstance));
2951 RTStrPrintf(pThis->szInst, sizeof(pThis->szInst), "virtio-net #%d", iInstance);
2952
2953 pThisCC->pDevIns = pDevIns;
2954 pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
2955 pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
2956 pThisCC->led.u32Magic = PDMLED_MAGIC;
2957
2958 /* Interfaces */
2959 pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
2960 pThisCC->INetworkDown.pfnReceive = virtioNetR3NetworkDown_Receive;
2961 pThisCC->INetworkDown.pfnReceiveGso = virtioNetR3NetworkDown_ReceiveGso;
2962 pThisCC->INetworkDown.pfnXmitPending = virtioNetR3NetworkDown_XmitPending;
2963 pThisCC->INetworkConfig.pfnGetMac = virtioNetR3NetworkConfig_GetMac;
2964 pThisCC->INetworkConfig.pfnGetLinkState = virtioNetR3NetworkConfig_GetLinkState;
2965 pThisCC->INetworkConfig.pfnSetLinkState = virtioNetR3NetworkConfig_SetLinkState;
2966
2967 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
2968
2969 /*
2970 * Validate configuration.
2971 */
2972 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo", "");
2973
2974 /* Get config params */
2975 int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
2976 if (RT_FAILURE(rc))
2977 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
2978
2979 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
2980 if (RT_FAILURE(rc))
2981 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
2982
2983 uint32_t uStatNo = iInstance;
2984 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
2985 if (RT_FAILURE(rc))
2986 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
2987
2988 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
2989 if (RT_FAILURE(rc))
2990 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
2991
2992 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
2993
2994 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
2995 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
2996 pThis->szInst, pThis->cMsLinkUpDelay / 1000));
2997
2998 Log(("[%s] Link up delay is set to %u seconds\n", pThis->szInst, pThis->cMsLinkUpDelay / 1000));
2999
3000 /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
3001 memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
3002
3003 Log(("Using MAC address for %s: %2x:%2x:%2x:%2x:%2x:%2x\n", pThis->szInst,
3004 pThis->macConfigured.au8[0], pThis->macConfigured.au8[1], pThis->macConfigured.au8[2],
3005 pThis->macConfigured.au8[3], pThis->macConfigured.au8[4], pThis->macConfigured.au8[5]));
3006
3007 LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
3008
3009 /*
3010 * Configure Virtio core (generic Virtio queue and infrastructure management) parameters.
3011 */
3012# if FEATURE_OFFERED(STATUS)
3013 pThis->virtioNetConfig.uStatus = 0;
3014# endif
3015
3016 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
3017 pThisCC->Virtio.pfnGuestVersionHandler = virtioGuestVersionHandler;
3018 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3019 pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChg;
3020 pThisCC->Virtio.pfnDevCapRead = virtioNetR3DevCapRead;
3021 pThisCC->Virtio.pfnDevCapWrite = virtioNetR3DevCapWrite;
3022
3023 VIRTIOPCIPARAMS VirtioPciParams;
3024 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
3025 VirtioPciParams.uClassBase = PCI_CLASS_BASE_NETWORK_CONTROLLER;
3026 VirtioPciParams.uClassSub = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
3027 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
3028 VirtioPciParams.uSubsystemId = PCI_DEVICE_ID_VIRTIONET_HOST; /* VirtIO 1.0 allows PCI Device ID here */
3029 VirtioPciParams.uInterruptLine = 0x00;
3030 VirtioPciParams.uInterruptPin = 0x01;
3031
3032 /* Create semaphore used to synchronize/throttle the downstream LUN's Rx waiter thread. */
3033 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
3034 if (RT_FAILURE(rc))
3035 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
3036
3037
3038 /* Initialize VirtIO core. (pfnStatusChanged callback when both host VirtIO core & guest driver are ready) */
3039 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
3040 VIRTIONET_HOST_FEATURES_OFFERED,
3041 &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
3042 if (RT_FAILURE(rc))
3043 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
3044
3045 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
3046 if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
3047 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
3048
3049
3050 /*
3051 * Initialize queues. Due to this being a transitional device (e.g. accommodating both modern
3052 * and legacy drivers), the control queue must be created whether or not the VIRTIO_NET_F_CTRL_VQ
3053 * is negotiated, because legacy drivers are not bound to begin configuration and I/O until
3054 * feature negotiation is complete. In the final analysis, there may be no good reason to
3055 * enforce VIRTIO_NET_F_CTRL_VQ as a prerequisite to handling guest control queue transactions,
3056 * but merely to log violations (e.g. control transactions without feature explicitly enabled),
3057 * once, thus not being strict with regard to misbehaving modern drivers.
3058 */
3059
3060 pThis->cVirtqPairs = 1; /* default, VirtIO 1.0, 5.1.6.5.5 */
3061 pThis->cVirtqs += pThis->cVirtqPairs * 2 + 1;
3062 pThis->aVirtqs[CTRLQIDX].fCtlVirtq = true;
3063
3064 virtioNetR3SetVirtqNames(pThis);
3065 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3066 {
3067 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
3068 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
3069 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
3070 pVirtq->uIdx = uVirtqNbr;
3071 pWorker->uIdx = uVirtqNbr;
3072 pWorkerR3->uIdx = uVirtqNbr;
3073 }
3074 /*
3075 * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
3076 */
3077 rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
3078 if (RT_FAILURE(rc))
3079 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
3080
3081 /* Create Link Up Timer */
3082 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL,
3083 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
3084 "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
3085 /*
3086 * Attach network driver instance
3087 */
3088 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
3089 if (RT_SUCCESS(rc))
3090 {
3091 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
3092 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
3093 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
3094 }
3095 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
3096 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
3097 {
3098 Log(("[%s] No attached driver!\n", pThis->szInst));
3099 AssertRCReturn(rc, rc);
3100 }
3101
3102 /*
3103 * Status driver
3104 */
3105 PPDMIBASE pUpBase;
3106 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
3107 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
3108 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
3109
3110 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
3111
3112 /*
3113 * Register saved state.
3114 */
3115 rc = PDMDevHlpSSMRegister(pDevIns, VIRTIONET_SAVED_STATE_VERSION, sizeof(*pThis),
3116 virtioNetR3SaveExec, virtioNetR3LoadExec);
3117 AssertRCReturn(rc, rc);
3118
3119 /*
3120 * Statistics and debug stuff.
3121 * The /Public/ bits are official and used by session info in the GUI.
3122 */
3123# ifdef VBOX_WITH_STATISTICS
3124 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3125 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
3126 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3127 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
3128 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
3129 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
3130
3131 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
3132 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
3133 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
3134 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
3135 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
3136 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
3137 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
3138 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
3139 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
3140 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
3141 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
3142 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
3143 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
3144 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
3145# endif
3146
3147 /*
3148 * Register the debugger info callback (ignore errors).
3149 */
3150 char szTmp[128];
3151 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "virtio-net", "Display virtio-net info (help, net, features, state, pointers, queues, all)", virtioNetR3Info);
3152 if (RT_FAILURE(rc))
3153 LogRel(("Failed to register DBGF info for device %s\n", szTmp));
3154 return rc;
3155}
3156
3157#else /* !IN_RING3 */
3158
3159/**
3160 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3161 */
3162static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
3163{
3164 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3165 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3166 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3167 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3168 return virtioCoreRZInit(pDevIns, &pThis->Virtio);
3169}
3170
3171#endif /* !IN_RING3 */
3172
3173/**
3174 * The device registration structure.
3175 */
3176const PDMDEVREG g_DeviceVirtioNet_1_0 =
3177{
3178 /* .uVersion = */ PDM_DEVREG_VERSION,
3179 /* .uReserved0 = */ 0,
3180 /* .szName = */ "virtio-net-1-dot-0",
3181 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
3182 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
3183 /* .cMaxInstances = */ ~0U,
3184 /* .uSharedVersion = */ 42,
3185 /* .cbInstanceShared = */ sizeof(VIRTIONET),
3186 /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
3187 /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
3188 /* .cMaxPciDevices = */ 1,
3189 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
3190 /* .pszDescription = */ "Virtio Host NET.\n",
3191#if defined(IN_RING3)
3192 /* .pszRCMod = */ "VBoxDDRC.rc",
3193 /* .pszR0Mod = */ "VBoxDDR0.r0",
3194 /* .pfnConstruct = */ virtioNetR3Construct,
3195 /* .pfnDestruct = */ virtioNetR3Destruct,
3196 /* .pfnRelocate = */ NULL,
3197 /* .pfnMemSetup = */ NULL,
3198 /* .pfnPowerOn = */ NULL,
3199 /* .pfnReset = */ NULL,
3200 /* .pfnSuspend = */ virtioNetWakeupRxBufWaiter,
3201 /* .pfnResume = */ NULL,
3202 /* .pfnAttach = */ virtioNetR3Attach,
3203 /* .pfnDetach = */ virtioNetR3Detach,
3204 /* .pfnQueryInterface = */ NULL,
3205 /* .pfnInitComplete = */ NULL,
3206 /* .pfnPowerOff = */ virtioNetWakeupRxBufWaiter,
3207 /* .pfnSoftReset = */ NULL,
3208 /* .pfnReserved0 = */ NULL,
3209 /* .pfnReserved1 = */ NULL,
3210 /* .pfnReserved2 = */ NULL,
3211 /* .pfnReserved3 = */ NULL,
3212 /* .pfnReserved4 = */ NULL,
3213 /* .pfnReserved5 = */ NULL,
3214 /* .pfnReserved6 = */ NULL,
3215 /* .pfnReserved7 = */ NULL,
3216#elif defined(IN_RING0)
3217 /* .pfnEarlyConstruct = */ NULL,
3218 /* .pfnConstruct = */ virtioNetRZConstruct,
3219 /* .pfnDestruct = */ NULL,
3220 /* .pfnFinalDestruct = */ NULL,
3221 /* .pfnRequest = */ NULL,
3222 /* .pfnReserved0 = */ NULL,
3223 /* .pfnReserved1 = */ NULL,
3224 /* .pfnReserved2 = */ NULL,
3225 /* .pfnReserved3 = */ NULL,
3226 /* .pfnReserved4 = */ NULL,
3227 /* .pfnReserved5 = */ NULL,
3228 /* .pfnReserved6 = */ NULL,
3229 /* .pfnReserved7 = */ NULL,
3230#elif defined(IN_RC)
3231 /* .pfnConstruct = */ virtioNetRZConstruct,
3232 /* .pfnReserved0 = */ NULL,
3233 /* .pfnReserved1 = */ NULL,
3234 /* .pfnReserved2 = */ NULL,
3235 /* .pfnReserved3 = */ NULL,
3236 /* .pfnReserved4 = */ NULL,
3237 /* .pfnReserved5 = */ NULL,
3238 /* .pfnReserved6 = */ NULL,
3239 /* .pfnReserved7 = */ NULL,
3240#else
3241# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3242#endif
3243 /* .uVersionEnd = */ PDM_DEVREG_VERSION
3244};
3245
Note: See TracBrowser for help on using the repository browser.

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