VirtualBox

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

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

VirtioCore: pcszMmioName -> szMmioName and pcszPortIoName -> szPortIoName in VIRTIOCORER3. bugref:8651

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

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