VirtualBox

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

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

DevVirtioNet_1_0.cpp: Convert VirtIO to be 'transitional' device, that handles both legacy (0.9) guests and modern (1.0+) guests. Various other small improvements and reduction/improved formatting of logging. See BugRef:8561, Comment #137

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