VirtualBox

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

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

Fix burn due to unwanted tab charager

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

© 2023 Oracle
ContactPrivacy policyTerms of Use