VirtualBox

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

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

Network/DevVirtioNet_1.0.cpp: Removed force interrupt flag from API and calls since it is no longer a necessary workardound option due to fixing how avail ring flags are read.

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

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