VirtualBox

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

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

Improve transitional behavior, and save/load exec code. Some Rx buffer handling code optimization for speed, and make it easier to understand and maintain. Add missing function comments and improve others. Try to make debug logging even clearer and more succinct. And any other miscellaneous small improvements I could find. See BugRef(8651) Comment #171

  • 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 92939 2021-12-15 15:51:28Z vboxsync $ $Revision: 92939 $ $Date: 2021-12-15 15:51:28 +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.pcszMmioName);
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