VirtualBox

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

Last change on this file since 85561 was 85415, checked in by vboxsync, 4 years ago

Network/DevVirtioNet_1_0.cpp: Fixed pause/resume/poweroff, added more support for multiqueue (MQ) handling (see bugref:8651, Comment 91), More little cleanup, comment fixes

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