VirtualBox

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

Last change on this file since 87766 was 87760, checked in by vboxsync, 4 years ago

VMM/TM,VMM/DevHlp: Require flag on timers that are to be used in ring-0 (and while refactoring a counte flag to check that all timers have been checked). Removed obsolete timer device helpers. bugref:9943

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