VirtualBox

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

Last change on this file since 84373 was 84351, checked in by vboxsync, 5 years ago

Network/DevVirtioNet_1_0.cpp: Fixed R0->R3 degradation in I/O path by implementing R0 MMIO, also allows worker thread synchronization via notification from guest to run in R0. Fixed bug where wasn't properly detecting when guest suppressed interrupts, and various minor fixes. See bugref:8651, Comment #70

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

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