VirtualBox

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

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

Fix more windows compiler warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 124.6 KB
Line 
1/* $Id: DevVirtioNet_1_0.cpp 84390 2020-05-20 06:27:41Z vboxsync $ $Revision: 84390 $ $Date: 2020-05-20 06:27:41 +0000 (Wed, 20 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#ifdef LOG_ENABLED
679DECLINLINE(void) virtioNetPrintFeatures(VIRTIONET *pThis)
680{
681 static struct
682 {
683 uint64_t fFeatureBit;
684 const char *pcszDesc;
685 } const s_aFeatures[] =
686 {
687 { VIRTIONET_F_CSUM, " CSUM Host handles packets with partial checksum.\n" },
688 { VIRTIONET_F_GUEST_CSUM, " GUEST_CSUM Guest handles packets with partial checksum.\n" },
689 { VIRTIONET_F_CTRL_GUEST_OFFLOADS, " CTRL_GUEST_OFFLOADS Control channel offloads reconfiguration support.\n" },
690 { VIRTIONET_F_MAC, " MAC Host has given MAC address.\n" },
691 { VIRTIONET_F_GUEST_TSO4, " GUEST_TSO4 Guest can receive TSOv4.\n" },
692 { VIRTIONET_F_GUEST_TSO6, " GUEST_TSO6 Guest can receive TSOv6.\n" },
693 { VIRTIONET_F_GUEST_ECN, " GUEST_ECN Guest can receive TSO with ECN.\n" },
694 { VIRTIONET_F_GUEST_UFO, " GUEST_UFO Guest can receive UFO.\n" },
695 { VIRTIONET_F_HOST_TSO4, " HOST_TSO4 Host can receive TSOv4.\n" },
696 { VIRTIONET_F_HOST_TSO6, " HOST_TSO6 Host can receive TSOv6.\n" },
697 { VIRTIONET_F_HOST_ECN, " HOST_ECN Host can receive TSO with ECN.\n" },
698 { VIRTIONET_F_HOST_UFO, " HOST_UFO Host can receive UFO.\n" },
699 { VIRTIONET_F_MRG_RXBUF, " MRG_RXBUF Guest can merge receive buffers.\n" },
700 { VIRTIONET_F_STATUS, " STATUS Configuration status field is available.\n" },
701 { VIRTIONET_F_CTRL_VQ, " CTRL_VQ Control channel is available.\n" },
702 { VIRTIONET_F_CTRL_RX, " CTRL_RX Control channel RX mode support.\n" },
703 { VIRTIONET_F_CTRL_VLAN, " CTRL_VLAN Control channel VLAN filtering.\n" },
704 { VIRTIONET_F_GUEST_ANNOUNCE, " GUEST_ANNOUNCE Guest can send gratuitous packets.\n" },
705 { VIRTIONET_F_MQ, " MQ Host supports multiqueue with automatic receive steering.\n" },
706 { VIRTIONET_F_CTRL_MAC_ADDR, " CTRL_MAC_ADDR Set MAC address through control channel.\n" }
707 };
708
709#define MAXLINE 80
710 /* Display as a single buf to prevent interceding log messages */
711 uint64_t fFeaturesOfferedMask = VIRTIONET_HOST_FEATURES_OFFERED;
712 uint16_t cbBuf = RT_ELEMENTS(s_aFeatures) * 132;
713 char *pszBuf = (char *)RTMemAllocZ(cbBuf);
714 Assert(pszBuf);
715 char *cp = pszBuf;
716 for (unsigned i = 0; i < RT_ELEMENTS(s_aFeatures); ++i)
717 {
718 uint64_t isOffered = fFeaturesOfferedMask & s_aFeatures[i].fFeatureBit;
719 uint64_t isNegotiated = pThis->fNegotiatedFeatures & s_aFeatures[i].fFeatureBit;
720 cp += RTStrPrintf(cp, cbBuf - (cp - pszBuf), " %s %s %s",
721 isOffered ? "+" : "-", isNegotiated ? "x" : " ", s_aFeatures[i].pcszDesc);
722 }
723 Log3(("VirtIO Net Features Configuration\n\n"
724 " Offered Accepted Feature Description\n"
725 " ------- -------- ------- -----------\n"
726 "%s\n", pszBuf));
727 RTMemFree(pszBuf);
728}
729#endif
730/*
731 * Checks whether negotiated features have required flag combinations.
732 * See VirtIO 1.0 specification, Section 5.1.3.1 */
733DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
734{
735 uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
736 || fFeatures & VIRTIONET_F_GUEST_TSO6
737 || fFeatures & VIRTIONET_F_GUEST_UFO;
738
739 uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
740 || fFeatures & VIRTIONET_F_HOST_TSO6
741 || fFeatures & VIRTIONET_F_HOST_UFO;
742
743 uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
744 || fFeatures & VIRTIONET_F_CTRL_VLAN
745 || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
746 || fFeatures & VIRTIONET_F_MQ
747 || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
748
749 if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
750 return false;
751
752 if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
753 return false;
754
755 if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
756 return false;
757
758 if ( fFeatures & VIRTIONET_F_GUEST_ECN
759 && !( fFeatures & VIRTIONET_F_GUEST_TSO4
760 || fFeatures & VIRTIONET_F_GUEST_TSO6))
761 return false;
762
763 if ( fFeatures & VIRTIONET_F_HOST_ECN
764 && !( fFeatures & VIRTIONET_F_HOST_TSO4
765 || fFeatures & VIRTIONET_F_HOST_TSO6))
766 return false;
767 return true;
768}
769
770/**
771 * Resolves to boolean true if uOffset matches a field offset and size exactly,
772 * (or if 64-bit field, if it accesses either 32-bit part as a 32-bit access)
773 * Assumption is this critereon is mandated by VirtIO 1.0, Section 4.1.3.1)
774 * (Easily re-written to allow unaligned bounded access to a field).
775 *
776 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
777 * @result - true or false
778 */
779#define MATCH_NET_CONFIG(member) \
780 ( ( RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member) == 8 \
781 && ( offConfig == RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) \
782 || offConfig == RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) + sizeof(uint32_t)) \
783 && cb == sizeof(uint32_t)) \
784 || (offConfig + cb <= RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) \
785 + RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member)) )
786
787#ifdef LOG_ENABLED
788# define LOG_NET_CONFIG_ACCESSOR(member) \
789 virtioCoreLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member), \
790 pv, cb, offIntra, fWrite, false, 0);
791#else
792# define LOG_NET_CONFIG_ACCESSOR(member) do { } while (0)
793#endif
794
795#define NET_CONFIG_ACCESSOR(member) \
796 do \
797 { \
798 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIONET_CONFIG_T, member); \
799 if (fWrite) \
800 memcpy((char *)&pThis->virtioNetConfig.member + offIntra, pv, cb); \
801 else \
802 memcpy(pv, (const char *)&pThis->virtioNetConfig.member + offIntra, cb); \
803 LOG_NET_CONFIG_ACCESSOR(member); \
804 } while(0)
805
806#define NET_CONFIG_ACCESSOR_READONLY(member) \
807 do \
808 { \
809 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIONET_CONFIG_T, member); \
810 if (fWrite) \
811 LogFunc(("%s Guest attempted to write readonly virtio_pci_common_cfg.%s\n", INSTANCE(pThis), #member)); \
812 else \
813 { \
814 memcpy(pv, (const char *)&pThis->virtioNetConfig.member + offIntra, cb); \
815 LOG_NET_CONFIG_ACCESSOR(member); \
816 } \
817 } while(0)
818
819
820static int virtioNetR3CfgAccessed(PVIRTIONET pThis, uint32_t offConfig, void *pv, uint32_t cb, bool fWrite)
821{
822 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
823
824 if (MATCH_NET_CONFIG(uMacAddress))
825 NET_CONFIG_ACCESSOR_READONLY(uMacAddress);
826#if FEATURE_OFFERED(STATUS)
827 else
828 if (MATCH_NET_CONFIG(uStatus))
829 NET_CONFIG_ACCESSOR_READONLY(uStatus);
830#endif
831#if FEATURE_OFFERED(MQ)
832 else
833 if (MATCH_NET_CONFIG(uMaxVirtqPairs))
834 NET_CONFIG_ACCESSOR_READONLY(uMaxVirtqPairs);
835#endif
836 else
837 {
838 LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n", INSTANCE(pThis), offConfig, offConfig, cb));
839 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
840 }
841 return VINF_SUCCESS;
842}
843
844#undef NET_CONFIG_ACCESSOR_READONLY
845#undef NET_CONFIG_ACCESSOR
846#undef LOG_ACCESSOR
847#undef MATCH_NET_CONFIG
848
849/**
850 * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
851 */
852static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
853{
854 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
855
856 LogFunc(("%s uOffset: %d, cb: %d\n", INSTANCE(pThis), uOffset, cb));
857 RT_NOREF(pThis);
858 return virtioNetR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
859}
860
861/**
862 * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
863 */
864static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
865{
866 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
867
868 Log10Func(("%s uOffset: %d, cb: %d: %.*Rhxs\n", INSTANCE(pThis), uOffset, cb, RT_MAX(cb, 8) , pv));
869 RT_NOREF(pThis);
870 return virtioNetR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
871}
872
873
874/*********************************************************************************************************************************
875* Misc *
876*********************************************************************************************************************************/
877
878/**
879 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
880 */
881static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
882{
883 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
884
885 /* Parse arguments. */
886 RT_NOREF2(pThis, pszArgs); //bool fVerbose = pszArgs && strstr(pszArgs, "verbose") != NULL;
887
888 /* Show basic information. */
889 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
890 pDevIns->pReg->szName,
891 pDevIns->iInstance);
892}
893
894
895/*********************************************************************************************************************************
896* Saved state *
897*********************************************************************************************************************************/
898
899/**
900 * @callback_method_impl{FNSSMDEVLOADEXEC}
901 */
902static DECLCALLBACK(int) virtioNetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
903{
904 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
905 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
906 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
907
908 RT_NOREF(pThisCC);
909 Log7Func(("%s LOAD EXEC!!\n", INSTANCE(pThis)));
910
911 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
912 AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVED_STATE_VERSION,
913 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
914
915 virtioNetR3SetVirtqNames(pThis);
916
917 pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
918
919 pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtQueues);
920 pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
921
922 for (int idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
923 pHlp->pfnSSMGetBool(pSSM, &pThis->afQueueAttached[idxQueue]);
924
925 int rc;
926
927 if (uPass == SSM_PASS_FINAL)
928 {
929
930 /* Load config area */
931#if FEATURE_OFFERED(STATUS)
932 /* config checks */
933 RTMAC macConfigured;
934 rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
935 AssertRCReturn(rc, rc);
936 if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
937 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
938 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
939 INSTANCE(pThis), &pThis->macConfigured, &macConfigured));
940#endif
941
942#if FEATURE_OFFERED(MQ)
943 pHlp->pfnSSMGetU16( pSSM, &pThis->virtioNetConfig.uMaxVirtqPairs);
944#endif
945 /* Save device-specific part */
946 pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
947 pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
948 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
949 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
950 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
951 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
952 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
953
954 pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
955 pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
956
957 if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
958 memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
959 (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
960
961 pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
962 pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
963
964 if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
965 memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
966 (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
967
968 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
969 AssertRCReturn(rc, rc);
970 }
971
972 /*
973 * Call the virtio core to let it load its state.
974 */
975 rc = virtioCoreR3LoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
976
977 /*
978 * Nudge queue workers
979 */
980 for (int idxWorker = 0; idxWorker < pThis->cWorkers; idxWorker++)
981 {
982 uint16_t idxQueue = pThisCC->aWorkers[idxWorker].idxQueue;
983 if (pThis->afQueueAttached[idxQueue])
984 {
985 Log7Func(("%s Waking %s worker.\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
986 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[idxWorker].hEvtProcess);
987 AssertRCReturn(rc, rc);
988 }
989 }
990 return rc;
991}
992
993/**
994 * @callback_method_impl{FNSSMDEVSAVEEXEC}
995 */
996static DECLCALLBACK(int) virtioNetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
997{
998 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
999 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1000 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1001
1002 RT_NOREF(pThisCC);
1003 Log7Func(("%s SAVE EXEC!!\n", INSTANCE(pThis)));
1004
1005 pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
1006
1007 pHlp->pfnSSMPutU16( pSSM, pThis->cVirtQueues);
1008 pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
1009
1010 for (int idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
1011 pHlp->pfnSSMPutBool(pSSM, pThis->afQueueAttached[idxQueue]);
1012
1013 /* Save config area */
1014#if FEATURE_OFFERED(STATUS)
1015 pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
1016 sizeof(pThis->virtioNetConfig.uMacAddress.au8));
1017#endif
1018#if FEATURE_OFFERED(MQ)
1019 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
1020#endif
1021
1022 /* Save device-specific part */
1023 pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
1024 pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
1025 pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
1026 pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
1027 pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
1028 pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
1029 pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
1030
1031 pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
1032 pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1033
1034 pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
1035 pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1036
1037 int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1038 AssertRCReturn(rc, rc);
1039
1040 /*
1041 * Call the virtio core to let it save its state.
1042 */
1043 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
1044}
1045
1046
1047/*********************************************************************************************************************************
1048* Device interface. *
1049*********************************************************************************************************************************/
1050/*xx*/
1051/**
1052 * @callback_method_impl{FNPDMDEVASYNCNOTIFY}
1053 */
1054static DECLCALLBACK(bool) virtioNetR3DeviceQuiesced(PPDMDEVINS pDevIns)
1055{
1056 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1057 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1058
1059 /** @todo create test to conclusively determine I/O has been quiesced and add it here: */
1060
1061 Log7Func(("%s Device I/O activity quiesced: %s\n",
1062 INSTANCE(pThis), virtioCoreGetStateChangeText(pThisCC->enmQuiescingFor)));
1063
1064 virtioCoreR3VmStateChanged(&pThis->Virtio, pThisCC->enmQuiescingFor);
1065
1066 pThis->fResetting = false;
1067 pThisCC->fQuiescing = false;
1068
1069 return true;
1070}
1071
1072/**
1073 * Worker for virtioNetR3Reset() and virtioNetR3SuspendOrPowerOff().
1074 */
1075static void virtioNetR3QuiesceDevice(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmQuiescingFor)
1076{
1077 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1078 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1079
1080 RT_NOREF(pThis);
1081
1082 /* Prevent worker threads from removing/processing elements from virtq's */
1083 pThisCC->fQuiescing = true;
1084 pThisCC->enmQuiescingFor = enmQuiescingFor;
1085
1086 /*
1087 * Wake downstream network driver thread that's waiting for Rx buffers to be available
1088 * to tell it that's going to happen...
1089 */
1090 virtioNetWakeupRxBufWaiter(pDevIns);
1091
1092 PDMDevHlpSetAsyncNotification(pDevIns, virtioNetR3DeviceQuiesced);
1093
1094 /* If already quiesced invoke async callback. */
1095 if (!ASMAtomicReadBool(&pThis->fLeafWantsRxBuffers))
1096 PDMDevHlpAsyncNotificationCompleted(pDevIns);
1097
1098 /** @todo make sure Rx and Tx are really quiesced (how do we synchronize w/downstream driver?) */
1099}
1100
1101/**
1102 * @interface_method_impl{PDMDEVREGR3,pfnReset}
1103 */
1104static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
1105{
1106 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1107 Log7Func(("%s\n", INSTANCE(pThis)));
1108 pThis->fResetting = true;
1109 virtioNetR3QuiesceDevice(pDevIns, kvirtIoVmStateChangedReset);
1110}
1111
1112/**
1113 * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
1114 */
1115static DECLCALLBACK(void) virtioNetR3SuspendOrPowerOff(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmType)
1116{
1117
1118 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1119 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1120 Log7Func(("%s\n", INSTANCE(pThis)));
1121
1122 RT_NOREF2(pThis, pThisCC);
1123
1124 virtioNetR3QuiesceDevice(pDevIns, enmType);
1125 virtioNetWakeupRxBufWaiter(pDevIns);
1126}
1127
1128/**
1129 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
1130 */
1131static DECLCALLBACK(void) virtioNetR3PowerOff(PPDMDEVINS pDevIns)
1132{
1133 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1134 Log7Func(("%s\n", INSTANCE(pThis)));
1135 RT_NOREF(pThis);
1136 virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedPowerOff);
1137}
1138
1139/**
1140 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
1141 */
1142static DECLCALLBACK(void) virtioNetR3Suspend(PPDMDEVINS pDevIns)
1143{
1144 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1145 Log7Func(("%s \n", INSTANCE(pThis)));
1146 RT_NOREF(pThis);
1147 virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedSuspend);
1148}
1149
1150/**
1151 * @interface_method_impl{PDMDEVREGR3,pfnResume}
1152 *
1153 * Just process the VM device-related state change itself.
1154 * Unlike SCSI driver, there are no packets to redo. No I/O was halted or saved while
1155 * quiescing for pfnSuspend(). Any packets in process were simply dropped by the upper
1156 * layer driver, presumably to be retried or cause erring out at the upper layers
1157 * of the network stack.
1158 */
1159static DECLCALLBACK(void) virtioNetR3Resume(PPDMDEVINS pDevIns)
1160{
1161 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1162 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1163 Log7Func(("\n"));
1164
1165 pThisCC->fQuiescing = false;
1166
1167 /* Ensure guest is working the queues */
1168 virtioCoreR3VmStateChanged(&pThis->Virtio, kvirtIoVmStateChangedResume);
1169}
1170
1171#ifdef IN_RING3
1172
1173DECLINLINE(uint16_t) virtioNetR3Checkum16(const void *pvBuf, size_t cb)
1174{
1175 uint32_t chksum = 0;
1176 uint16_t *pu = (uint16_t *)pvBuf;
1177
1178 while (cb > 1)
1179 {
1180 chksum += *pu++;
1181 cb -= 2;
1182 }
1183 if (cb)
1184 chksum += *(uint8_t*)pu;
1185 while (chksum >> 16)
1186 chksum = (chksum >> 16) + (chksum & 0xFFFF);
1187 return ~chksum;
1188}
1189
1190DECLINLINE(void) virtioNetR3CompleteChecksum(uint8_t *pBuf, size_t cbSize, uint16_t uStart, uint16_t uOffset)
1191{
1192 AssertReturnVoid(uStart < cbSize);
1193 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cbSize);
1194 *(uint16_t *)(pBuf + uStart + uOffset) = virtioNetR3Checkum16(pBuf + uStart, cbSize - uStart);
1195}
1196
1197/**
1198 * Turns on/off the read status LED.
1199 *
1200 * @returns VBox status code.
1201 * @param pThis Pointer to the device state structure.
1202 * @param fOn New LED state.
1203 */
1204void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
1205{
1206 Log10Func(("%s\n", fOn ? "on" : "off"));
1207 if (fOn)
1208 pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
1209 else
1210 pThisR3->led.Actual.s.fReading = fOn;
1211}
1212
1213/**
1214 * Turns on/off the write status LED.
1215 *
1216 * @returns VBox status code.
1217 * @param pThis Pointer to the device state structure.
1218 * @param fOn New LED state.
1219 */
1220void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
1221{
1222 Log10Func(("%s\n", fOn ? "on" : "off"));
1223 if (fOn)
1224 pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
1225 else
1226 pThisR3->led.Actual.s.fWriting = fOn;
1227}
1228
1229/**
1230 * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
1231 * available. This must be called before the pfnRecieve() method is called.
1232 *
1233 * @remarks As a side effect this function enables queue notification
1234 * if it cannot receive because the queue is empty.
1235 * It disables notification if it can receive.
1236 *
1237 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1238 * @thread RX
1239 */
1240static int virtioNetR3IsRxQueuePrimed(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint16_t idxRxQueue)
1241{
1242#define LOGPARAMS INSTANCE(pThis), VIRTQNAME(idxRxQueue)
1243
1244 if (!pThis->fVirtioReady)
1245 {
1246 Log8Func(("%s %s VirtIO not ready (rc = VERR_NET_NO_BUFFER_SPACE)\n", LOGPARAMS));
1247 }
1248 else if (!virtioCoreIsQueueEnabled(&pThis->Virtio, idxRxQueue))
1249 {
1250 Log8Func(("%s %s queue not enabled (rc = VERR_NET_NO_BUFFER_SPACE)\n", LOGPARAMS));
1251 }
1252 else if (virtioCoreQueueIsEmpty(pDevIns, &pThis->Virtio, idxRxQueue))
1253 {
1254 Log8Func(("%s %s queue is empty (rc = VERR_NET_NO_BUFFER_SPACE)\n", LOGPARAMS));
1255 virtioCoreQueueSetNotify(&pThis->Virtio, idxRxQueue, true);
1256 }
1257 else
1258 {
1259 Log8Func(("%s %s ready with available buffers\n", LOGPARAMS));
1260 virtioCoreQueueSetNotify(&pThis->Virtio, idxRxQueue, false);
1261 return VINF_SUCCESS;
1262 }
1263 return VERR_NET_NO_BUFFER_SPACE;
1264}
1265
1266
1267static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis)
1268{
1269 /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
1270 selection algorithm feasible or even necessary to prevent starvation? */
1271 for (int idxQueuePair = 0; idxQueuePair < pThis->cVirtqPairs; idxQueuePair++)
1272 if (RT_SUCCESS(virtioNetR3IsRxQueuePrimed(pDevIns, pThis, RXQIDX(idxQueuePair))))
1273 return true;
1274 return false;
1275}
1276/*
1277 * Returns true if VirtIO core and device are in a running and operational state
1278 */
1279DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
1280{
1281 if (!pThis->fVirtioReady)
1282 return false;
1283
1284 if (pThis->fQuiescing)
1285 return false;
1286
1287 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1288 if (!RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
1289 return false;
1290
1291 return true;
1292}
1293
1294/**
1295 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
1296 */
1297static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
1298{
1299 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1300 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1301 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1302
1303 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis))
1304 {
1305 Log10Func(("%s Rx bufs now available, releasing waiter...\n", INSTANCE(pThis)));
1306 return VINF_SUCCESS;
1307 }
1308 if (!timeoutMs)
1309 return VERR_NET_NO_BUFFER_SPACE;
1310
1311 LogFlowFunc(("%s timeoutMs=%u\n", INSTANCE(pThis), timeoutMs));
1312
1313 ASMAtomicXchgBool(&pThis->fLeafWantsRxBuffers, true);
1314 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
1315
1316 do {
1317 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis))
1318 {
1319 Log10Func(("%s Rx bufs now available, releasing waiter...\n", INSTANCE(pThis)));
1320 return VINF_SUCCESS;
1321 }
1322 Log9Func(("%s Starved for guest Rx bufs, waiting %u ms ...\n",
1323 INSTANCE(pThis), timeoutMs));
1324
1325 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
1326
1327 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
1328 continue;
1329
1330 if (RT_FAILURE(rc))
1331 RTThreadSleep(1);
1332
1333 } while (virtioNetIsOperational(pThis, pDevIns));
1334
1335 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
1336 ASMAtomicXchgBool(&pThis->fLeafWantsRxBuffers, false);
1337
1338 Log7Func(("%s Wait for Rx buffers available was interrupted\n", INSTANCE(pThis)));
1339 return VERR_INTERRUPTED;
1340}
1341
1342
1343/**
1344 * Sets up the GSO context according to the Virtio header.
1345 *
1346 * @param pGso The GSO context to setup.
1347 * @param pCtx The context descriptor.
1348 */
1349DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONET_PKT_HDR_T const *pPktHdr)
1350{
1351 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1352
1353 if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
1354 {
1355 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
1356 return NULL;
1357 }
1358 switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
1359 {
1360 case VIRTIONET_HDR_GSO_TCPV4:
1361 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
1362 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1363 break;
1364 case VIRTIONET_HDR_GSO_TCPV6:
1365 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
1366 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1367 break;
1368 case VIRTIONET_HDR_GSO_UDP:
1369 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
1370 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
1371 break;
1372 default:
1373 return NULL;
1374 }
1375 if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
1376 pGso->offHdr2 = pPktHdr->uChksumStart;
1377 else
1378 {
1379 AssertMsgFailed(("GSO without checksum offloading!\n"));
1380 return NULL;
1381 }
1382 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1383 pGso->cbHdrsTotal = pPktHdr->uHdrLen;
1384 pGso->cbMaxSeg = pPktHdr->uGsoSize;
1385 return pGso;
1386}
1387
1388/**
1389 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
1390 */
1391static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1392{
1393 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
1394 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
1395 memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
1396 return VINF_SUCCESS;
1397}
1398
1399/**
1400 * Returns true if it is a broadcast packet.
1401 *
1402 * @returns true if destination address indicates broadcast.
1403 * @param pvBuf The ethernet packet.
1404 */
1405DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
1406{
1407 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1408 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
1409}
1410
1411/**
1412 * Returns true if it is a multicast packet.
1413 *
1414 * @remarks returns true for broadcast packets as well.
1415 * @returns true if destination address indicates multicast.
1416 * @param pvBuf The ethernet packet.
1417 */
1418DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
1419{
1420 return (*(char*)pvBuf) & 1;
1421}
1422/**
1423 * Determines if the packet is to be delivered to upper layer.
1424 *
1425 * @returns true if packet is intended for this node.
1426 * @param pThis Pointer to the state structure.
1427 * @param pvBuf The ethernet packet.
1428 * @param cb Number of bytes available in the packet.
1429 */
1430static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
1431{
1432
1433 if (LogIs11Enabled())
1434 {
1435 char *pszType;
1436 if (virtioNetR3IsMulticast(pvBuf))
1437 pszType = (char *)"Multicast";
1438 else if (virtioNetR3IsBroadcast(pvBuf))
1439 pszType = (char *)"Broadcast";
1440 else
1441 pszType = (char *)"Unicast";
1442
1443 LogFunc(("%s node(%RTmac %s%s), pkt(%RTmac %s)",
1444 INSTANCE(pThis), pThis->virtioNetConfig.uMacAddress.au8,
1445 pThis->fPromiscuous ? "promiscuous" : "",
1446 pThis->fAllMulticast ? " all-multicast" : "",
1447 pvBuf, pszType));
1448 }
1449
1450 if (pThis->fPromiscuous)
1451 return true;
1452
1453 /* Ignore everything outside of our VLANs */
1454 uint16_t *uPtr = (uint16_t *)pvBuf;
1455
1456 /* Compare TPID with VLAN Ether Type */
1457 if ( uPtr[6] == RT_H2BE_U16(0x8100)
1458 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
1459 {
1460 Log11Func(("\n%s not our VLAN, returning false\n", INSTANCE(pThis)));
1461 return false;
1462 }
1463
1464 if (virtioNetR3IsBroadcast(pvBuf))
1465 {
1466 Log11(("... accept (broadcast)\n"));
1467 if (LogIs12Enabled())
1468 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1469 return true;
1470 }
1471 if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
1472 {
1473 Log11(("... accept (all-multicast mode)\n"));
1474 if (LogIs12Enabled())
1475 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1476 return true;
1477 }
1478
1479 if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
1480 {
1481 Log11((". . . accept (direct to this node)\n"));
1482 if (LogIs12Enabled())
1483 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1484 return true;
1485 }
1486
1487 for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
1488 {
1489 if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
1490 {
1491 Log11(("... accept (in multicast array)\n"));
1492 if (LogIs12Enabled())
1493 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1494 return true;
1495 }
1496 }
1497
1498 for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
1499 if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
1500 {
1501 Log11(("... accept (in unicast array)\n"));
1502 return true;
1503 }
1504
1505 if (LogIs12Enabled())
1506 Log(("... reject\n"));
1507
1508 return false;
1509}
1510
1511static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, const void *pvBuf, size_t cb,
1512 VIRTIONET_PKT_HDR_T *rxPktHdr, uint16_t cSegsAllocated,
1513 PRTSGBUF pVirtSegBufToGuest, PRTSGSEG paVirtSegsToGuest,
1514 uint16_t idxRxQueue)
1515{
1516 uint8_t fAddPktHdr = true;
1517 RTGCPHYS gcPhysPktHdrNumBuffers = 0;
1518 uint16_t cDescs;
1519 uint64_t uOffset;
1520 for (cDescs = uOffset = 0; uOffset < cb; )
1521 {
1522 PVIRTIO_DESC_CHAIN_T pDescChain = NULL;
1523
1524 int rc = virtioCoreR3QueueGet(pDevIns, &pThis->Virtio, RXQIDX(idxRxQueue), &pDescChain, true);
1525 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
1526
1527 /** @todo Find a better way to deal with this */
1528 AssertMsgReturnStmt(rc == VINF_SUCCESS && pDescChain->cbPhysReturn,
1529 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
1530 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain),
1531 VERR_INTERNAL_ERROR);
1532
1533 /* Length of first seg of guest Rx buf should never be less than sizeof(virtio_net_pkt_hdr).
1534 * Otherwise code has to become more complicated, e.g. locate & cache seg idx & offset of
1535 * virtio_net_header.num_buffers, to defer updating (in gcPhys). Re-visit if needed */
1536
1537 AssertMsgReturnStmt(pDescChain->pSgPhysReturn->paSegs[0].cbSeg >= sizeof(VIRTIONET_PKT_HDR_T),
1538 ("Desc chain's first seg has insufficient space for pkt header!\n"),
1539 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain),
1540 VERR_INTERNAL_ERROR);
1541
1542 size_t cbDescChainLeft = pDescChain->cbPhysReturn;
1543 uint8_t cbHdr = sizeof(VIRTIONET_PKT_HDR_T);
1544
1545 /* Fill the Guest Rx buffer with data received from the interface */
1546 for (uint16_t cSegs = 0; uOffset < cb && cbDescChainLeft; )
1547 {
1548 if (fAddPktHdr)
1549 {
1550 /* Lead with packet header */
1551 paVirtSegsToGuest[0].cbSeg = cbHdr;
1552 paVirtSegsToGuest[0].pvSeg = RTMemAlloc(cbHdr);
1553 AssertReturn(paVirtSegsToGuest[0].pvSeg, VERR_NO_MEMORY);
1554 cbDescChainLeft -= cbHdr;
1555
1556 memcpy(paVirtSegsToGuest[0].pvSeg, rxPktHdr, cbHdr);
1557
1558 /* Calculate & cache addr of field to update after final value is known, in gcPhys mem */
1559 gcPhysPktHdrNumBuffers = pDescChain->pSgPhysReturn->paSegs[0].gcPhys
1560 + RT_UOFFSETOF(VIRTIONET_PKT_HDR_T, uNumBuffers);
1561 fAddPktHdr = false;
1562 cSegs++;
1563 }
1564
1565 if (cSegs >= cSegsAllocated)
1566 {
1567 cSegsAllocated <<= 1; /* double allocation size */
1568 paVirtSegsToGuest = (PRTSGSEG)RTMemRealloc(paVirtSegsToGuest, sizeof(RTSGSEG) * cSegsAllocated);
1569 if (!paVirtSegsToGuest)
1570 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain);
1571 AssertReturn(paVirtSegsToGuest, VERR_NO_MEMORY);
1572 }
1573
1574 /* Append remaining Rx pkt or as much current desc chain has room for */
1575 size_t cbCropped = RT_MIN(cb, cbDescChainLeft);
1576 paVirtSegsToGuest[cSegs].cbSeg = cbCropped;
1577 paVirtSegsToGuest[cSegs].pvSeg = ((uint8_t *)pvBuf) + uOffset;
1578 cbDescChainLeft -= cbCropped;
1579 uOffset += cbCropped;
1580 cDescs++;
1581 cSegs++;
1582 RTSgBufInit(pVirtSegBufToGuest, paVirtSegsToGuest, cSegs);
1583 Log7Func(("Send Rx pkt to guest...\n"));
1584 STAM_PROFILE_START(&pThis->StatReceiveStore, a);
1585 virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, idxRxQueue,
1586 pVirtSegBufToGuest, pDescChain, true);
1587 STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
1588
1589 if (FEATURE_DISABLED(MRG_RXBUF))
1590 break;
1591 }
1592
1593 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain);
1594 }
1595
1596 if (uOffset < cb)
1597 {
1598 LogFunc(("%s Packet did not fit into RX queue (packet size=%u)!\n", INSTANCE(pThis), cb));
1599 return VERR_TOO_MUCH_DATA;
1600 }
1601
1602 /* Fix-up pkthdr (in guest phys. memory) with number buffers (descriptors) processed */
1603
1604 int rc = PDMDevHlpPCIPhysWrite(pDevIns, gcPhysPktHdrNumBuffers, &cDescs, sizeof(cDescs));
1605 AssertMsgRCReturn(rc,
1606 ("Failure updating descriptor count in pkt hdr in guest physical memory\n"),
1607 rc);
1608
1609 /** @todo WHY *must* we *force* notifying guest that we filled its Rx buffer(s)?
1610 * If we don't notify the guest, it doesn't detect it and stalls, even though
1611 * guest is responsible for setting the used-ring flag in the Rx queue that tells
1612 * us to skip the notification interrupt! Obviously forcing the interrupt is
1613 * non-optimal performance-wise and seems to contradict the Virtio spec.
1614 * Is that a bug in the linux virtio_net.c driver? */
1615
1616 virtioCoreQueueSync(pDevIns, &pThis->Virtio, RXQIDX(idxRxQueue), /* fForce */ true);
1617
1618 return VINF_SUCCESS;
1619}
1620
1621
1622/**
1623 * Pad and store received packet.
1624 *
1625 * @remarks Make sure that the packet appears to upper layer as one coming
1626 * from real Ethernet: pad it and insert FCS.
1627 *
1628 * @returns VBox status code.
1629 * @param pDevIns The device instance.
1630 * @param pThis The virtio-net shared instance data.
1631 * @param pvBuf The available data.
1632 * @param cb Number of bytes available in the buffer.
1633 * @param pGso Pointer to Global Segmentation Offload structure
1634 * @param idxRxQueue Rx queue to work with
1635 * @thread RX
1636 */
1637static int virtioNetR3HandleRxPacket(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1638 const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso, uint16_t idxRxQueue)
1639{
1640 RT_NOREF(pThisCC);
1641
1642 LogFunc(("%s (%RTmac) pGso %s\n", INSTANCE(pThis), pvBuf, pGso ? "present" : "not present"));
1643 VIRTIONET_PKT_HDR_T rxPktHdr = { 0 };
1644
1645 if (pGso)
1646 {
1647 Log2Func(("%s gso type=%x cbPktHdrsTotal=%u cbPktHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
1648 INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal,
1649 pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1650
1651 rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
1652 switch (pGso->u8Type)
1653 {
1654 case PDMNETWORKGSOTYPE_IPV4_TCP:
1655 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
1656 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1657 break;
1658 case PDMNETWORKGSOTYPE_IPV6_TCP:
1659 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
1660 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1661 break;
1662 case PDMNETWORKGSOTYPE_IPV4_UDP:
1663 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
1664 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
1665 break;
1666 default:
1667 return VERR_INVALID_PARAMETER;
1668 }
1669 rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
1670 rxPktHdr.uGsoSize = pGso->cbMaxSeg;
1671 rxPktHdr.uChksumStart = pGso->offHdr2;
1672 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
1673 }
1674 else
1675 {
1676 rxPktHdr.uFlags = 0;
1677 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_NONE;
1678 }
1679
1680 uint16_t cSegsAllocated = VIRTIONET_PREALLOCATE_RX_SEG_COUNT;
1681
1682 PRTSGBUF pVirtSegBufToGuest = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
1683 AssertReturn(pVirtSegBufToGuest, VERR_NO_MEMORY);
1684
1685 PRTSGSEG paVirtSegsToGuest = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * cSegsAllocated);
1686 AssertReturnStmt(paVirtSegsToGuest, RTMemFree(pVirtSegBufToGuest), VERR_NO_MEMORY);
1687
1688 int rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pvBuf, cb, &rxPktHdr, cSegsAllocated,
1689 pVirtSegBufToGuest, paVirtSegsToGuest, idxRxQueue);
1690
1691 RTMemFree(paVirtSegsToGuest);
1692 RTMemFree(pVirtSegBufToGuest);
1693
1694 Log7(("\n"));
1695 return rc;
1696}
1697
1698/**
1699 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
1700 */
1701static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf,
1702 size_t cb, PCPDMNETWORKGSO pGso)
1703{
1704 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1705 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1706 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1707
1708 if (!pThis->fVirtioReady)
1709 {
1710 LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
1711 return VERR_INTERRUPTED;
1712 }
1713 if (pThis->fQuiescing)
1714 {
1715 LogRelFunc(("Quiescing I/O for suspend or power off, aborting downstream receive\n"));
1716 return VERR_INTERRUPTED;
1717 }
1718
1719 if (pGso)
1720 {
1721 uint32_t uFeatures = pThis->fNegotiatedFeatures;
1722
1723 switch (pGso->u8Type)
1724 {
1725 case PDMNETWORKGSOTYPE_IPV4_TCP:
1726 uFeatures &= VIRTIONET_F_GUEST_TSO4;
1727 break;
1728 case PDMNETWORKGSOTYPE_IPV6_TCP:
1729 uFeatures &= VIRTIONET_F_GUEST_TSO6;
1730 break;
1731 case PDMNETWORKGSOTYPE_IPV4_UDP:
1732 case PDMNETWORKGSOTYPE_IPV6_UDP:
1733 uFeatures &= VIRTIONET_F_GUEST_UFO;
1734 break;
1735 default:
1736 uFeatures = 0;
1737 break;
1738 }
1739 if (!uFeatures)
1740 {
1741 Log2Func(("%s GSO type (0x%x) not supported\n", INSTANCE(pThis), pGso->u8Type));
1742 return VERR_NOT_SUPPORTED;
1743 }
1744 }
1745
1746 Log10Func(("%s pvBuf=%p cb=%3u pGso=%p ...", INSTANCE(pThis), pvBuf, cb, pGso));
1747
1748 /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
1749 selection algorithm feasible or even necessary to prevent starvation? */
1750
1751 for (int idxQueuePair = 0; idxQueuePair < pThis->cVirtqPairs; idxQueuePair++)
1752 {
1753 if (RT_SUCCESS(!virtioNetR3IsRxQueuePrimed(pDevIns, pThis, RXQIDX(idxQueuePair))))
1754 {
1755 /* Drop packets if VM is not running or cable is disconnected. */
1756 if (!virtioNetIsOperational(pThis, pDevIns) || !IS_LINK_UP(pThis))
1757 return VINF_SUCCESS;
1758
1759 STAM_PROFILE_START(&pThis->StatReceive, a);
1760 virtioNetR3SetReadLed(pThisCC, true);
1761
1762 int rc = VINF_SUCCESS;
1763 if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
1764 {
1765 rc = virtioNetR3HandleRxPacket(pDevIns, pThis, pThisCC, pvBuf, cb, pGso, RXQIDX(idxQueuePair));
1766 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
1767 }
1768
1769 virtioNetR3SetReadLed(pThisCC, false);
1770 STAM_PROFILE_STOP(&pThis->StatReceive, a);
1771 return rc;
1772 }
1773 }
1774 return VERR_INTERRUPTED;
1775}
1776
1777/**
1778 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
1779 */
1780static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
1781{
1782 return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
1783}
1784
1785/* Read physical bytes from the out segment(s) of descriptor chain */
1786static void virtioNetR3PullChain(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIO_DESC_CHAIN_T pDescChain, void *pv, size_t cb)
1787{
1788 uint8_t *pb = (uint8_t *)pv;
1789 size_t cbLim = RT_MIN(pDescChain->cbPhysSend, cb);
1790 while (cbLim)
1791 {
1792 size_t cbSeg = cbLim;
1793 RTGCPHYS GCPhys = virtioCoreSgBufGetNextSegment(pDescChain->pSgPhysSend, &cbSeg);
1794 PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pb, cbSeg);
1795 pb += cbSeg;
1796 cbLim -= cbSeg;
1797 pDescChain->cbPhysSend -= cbSeg;
1798 }
1799 LogFunc(("%s Pulled %d/%d bytes from desc chain (%d bytes left)\n",
1800 INSTANCE(pThis), cb - cbLim, cb, pDescChain->cbPhysSend));
1801 RT_NOREF(pThis);
1802}
1803
1804static uint8_t virtioNetR3CtrlRx(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1805 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1806{
1807
1808#define LOG_VIRTIONET_FLAG(fld) LogFunc(("%s Setting %s=%d\n", INSTANCE(pThis), #fld, pThis->fld))
1809
1810 LogFunc(("%s Processing CTRL Rx command\n", INSTANCE(pThis)));
1811 RT_NOREF(pThis);
1812 switch(pCtrlPktHdr->uCmd)
1813 {
1814 case VIRTIONET_CTRL_RX_PROMISC:
1815 break;
1816 case VIRTIONET_CTRL_RX_ALLMULTI:
1817 break;
1818 case VIRTIONET_CTRL_RX_ALLUNI:
1819 /* fallthrough */
1820 case VIRTIONET_CTRL_RX_NOMULTI:
1821 /* fallthrough */
1822 case VIRTIONET_CTRL_RX_NOUNI:
1823 /* fallthrough */
1824 case VIRTIONET_CTRL_RX_NOBCAST:
1825 AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
1826 ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
1827 VIRTIONET_ERROR);
1828 /* fall out */
1829 }
1830
1831 uint8_t fOn, fPromiscChanged = false;
1832 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &fOn, (size_t)RT_MIN(pDescChain->cbPhysSend, sizeof(fOn)));
1833
1834 switch(pCtrlPktHdr->uCmd)
1835 {
1836 case VIRTIONET_CTRL_RX_PROMISC:
1837 pThis->fPromiscuous = RT_BOOL(fOn);
1838 fPromiscChanged = true;
1839 LOG_VIRTIONET_FLAG(fPromiscuous);
1840 break;
1841 case VIRTIONET_CTRL_RX_ALLMULTI:
1842 pThis->fAllMulticast = RT_BOOL(fOn);
1843 fPromiscChanged = true;
1844 LOG_VIRTIONET_FLAG(fAllMulticast);
1845 break;
1846 case VIRTIONET_CTRL_RX_ALLUNI:
1847 pThis->fAllUnicast = RT_BOOL(fOn);
1848 LOG_VIRTIONET_FLAG(fAllUnicast);
1849 break;
1850 case VIRTIONET_CTRL_RX_NOMULTI:
1851 pThis->fNoMulticast = RT_BOOL(fOn);
1852 LOG_VIRTIONET_FLAG(fNoMulticast);
1853 break;
1854 case VIRTIONET_CTRL_RX_NOUNI:
1855 pThis->fNoUnicast = RT_BOOL(fOn);
1856 LOG_VIRTIONET_FLAG(fNoUnicast);
1857 break;
1858 case VIRTIONET_CTRL_RX_NOBCAST:
1859 pThis->fNoBroadcast = RT_BOOL(fOn);
1860 LOG_VIRTIONET_FLAG(fNoBroadcast);
1861 break;
1862 }
1863
1864 if (pThisCC->pDrv && fPromiscChanged)
1865 if (pThis->fPromiscuous | pThis->fAllMulticast)
1866 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
1867 else
1868 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, false);
1869
1870 return VIRTIONET_OK;
1871}
1872
1873static uint8_t virtioNetR3CtrlMac(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1874 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1875{
1876 LogFunc(("%s Processing CTRL MAC command\n", INSTANCE(pThis)));
1877
1878 RT_NOREF(pThisCC);
1879
1880#define ASSERT_CTRL_ADDR_SET(v) \
1881 AssertMsgReturn((v), ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR)
1882
1883#define ASSERT_CTRL_TABLE_SET(v) \
1884 AssertMsgReturn((v), ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR)
1885
1886 AssertMsgReturn(pDescChain->cbPhysSend >= sizeof(*pCtrlPktHdr),
1887 ("insufficient descriptor space for ctrl pkt hdr"),
1888 VIRTIONET_ERROR);
1889
1890 size_t cbRemaining = pDescChain->cbPhysSend;
1891 switch(pCtrlPktHdr->uCmd)
1892 {
1893 case VIRTIONET_CTRL_MAC_ADDR_SET:
1894 {
1895 /* Set default Rx filter MAC */
1896 ASSERT_CTRL_ADDR_SET(cbRemaining >= sizeof(VIRTIONET_CTRL_MAC_TABLE_LEN));
1897 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &pThis->rxFilterMacDefault, sizeof(VIRTIONET_CTRL_MAC_TABLE_LEN));
1898 break;
1899 }
1900 case VIRTIONET_CTRL_MAC_TABLE_SET:
1901 {
1902 VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
1903
1904 /* Load unicast MAC filter table */
1905 ASSERT_CTRL_TABLE_SET(cbRemaining >= sizeof(cMacs));
1906 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &cMacs, sizeof(cMacs));
1907 cbRemaining -= sizeof(cMacs);
1908 Log7Func(("%s Guest provided %d unicast MAC Table entries\n", INSTANCE(pThis), cMacs));
1909 if (cMacs)
1910 {
1911 uint32_t cbMacs = cMacs * sizeof(RTMAC);
1912 ASSERT_CTRL_TABLE_SET(cbRemaining >= cbMacs);
1913 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &pThis->aMacUnicastFilter, cbMacs);
1914 cbRemaining -= cbMacs;
1915 }
1916 pThis->cUnicastFilterMacs = cMacs;
1917
1918 /* Load multicast MAC filter table */
1919 ASSERT_CTRL_TABLE_SET(cbRemaining >= sizeof(cMacs));
1920 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &cMacs, sizeof(cMacs));
1921 cbRemaining -= sizeof(cMacs);
1922 Log10Func(("%s Guest provided %d multicast MAC Table entries\n", INSTANCE(pThis), cMacs));
1923 if (cMacs)
1924 {
1925 uint32_t cbMacs = cMacs * sizeof(RTMAC);
1926 ASSERT_CTRL_TABLE_SET(cbRemaining >= cbMacs);
1927 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &pThis->aMacMulticastFilter, cbMacs);
1928 cbRemaining -= cbMacs;
1929 }
1930 pThis->cMulticastFilterMacs = cMacs;
1931
1932#ifdef LOG_ENABLED
1933 LogFunc(("%s unicast MACs:\n", INSTANCE(pThis)));
1934 for(unsigned i = 0; i < cMacs; i++)
1935 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
1936
1937 LogFunc(("%s multicast MACs:\n", INSTANCE(pThis)));
1938 for(unsigned i = 0; i < cMacs; i++)
1939 LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
1940#endif
1941 }
1942 }
1943 return VIRTIONET_OK;
1944}
1945
1946static uint8_t virtioNetR3CtrlVlan(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1947 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1948{
1949 LogFunc(("%s Processing CTRL VLAN command\n", INSTANCE(pThis)));
1950
1951 RT_NOREF(pThisCC);
1952
1953 uint16_t uVlanId;
1954 size_t cbRemaining = pDescChain->cbPhysSend - sizeof(*pCtrlPktHdr);
1955 AssertMsgReturn(cbRemaining > sizeof(uVlanId),
1956 ("DESC chain too small for VIRTIO_NET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
1957 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &uVlanId, sizeof(uVlanId));
1958 AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
1959 ("%s VLAN ID out of range (VLAN ID=%u)\n", INSTANCE(pThis), uVlanId), VIRTIONET_ERROR);
1960 LogFunc(("%s uCommand=%u VLAN ID=%u\n", INSTANCE(pThis), pCtrlPktHdr->uCmd, uVlanId));
1961 switch (pCtrlPktHdr->uCmd)
1962 {
1963 case VIRTIONET_CTRL_VLAN_ADD:
1964 ASMBitSet(pThis->aVlanFilter, uVlanId);
1965 break;
1966 case VIRTIONET_CTRL_VLAN_DEL:
1967 ASMBitClear(pThis->aVlanFilter, uVlanId);
1968 break;
1969 default:
1970 return VIRTIONET_ERROR;
1971 }
1972 return VIRTIONET_OK;
1973}
1974
1975static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1976 PVIRTIO_DESC_CHAIN_T pDescChain)
1977{
1978 LogFunc(("%s Received CTRL packet from guest\n", INSTANCE(pThis)));
1979
1980 if (pDescChain->cbPhysSend < 2)
1981 {
1982 LogFunc(("%s CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", INSTANCE(pThis)));
1983 return;
1984 }
1985 else if (pDescChain->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
1986 {
1987 LogFunc(("%s Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", INSTANCE(pThis)));
1988 return;
1989 }
1990
1991 /*
1992 * Allocate buffer and read in the control command
1993 */
1994 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr = (PVIRTIONET_CTRL_HDR_T)RTMemAllocZ(sizeof(VIRTIONET_CTRL_HDR_T));
1995
1996 AssertPtrReturnVoid(pCtrlPktHdr);
1997
1998 AssertMsgReturnVoid(pDescChain->cbPhysSend >= sizeof(VIRTIONET_CTRL_HDR_T),
1999 ("DESC chain too small for CTRL pkt header"));
2000
2001 virtioNetR3PullChain(pDevIns, pThis, pDescChain, pCtrlPktHdr,
2002 RT_MIN(pDescChain->cbPhysSend, sizeof(VIRTIONET_CTRL_HDR_T)));
2003
2004 Log7Func(("%s CTRL COMMAND: class=%d command=%d\n", INSTANCE(pThis), pCtrlPktHdr->uClass, pCtrlPktHdr->uCmd));
2005
2006 uint8_t uAck;
2007 switch (pCtrlPktHdr->uClass)
2008 {
2009 case VIRTIONET_CTRL_RX:
2010 uAck = virtioNetR3CtrlRx(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
2011 break;
2012 case VIRTIONET_CTRL_MAC:
2013 uAck = virtioNetR3CtrlMac(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
2014 break;
2015 case VIRTIONET_CTRL_VLAN:
2016 uAck = virtioNetR3CtrlVlan(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
2017 break;
2018 case VIRTIONET_CTRL_ANNOUNCE:
2019 uAck = VIRTIONET_OK;
2020 if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
2021 {
2022 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
2023 "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", INSTANCE(pThis)));
2024 break;
2025 }
2026 if (pCtrlPktHdr->uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
2027 {
2028 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", INSTANCE(pThis)));
2029 break;
2030 }
2031 pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
2032 Log7Func(("%s Clearing VIRTIONET_F_ANNOUNCE in config status\n", INSTANCE(pThis)));
2033 break;
2034
2035 default:
2036 LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", pCtrlPktHdr->uClass));
2037 uAck = VIRTIONET_ERROR;
2038 }
2039
2040 /* Currently CTRL pkt header just returns ack, but keeping segment logic generic/flexible
2041 * in case that changes to make adapting more straightforward */
2042 int cSegs = 1;
2043
2044 /* Return CTRL packet Ack byte (result code) to guest driver */
2045 PRTSGSEG paReturnSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG));
2046 AssertMsgReturnVoid(paReturnSegs, ("Out of memory"));
2047
2048 RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
2049 memcpy(paReturnSegs, aStaticSegs, sizeof(RTSGSEG));
2050
2051 PRTSGBUF pReturnSegBuf = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
2052 AssertMsgReturnVoid(pReturnSegBuf, ("Out of memory"));
2053
2054 /* Copy segment data to malloc'd memory to avoid stack out-of-scope errors sanitizer doesn't detect */
2055 for (int i = 0; i < cSegs; i++)
2056 {
2057 void *pv = paReturnSegs[i].pvSeg;
2058 paReturnSegs[i].pvSeg = RTMemAlloc(aStaticSegs[i].cbSeg);
2059 AssertMsgReturnVoid(paReturnSegs[i].pvSeg, ("Out of memory"));
2060 memcpy(paReturnSegs[i].pvSeg, pv, aStaticSegs[i].cbSeg);
2061 }
2062
2063 RTSgBufInit(pReturnSegBuf, paReturnSegs, cSegs);
2064
2065 virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, CTRLQIDX, pReturnSegBuf, pDescChain, true);
2066 virtioCoreQueueSync(pDevIns, &pThis->Virtio, CTRLQIDX, false);
2067
2068 for (int i = 0; i < cSegs; i++)
2069 RTMemFree(paReturnSegs[i].pvSeg);
2070
2071 RTMemFree(paReturnSegs);
2072 RTMemFree(pReturnSegBuf);
2073
2074 LogFunc(("%s Finished processing CTRL command with status %s\n",
2075 INSTANCE(pThis), uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
2076}
2077
2078static int virtioNetR3ReadHeader(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONET_PKT_HDR_T pPktHdr, size_t cbFrame)
2079{
2080 int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pPktHdr, sizeof(*pPktHdr));
2081 if (RT_FAILURE(rc))
2082 return rc;
2083
2084 Log(("virtio-net: header (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
2085 pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
2086 pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
2087
2088 if (pPktHdr->uGsoType)
2089 {
2090 uint32_t uMinHdrSize;
2091
2092 /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
2093 AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2094 && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
2095 ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
2096
2097 switch (pPktHdr->uGsoType)
2098 {
2099 case VIRTIONET_HDR_GSO_TCPV4:
2100 case VIRTIONET_HDR_GSO_TCPV6:
2101 uMinHdrSize = sizeof(RTNETTCP);
2102 break;
2103 case VIRTIONET_HDR_GSO_UDP:
2104 uMinHdrSize = 0;
2105 break;
2106 default:
2107 LogFunc(("Bad GSO type in packet header\n"));
2108 return VERR_INVALID_PARAMETER;
2109 }
2110 /* Header + MSS must not exceed the packet size. */
2111 AssertMsgReturn(RT_LIKELY(uMinHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
2112 ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
2113 }
2114
2115 AssertMsgReturn( !pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM
2116 || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
2117 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
2118 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
2119 VERR_BUFFER_OVERFLOW);
2120
2121 return VINF_SUCCESS;
2122}
2123
2124static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
2125 PPDMNETWORKGSO pGso, PVIRTIONET_PKT_HDR_T pPktHdr)
2126{
2127 virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
2128 if (pGso)
2129 {
2130 /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
2131 /*
2132 * We cannot use cdHdrs provided by the guest because of different ways
2133 * it gets filled out by different versions of kernels.
2134 */
2135 //if (pGso->cbHdrs < pPktHdr->uCSumStart + pPktHdr->uCSumOffset + 2)
2136 {
2137 Log4Func(("%s HdrLen before adjustment %d.\n",
2138 INSTANCE(pThis), pGso->cbHdrsTotal));
2139 switch (pGso->u8Type)
2140 {
2141 case PDMNETWORKGSOTYPE_IPV4_TCP:
2142 case PDMNETWORKGSOTYPE_IPV6_TCP:
2143 pGso->cbHdrsTotal = pPktHdr->uChksumStart +
2144 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
2145 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
2146 break;
2147 case PDMNETWORKGSOTYPE_IPV4_UDP:
2148 pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
2149 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
2150 break;
2151 }
2152 /* Update GSO structure embedded into the frame */
2153 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
2154 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
2155 Log4Func(("%s adjusted HdrLen to %d.\n",
2156 INSTANCE(pThis), pGso->cbHdrsTotal));
2157 }
2158 Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
2159 INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2160 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2161 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
2162 }
2163 else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2164 {
2165 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
2166 /*
2167 * This is not GSO frame but checksum offloading is requested.
2168 */
2169 virtioNetR3CompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
2170 pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
2171 }
2172
2173 return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, false);
2174}
2175
2176static void virtioNetR3TransmitPendingPackets(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2177 uint16_t idxTxQueue, bool fOnWorkerThread)
2178{
2179
2180 PVIRTIOCORE pVirtio = &pThis->Virtio;
2181
2182 /*
2183 * Only one thread is allowed to transmit at a time, others should skip
2184 * transmission as the packets will be picked up by the transmitting
2185 * thread.
2186 */
2187 if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
2188 return;
2189
2190 if (!pThis->fVirtioReady)
2191 {
2192 LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x).\n",
2193 INSTANCE(pThis), pThis->virtioNetConfig.uStatus));
2194 return;
2195 }
2196
2197 if (!pThis->fCableConnected)
2198 {
2199 Log(("%s Ignoring transmit requests while cable is disconnected.\n", INSTANCE(pThis)));
2200 return;
2201 }
2202
2203 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2204 if (pDrv)
2205 {
2206 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
2207 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
2208 if (rc == VERR_TRY_AGAIN)
2209 {
2210 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2211 return;
2212 }
2213 }
2214
2215 int cPkts = virtioCoreR3QueuePendingCount(pVirtio->pDevInsR3, pVirtio, idxTxQueue);
2216 if (!cPkts)
2217 {
2218 LogFunc(("%s No packets to send found on %s\n", INSTANCE(pThis), VIRTQNAME(idxTxQueue)));
2219
2220 if (pDrv)
2221 pDrv->pfnEndXmit(pDrv);
2222
2223 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2224 return;
2225 }
2226 LogFunc(("%s About to transmit %d pending packets\n", INSTANCE(pThis), cPkts));
2227
2228 virtioNetR3SetWriteLed(pThisCC, true);
2229
2230 int rc;
2231 PVIRTIO_DESC_CHAIN_T pDescChain = NULL;
2232 while ((rc = virtioCoreR3QueuePeek(pVirtio->pDevInsR3, pVirtio, idxTxQueue, &pDescChain)) == VINF_SUCCESS)
2233 {
2234 Log10Func(("%s fetched descriptor chain from %s\n", INSTANCE(pThis), VIRTQNAME(idxTxQueue)));
2235
2236 PVIRTIOSGBUF pSgPhysSend = pDescChain->pSgPhysSend;
2237 PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
2238 uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
2239
2240 VIRTIONET_PKT_HDR_T PktHdr;
2241 size_t uSize = 0;
2242
2243 Assert(paSegsFromGuest[0].cbSeg >= sizeof(PktHdr));
2244
2245 /* Compute total frame size. */
2246 for (unsigned i = 0; i < cSegsFromGuest && uSize < VIRTIONET_MAX_FRAME_SIZE; i++)
2247 uSize += paSegsFromGuest[i].cbSeg;
2248
2249 Log5Func(("%s complete frame is %u bytes.\n", INSTANCE(pThis), uSize));
2250 Assert(uSize <= VIRTIONET_MAX_FRAME_SIZE);
2251
2252 /* Truncate oversized frames. */
2253 if (uSize > VIRTIONET_MAX_FRAME_SIZE)
2254 uSize = VIRTIONET_MAX_FRAME_SIZE;
2255
2256 if (pThisCC->pDrv)
2257 {
2258 PDMNETWORKGSO Gso;
2259 PPDMNETWORKGSO pGso = virtioNetR3SetupGsoCtx(&Gso, &PktHdr);
2260 uint64_t uOffset;
2261
2262 PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
2263 rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uSize, pGso, &pSgBufToPdmLeafDevice);
2264 if (RT_SUCCESS(rc))
2265 {
2266 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
2267 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
2268
2269 uSize -= sizeof(PktHdr);
2270 rc = virtioNetR3ReadHeader(pDevIns, paSegsFromGuest[0].gcPhys, &PktHdr, uSize);
2271 if (RT_FAILURE(rc))
2272 return;
2273 virtioCoreSgBufAdvance(pSgPhysSend, sizeof(PktHdr));
2274
2275 size_t cbCopied = 0;
2276 size_t cbTotal = 0;
2277 size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uSize;
2278 uOffset = 0;
2279 while (cbRemain)
2280 {
2281 PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
2282 uint64_t srcSgStart = (uint64_t)paSeg->gcPhys;
2283 uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
2284 uint64_t srcSgCur = (uint64_t)pSgPhysSend->gcPhysCur;
2285 cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
2286 PDMDevHlpPCIPhysRead(pDevIns,
2287 (RTGCPHYS)pSgPhysSend->gcPhysCur,
2288 ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
2289 virtioCoreSgBufAdvance(pSgPhysSend, cbCopied);
2290 cbRemain -= cbCopied;
2291 uOffset += cbCopied;
2292 cbTotal += cbCopied;
2293 }
2294
2295 LogFunc((".... Copied %lu bytes to %lu byte guest buffer, residual=%lu\n",
2296 cbTotal, pDescChain->cbPhysSend, pDescChain->cbPhysSend - cbTotal));
2297
2298 rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, &PktHdr);
2299 if (RT_FAILURE(rc))
2300 {
2301 LogFunc(("%s Failed to transmit frame, rc = %Rrc\n", INSTANCE(pThis), rc));
2302 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2303 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
2304 pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
2305 }
2306 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2307 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
2308 }
2309 else
2310 {
2311 Log4Func(("Failed to allocate S/G buffer: size=%u rc=%Rrc\n", uSize, rc));
2312 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
2313 virtioCoreR3DescChainRelease(pVirtio, pDescChain);
2314 break;
2315 }
2316
2317 /* Remove this descriptor chain from the available ring */
2318 virtioCoreR3QueueSkip(pVirtio, idxTxQueue);
2319
2320 /* No data to return to guest, but call is needed put elem (e.g. desc chain) on used ring */
2321 virtioCoreR3QueuePut(pVirtio->pDevInsR3, pVirtio, idxTxQueue, NULL, pDescChain, false);
2322
2323 /* Update used ring idx and notify guest that we've transmitted the data it sent */
2324 virtioCoreQueueSync(pVirtio->pDevInsR3, pVirtio, idxTxQueue, false);
2325 }
2326
2327 virtioCoreR3DescChainRelease(pVirtio, pDescChain);
2328 pDescChain = NULL;
2329 }
2330 virtioNetR3SetWriteLed(pThisCC, false);
2331
2332 if (pDrv)
2333 pDrv->pfnEndXmit(pDrv);
2334
2335 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2336}
2337
2338/**
2339 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2340 */
2341static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
2342{
2343 LogFunc(("\n"));
2344 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2345 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2346 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2347
2348 STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
2349
2350 /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
2351 selection algorithm feasible or even necessary */
2352 virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, TXQIDX(0), false /*fOnWorkerThread*/);
2353}
2354
2355/**
2356 * @callback_method_impl{FNPDMTHREADDEV}
2357 */
2358static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
2359{
2360 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2361 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2362 uint16_t const idxWorker = (uint16_t)(uintptr_t)pThread->pvUser;
2363 PVIRTIONETWORKER pWorker = &pThis->aWorkers[idxWorker];
2364 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[idxWorker];
2365 uint16_t const idxQueue = pWorkerR3->idxQueue;
2366
2367 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
2368 {
2369 return VINF_SUCCESS;
2370 }
2371 LogFunc(("%s worker thread started for %s\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2372 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, false);
2373 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
2374 {
2375 if (virtioCoreQueueIsEmpty(pDevIns, &pThis->Virtio, idxQueue))
2376 {
2377 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
2378 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, true);
2379 ASMAtomicWriteBool(&pWorker->fSleeping, true);
2380 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
2381 if (!fNotificationSent)
2382 {
2383 Log10Func(("%s %s worker sleeping...\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2384 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
2385 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
2386 STAM_COUNTER_INC(&pThis->StatTransmitByThread);
2387 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
2388 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
2389 return VINF_SUCCESS;
2390 if (rc == VERR_INTERRUPTED)
2391 {
2392 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, false);
2393 continue;
2394 }
2395 Log10Func(("%s %s worker woken\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2396 ASMAtomicWriteBool(&pWorker->fNotified, false);
2397 }
2398 ASMAtomicWriteBool(&pWorker->fSleeping, false);
2399 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, false);
2400 }
2401
2402 /* Dispatch to the handler for the queue this worker is set up to drive */
2403
2404 if (!pThisCC->fQuiescing)
2405 {
2406 if (IS_CTRL_QUEUE(idxQueue))
2407 {
2408 Log10Func(("%s fetching next descriptor chain from %s\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2409 PVIRTIO_DESC_CHAIN_T pDescChain = NULL;
2410 int rc = virtioCoreR3QueueGet(pDevIns, &pThis->Virtio, idxQueue, &pDescChain, true);
2411 if (rc == VERR_NOT_AVAILABLE)
2412 {
2413 Log10Func(("%s Nothing found in %s\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2414 continue;
2415 }
2416 virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pDescChain);
2417 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain);
2418 }
2419 else if (IS_TX_QUEUE(idxQueue))
2420 {
2421 Log10Func(("%s Notified of data to transmit\n", INSTANCE(pThis)));
2422 virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC,
2423 idxQueue, false /* fOnWorkerThread */);
2424 }
2425
2426 /* Rx queues aren't handled by our worker threads. Instead, the PDM network
2427 * leaf driver invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback,
2428 * which waits until notified directly by virtioNetQueueNotified()
2429 * that guest IN buffers have been added to receive virt queue.
2430 */
2431 }
2432 }
2433 return VINF_SUCCESS;
2434}
2435
2436#ifdef USING_CRITICAL_SECTION
2437DECLINLINE(int) virtioNetR3CsEnter(PPDMDEVINS pDevIns, PVIRTIONET pThis, int rcBusy)
2438{
2439 RT_NOREF(pDevIns, pThis, rcBusy);
2440 /* Original DevVirtioNet uses CS in attach/detach/link-up timer/tx timer/transmit */
2441 LogFunc(("%s CS unimplemented. What does the critical section protect in orig driver??", INSTANCE(pThis)));
2442 return VINF_SUCCESS;
2443}
2444
2445DECLINLINE(void) virtioNetR3CsLeave(PPDMDEVINS pDevIns, PVIRTIONET pThis)
2446{
2447 RT_NOREF(pDevIns, pThis);
2448 LogFunc(("%s CS unimplemented. What does the critical section protect in orig driver??", INSTANCE(pThis)));
2449}
2450#endif /* USING_CRITICAL_SECTION */
2451
2452/**
2453 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
2454 */
2455static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
2456{
2457 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2458 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2459 RT_NOREF(pTimer, pvUser);
2460
2461 ENTER_CRITICAL_SECTION;
2462
2463 SET_LINK_UP(pThis);
2464
2465 virtioNetWakeupRxBufWaiter(pDevIns);
2466
2467 LEAVE_CRITICAL_SECTION;
2468
2469 LogFunc(("%s Link is up\n", INSTANCE(pThis)));
2470
2471 if (pThisCC->pDrv)
2472 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
2473}
2474
2475/**
2476 * Takes down the link temporarily if it's current status is up.
2477 *
2478 * This is used during restore and when replumbing the network link.
2479 *
2480 * The temporary link outage is supposed to indicate to the OS that all network
2481 * connections have been lost and that it for instance is appropriate to
2482 * renegotiate any DHCP lease.
2483 *
2484 * @param pDevIns The device instance.
2485 * @param pThis The virtio-net shared instance data.
2486 * @param pThisCC The virtio-net ring-3 instance data.
2487 */
2488static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2489{
2490 if (IS_LINK_UP(pThis))
2491 {
2492 SET_LINK_DOWN(pThis);
2493
2494 /* Restore the link back in 5 seconds. */
2495 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
2496 AssertRC(rc);
2497
2498 LogFunc(("%s Link is down temporarily\n", INSTANCE(pThis)));
2499 }
2500}
2501
2502/**
2503 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
2504 */
2505static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
2506{
2507 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2508 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2509
2510 return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
2511}
2512
2513/**
2514 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
2515 */
2516static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
2517{
2518 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2519 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2520 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2521
2522 bool fCachedLinkIsUp = IS_LINK_UP(pThis);
2523 bool fActiveLinkIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
2524
2525 if (LogIs7Enabled())
2526 {
2527 LogFunc(("%s", INSTANCE(pThis)));
2528 switch(enmState)
2529 {
2530 case PDMNETWORKLINKSTATE_UP:
2531 Log(("UP\n"));
2532 break;
2533 case PDMNETWORKLINKSTATE_DOWN:
2534 Log(("DOWN\n"));
2535 break;
2536 case PDMNETWORKLINKSTATE_DOWN_RESUME:
2537 Log(("DOWN (RESUME)\n"));
2538 break;
2539 default:
2540 Log(("UNKNOWN)\n"));
2541 }
2542 }
2543
2544 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
2545 {
2546 if (fCachedLinkIsUp)
2547 {
2548 /*
2549 * We bother to bring the link down only if it was up previously. The UP link state
2550 * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
2551 */
2552 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
2553 if (pThisCC->pDrv)
2554 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2555 }
2556 }
2557 else if (fActiveLinkIsUp != fCachedLinkIsUp)
2558 {
2559 if (fCachedLinkIsUp)
2560 {
2561 Log(("%s Link is up\n", INSTANCE(pThis)));
2562 pThis->fCableConnected = true;
2563 SET_LINK_UP(pThis);
2564 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2565 }
2566 else /* cached Link state is down */
2567 {
2568 /* The link was brought down explicitly, make sure it won't come up by timer. */
2569 PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
2570 Log(("%s Link is down\n", INSTANCE(pThis)));
2571 pThis->fCableConnected = false;
2572 SET_LINK_DOWN(pThis);
2573 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2574 }
2575 if (pThisCC->pDrv)
2576 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2577 }
2578 return VINF_SUCCESS;
2579}
2580
2581static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2582{
2583 Log10Func(("%s\n", INSTANCE(pThis)));
2584 int rc = VINF_SUCCESS;
2585 for (unsigned idxWorker = 0; idxWorker < pThis->cWorkers; idxWorker++)
2586 {
2587 PVIRTIONETWORKER pWorker = &pThis->aWorkers[idxWorker];
2588 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2589 {
2590 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
2591 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2592 }
2593 if (pThisCC->aWorkers[idxWorker].pThread)
2594 {
2595 int rcThread;
2596 rc = PDMDevHlpThreadDestroy(pDevIns, pThisCC->aWorkers[idxWorker].pThread, &rcThread);
2597 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
2598 AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
2599 pThisCC->aWorkers[idxWorker].pThread = NULL;
2600 }
2601 }
2602 return rc;
2603}
2604
2605static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2606 uint16_t idxWorker, uint16_t idxQueue)
2607{
2608 Log10Func(("%s\n", INSTANCE(pThis)));
2609 int rc = VINF_SUCCESS;
2610 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->aWorkers[idxWorker].hEvtProcess);
2611
2612 if (RT_FAILURE(rc))
2613 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2614 N_("DevVirtioNET: Failed to create SUP event semaphore"));
2615
2616 LogFunc(("creating thread, idxWorker=%d, idxQueue=%d\n", idxWorker, idxQueue));
2617 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->aWorkers[idxWorker].pThread,
2618 (void *)(uintptr_t)idxWorker, virtioNetR3WorkerThread,
2619 virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, VIRTQNAME(idxQueue));
2620 if (rc != VINF_SUCCESS)
2621 {
2622 LogRel(("Error creating thread for Virtual Queue %s: %Rrc\n", VIRTQNAME(idxQueue), rc));
2623 return rc;
2624 }
2625 pThisCC->aWorkers[idxWorker].idxQueue = idxQueue;
2626 pThis->afQueueAttached[idxQueue] = true;
2627 return rc;
2628}
2629
2630static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2631{
2632 Log10Func(("%s\n", INSTANCE(pThis)));
2633
2634 int rc;
2635 uint16_t idxWorker = 0;
2636 for (uint16_t idxQueuePair = 0; idxQueuePair < pThis->cVirtqPairs; idxQueuePair++)
2637 {
2638 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, pThisCC, idxWorker, TXQIDX(idxQueuePair));
2639 AssertRCReturn(rc, rc);
2640 idxWorker++;
2641 }
2642 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, pThisCC, idxWorker, CTRLQIDX);
2643 pThis->cWorkers = idxWorker + 1;
2644 return rc;
2645}
2646/**
2647 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
2648 */
2649static DECLCALLBACK(void) virtioNetR3StatusChanged(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
2650{
2651 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
2652 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
2653
2654 pThis->fVirtioReady = fVirtioReady;
2655
2656 if (fVirtioReady)
2657 {
2658 LogFunc(("%s VirtIO ready\n-----------------------------------------------------------------------------------------\n",
2659 INSTANCE(pThis)));
2660 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
2661
2662 pThis->fResetting = false;
2663 pThisCC->fQuiescing = false;
2664 pThis->fNegotiatedFeatures = virtioCoreGetAcceptedFeatures(pVirtio);
2665#ifdef LOG_ENABLED
2666 virtioPrintFeatures(pVirtio);
2667 virtioNetPrintFeatures(pThis);
2668#endif
2669 for (unsigned idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
2670 {
2671 (void) virtioCoreR3QueueAttach(&pThis->Virtio, idxQueue, VIRTQNAME(idxQueue));
2672 pThis->afQueueAttached[idxQueue] = true;
2673 if (virtioCoreQueueIsEmpty(pThisCC->pDevIns, &pThis->Virtio, idxQueue))
2674 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, true);
2675 }
2676 }
2677 else
2678 {
2679 LogFunc(("%s VirtIO is resetting\n", INSTANCE(pThis)));
2680
2681 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
2682 Log7Func(("%s Link is %s\n", INSTANCE(pThis), pThis->fCableConnected ? "up" : "down"));
2683
2684 pThis->fPromiscuous = true;
2685 pThis->fAllMulticast = false;
2686 pThis->fAllUnicast = false;
2687 pThis->fNoMulticast = false;
2688 pThis->fNoUnicast = false;
2689 pThis->fNoBroadcast = false;
2690 pThis->uIsTransmitting = 0;
2691 pThis->cUnicastFilterMacs = 0;
2692 pThis->cMulticastFilterMacs = 0;
2693
2694 memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
2695 memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
2696 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
2697
2698 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
2699
2700 for (unsigned idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
2701 pThis->afQueueAttached[idxQueue] = false;
2702 }
2703}
2704#endif /* IN_RING3 */
2705
2706/**
2707 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
2708 *
2709 * One harddisk at one port has been unplugged.
2710 * The VM is suspended at this point.
2711 */
2712static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2713{
2714 RT_NOREF(fFlags);
2715
2716 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2717 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2718
2719 Log7Func(("%s\n", INSTANCE(pThis)));
2720 AssertLogRelReturnVoid(iLUN == 0);
2721
2722 ENTER_CRITICAL_SECTION;
2723
2724 RT_NOREF(pThis);
2725
2726 /*
2727 * Zero important members.
2728 */
2729 pThisCC->pDrvBase = NULL;
2730 pThisCC->pDrv = NULL;
2731
2732 LEAVE_CRITICAL_SECTION;
2733}
2734
2735/**
2736 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
2737 *
2738 * This is called when we change block driver.
2739 */
2740static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2741{
2742 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2743 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2744
2745 RT_NOREF(fFlags);
2746
2747 Log7Func(("%s", INSTANCE(pThis)));
2748
2749 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
2750
2751 ENTER_CRITICAL_SECTION;
2752
2753 RT_NOREF(pThis);
2754
2755// int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pDevIns->IBase, &pThisCC->pDrvBase, "Network Port");
2756 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
2757 if (RT_SUCCESS(rc))
2758 {
2759 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2760 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2761 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
2762 }
2763 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2764 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2765 Log(("%s No attached driver!\n", INSTANCE(pThis)));
2766
2767 LEAVE_CRITICAL_SECTION;
2768 return rc;
2769
2770 AssertRelease(!pThisCC->pDrvBase);
2771 return rc;
2772}
2773
2774/**
2775 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
2776 */
2777static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2778{
2779 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
2780 if (iLUN)
2781 return VERR_PDM_LUN_NOT_FOUND;
2782 *ppLed = &pThisR3->led;
2783 return VINF_SUCCESS;
2784}
2785
2786
2787/**
2788 * @interface_method_impl{PDMIBASE,pfnQueryInterface,
2789 */
2790static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2791{
2792 PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
2793
2794 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
2795 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
2796 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
2797 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
2798 return NULL;
2799}
2800
2801/**
2802 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
2803 */
2804static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
2805{
2806 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2807
2808 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2809 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2810
2811 Log(("%s Destroying instance\n", INSTANCE(pThis)));
2812
2813 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
2814 {
2815 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
2816 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
2817 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
2818 }
2819
2820 virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
2821
2822 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
2823
2824 return VINF_SUCCESS;
2825}
2826
2827/**
2828 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2829 */
2830static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2831{
2832 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2833 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2834 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2835 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2836
2837 /*
2838 * Quick initialization of the state data, making sure that the destructor always works.
2839 */
2840 Log7Func(("PDM device instance: %d\n", iInstance));
2841
2842 RTStrPrintf(pThis->szInstanceName, sizeof(pThis->szInstanceName), "VIRTIONET%d", iInstance);
2843
2844 pThisCC->pDevIns = pDevIns;
2845
2846 pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
2847 pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
2848 pThisCC->led.u32Magic = PDMLED_MAGIC;
2849
2850 /* Interfaces */
2851 pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
2852 pThisCC->INetworkDown.pfnReceive = virtioNetR3NetworkDown_Receive;
2853 pThisCC->INetworkDown.pfnReceiveGso = virtioNetR3NetworkDown_ReceiveGso;
2854 pThisCC->INetworkDown.pfnXmitPending = virtioNetR3NetworkDown_XmitPending;
2855 pThisCC->INetworkConfig.pfnGetMac = virtioNetR3NetworkConfig_GetMac;
2856 pThisCC->INetworkConfig.pfnGetLinkState = virtioNetR3NetworkConfig_GetLinkState;
2857 pThisCC->INetworkConfig.pfnSetLinkState = virtioNetR3NetworkConfig_SetLinkState;
2858
2859 /*
2860 * Validate configuration.
2861 */
2862 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo", "");
2863
2864 /* Get config params */
2865 int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
2866 if (RT_FAILURE(rc))
2867 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
2868
2869 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
2870 if (RT_FAILURE(rc))
2871 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
2872
2873 uint32_t uStatNo = iInstance;
2874 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
2875 if (RT_FAILURE(rc))
2876 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
2877
2878 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
2879 if (RT_FAILURE(rc))
2880 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
2881
2882 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
2883
2884 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
2885 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
2886 INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
2887
2888 Log(("%s Link up delay is set to %u seconds\n", INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
2889
2890 /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
2891 memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
2892
2893
2894 LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
2895
2896 /*
2897 * Do core virtio initialization.
2898 */
2899
2900#if FEATURE_OFFERED(STATUS)
2901 pThis->virtioNetConfig.uStatus = 0;
2902#endif
2903
2904#if FEATURE_OFFERED(MQ)
2905 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
2906#endif
2907
2908 /* Initialize the generic Virtio core: */
2909 pThisCC->Virtio.pfnQueueNotified = virtioNetQueueNotified;
2910 pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChanged;
2911 pThisCC->Virtio.pfnDevCapRead = virtioNetR3DevCapRead;
2912 pThisCC->Virtio.pfnDevCapWrite = virtioNetR3DevCapWrite;
2913
2914 VIRTIOPCIPARAMS VirtioPciParams;
2915 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
2916 VirtioPciParams.uClassBase = PCI_CLASS_BASE_NETWORK_CONTROLLER;
2917 VirtioPciParams.uClassSub = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
2918 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2919 VirtioPciParams.uSubsystemId = PCI_DEVICE_ID_VIRTIONET_HOST; /* VirtIO 1.0 spec allows PCI Device ID here */
2920 VirtioPciParams.uInterruptLine = 0x00;
2921 VirtioPciParams.uInterruptPin = 0x01;
2922
2923 /*
2924 * Initialize VirtIO core. This will result in a "status changed" callback
2925 * when VirtIO is ready, at which time the Rx queue and ctrl queue worker threads will be created.
2926 */
2927 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, INSTANCE(pThis),
2928 VIRTIONET_HOST_FEATURES_OFFERED,
2929 &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
2930 if (RT_FAILURE(rc))
2931 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
2932
2933 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
2934 if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
2935 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
2936
2937 pThis->cVirtqPairs = pThis->fNegotiatedFeatures & VIRTIONET_F_MQ
2938 ? pThis->virtioNetConfig.uMaxVirtqPairs : 1;
2939
2940 pThis->cVirtQueues += pThis->cVirtqPairs * 2 + 1;
2941
2942 /* Create Link Up Timer */
2943 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL, TMTIMER_FLAGS_NO_CRIT_SECT,
2944 "VirtioNet Link Up Timer", &pThisCC->hLinkUpTimer);
2945
2946 /*
2947 * Initialize queues.
2948 */
2949 virtioNetR3SetVirtqNames(pThis);
2950
2951 /*
2952 * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
2953 */
2954 rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
2955 if (RT_FAILURE(rc))
2956 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to worker threads"));
2957
2958 /*
2959 * Create the semaphore that will be used to synchronize/throttle
2960 * the downstream LUN's Rx waiter thread.
2961 */
2962 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
2963 if (RT_FAILURE(rc))
2964 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
2965
2966 /*
2967 * Attach network driver instance
2968 */
2969 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
2970 if (RT_SUCCESS(rc))
2971 {
2972 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2973 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2974 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
2975 }
2976 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2977 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2978 Log(("%s No attached driver!\n", INSTANCE(pThis)));
2979
2980 /*
2981 * Status driver
2982 */
2983 PPDMIBASE pUpBase;
2984 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
2985 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
2986 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2987
2988 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
2989
2990 /*
2991 * Register saved state.
2992 */
2993 rc = PDMDevHlpSSMRegister(pDevIns, VIRTIONET_SAVED_STATE_VERSION, sizeof(*pThis),
2994 virtioNetR3SaveExec, virtioNetR3LoadExec);
2995 AssertRCReturn(rc, rc);
2996
2997
2998
2999 /*
3000 * Statistics and debug stuff.
3001 * The /Public/ bits are official and used by session info in the GUI.
3002 */
3003 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3004 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
3005 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3006 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
3007 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
3008 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
3009
3010 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
3011 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
3012 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
3013 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
3014 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
3015 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
3016# ifdef VBOX_WITH_STATISTICS
3017 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
3018 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
3019 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
3020 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
3021 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
3022 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
3023 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
3024 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
3025# endif
3026
3027 /*
3028 * Register the debugger info callback (ignore errors).
3029 */
3030 char szTmp[128];
3031 RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
3032 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-net info", virtioNetR3Info);
3033 return rc;
3034}
3035
3036#else /* !IN_RING3 */
3037
3038/**
3039 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3040 */
3041static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
3042{
3043 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3044 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3045 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3046 pThisCC->Virtio.pfnQueueNotified = virtioNetQueueNotified;
3047 return virtioCoreRZInit(pDevIns, &pThis->Virtio);
3048}
3049
3050#endif /* !IN_RING3 */
3051
3052/**
3053 * The device registration structure.
3054 */
3055const PDMDEVREG g_DeviceVirtioNet_1_0 =
3056{
3057 /* .uVersion = */ PDM_DEVREG_VERSION,
3058 /* .uReserved0 = */ 0,
3059 /* .szName = */ "virtio-net-1-dot-0",
3060 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
3061 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
3062 /* .cMaxInstances = */ ~0U,
3063 /* .uSharedVersion = */ 42,
3064 /* .cbInstanceShared = */ sizeof(VIRTIONET),
3065 /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
3066 /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
3067 /* .cMaxPciDevices = */ 1,
3068 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
3069 /* .pszDescription = */ "Virtio Host NET.\n",
3070#if defined(IN_RING3)
3071 /* .pszRCMod = */ "VBoxDDRC.rc",
3072 /* .pszR0Mod = */ "VBoxDDR0.r0",
3073 /* .pfnConstruct = */ virtioNetR3Construct,
3074 /* .pfnDestruct = */ virtioNetR3Destruct,
3075 /* .pfnRelocate = */ NULL,
3076 /* .pfnMemSetup = */ NULL,
3077 /* .pfnPowerOn = */ NULL,
3078 /* .pfnReset = */ virtioNetR3Reset,
3079 /* .pfnSuspend = */ virtioNetR3Suspend,
3080 /* .pfnResume = */ virtioNetR3Resume,
3081 /* .pfnAttach = */ virtioNetR3Attach,
3082 /* .pfnDetach = */ virtioNetR3Detach,
3083 /* .pfnQueryInterface = */ NULL,
3084 /* .pfnInitComplete = */ NULL,
3085 /* .pfnPowerOff = */ virtioNetR3PowerOff,
3086 /* .pfnSoftReset = */ 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_RING0)
3096 /* .pfnEarlyConstruct = */ NULL,
3097 /* .pfnConstruct = */ virtioNetRZConstruct,
3098 /* .pfnDestruct = */ NULL,
3099 /* .pfnFinalDestruct = */ NULL,
3100 /* .pfnRequest = */ NULL,
3101 /* .pfnReserved0 = */ NULL,
3102 /* .pfnReserved1 = */ NULL,
3103 /* .pfnReserved2 = */ NULL,
3104 /* .pfnReserved3 = */ NULL,
3105 /* .pfnReserved4 = */ NULL,
3106 /* .pfnReserved5 = */ NULL,
3107 /* .pfnReserved6 = */ NULL,
3108 /* .pfnReserved7 = */ NULL,
3109#elif defined(IN_RC)
3110 /* .pfnConstruct = */ virtioNetRZConstruct,
3111 /* .pfnReserved0 = */ NULL,
3112 /* .pfnReserved1 = */ NULL,
3113 /* .pfnReserved2 = */ NULL,
3114 /* .pfnReserved3 = */ NULL,
3115 /* .pfnReserved4 = */ NULL,
3116 /* .pfnReserved5 = */ NULL,
3117 /* .pfnReserved6 = */ NULL,
3118 /* .pfnReserved7 = */ NULL,
3119#else
3120# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3121#endif
3122 /* .uVersionEnd = */ PDM_DEVREG_VERSION
3123};
3124
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