VirtualBox

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

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

Devices/Virtio|ai: Fixes and performance improvements for the 1.0 virtio-net implementation, bugref:8651

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

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