VirtualBox

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

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

Fixes issue in BugRef(8651) Comment #100

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