VirtualBox

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

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

Devices/DevVirtioNet_1_0: Clear the whole unicast filter table up front instead of trying to selectively clear out the remainder causing a buffer overflow, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 161.2 KB
Line 
1/* $Id: DevVirtioNet_1_0.cpp 94976 2022-05-10 09:43:49Z vboxsync $ $Revision: 94976 $ $Date: 2022-05-10 09:43:49 +0000 (Tue, 10 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 /* Zero-out the the Unicast/Multicast filter table */
1156 memset(&pThis->aMacUnicastFilter[0], 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1157
1158 rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
1159 AssertRCReturn(rc, rc);
1160 rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
1161 AssertRCReturn(rc, rc);
1162 /*
1163 * The 0.95 legacy virtio spec defines a control queue command VIRTIO_NET_CTRL_MAC_TABLE_SET,
1164 * wherein guest driver configures two variable length mac filter tables: A unicast filter,
1165 * and a multicast filter. However original VBox virtio-net saved both sets of filter entries
1166 * in a single table, abandoning the distinction between unicast and multicast filters. It preserved
1167 * only *one* filter's table length, leaving no way to separate table back out into respective unicast
1168 * and multicast tables this device implementation preserves. Deduced from legacy code, the original
1169 * assumption was that the both MAC filters are whitelists that can be processed identically
1170 * (from the standpoint of a *single* host receiver), such that the distinction between unicast and
1171 * multicast doesn't matter in any one VM's context. Little choice here but to save the undifferentiated
1172 * unicast & multicast MACs to the unicast filter table and leave multicast table empty/unused.
1173 */
1174 uint32_t cCombinedUnicastMulticastEntries;
1175 rc = pHlp->pfnSSMGetU32(pSSM, &cCombinedUnicastMulticastEntries);
1176 AssertRCReturn(rc, rc);
1177 AssertReturn(cCombinedUnicastMulticastEntries <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1178 pThis->cUnicastFilterMacs = cCombinedUnicastMulticastEntries;
1179 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aMacUnicastFilter, cCombinedUnicastMulticastEntries * sizeof(RTMAC));
1180 AssertRCReturn(rc, rc);
1181 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1182 AssertRCReturn(rc, rc);
1183 }
1184 else
1185 {
1186 pThis->fAllMulticast = false;
1187 pThis->cUnicastFilterMacs = 0;
1188 memset(&pThis->aMacUnicastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1189
1190 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
1191
1192 pThis->fPromiscuous = true;
1193 if (pThisCC->pDrv)
1194 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
1195 }
1196
1197 /*
1198 * Log the restored VirtIO feature selection.
1199 */
1200 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
1201 virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
1202
1203 /*
1204 * Configure remaining transitional device parameters presumably or deductively
1205 * as these weren't part of the legacy device code thus it didn't save them to SSM
1206 */
1207 pThis->fCableConnected = 1;
1208 pThis->fAllUnicast = 0;
1209 pThis->fNoMulticast = 0;
1210 pThis->fNoUnicast = 0;
1211 pThis->fNoBroadcast = 0;
1212
1213 /* Zero out the multicast table and count, all MAC filters, if any, are in the unicast filter table */
1214 pThis->cMulticastFilterMacs = 0;
1215 memset(&pThis->aMacMulticastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1216 }
1217 return VINF_SUCCESS;
1218}
1219
1220/**
1221 * @callback_method_impl{FNSSMDEVLOADEXEC}
1222 *
1223 * @note: This loads state saved by a Modern (VirtIO 1.0+) device, of which this transitional device is one,
1224 * and thus supports both legacy and modern guest virtio drivers.
1225 */
1226static DECLCALLBACK(int) virtioNetR3ModernDeviceLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1227{
1228 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1229 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1230 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1231 int rc;
1232
1233 RT_NOREF(pThisCC);
1234
1235 RTMAC uMacLoaded, uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
1236 rc = pHlp->pfnSSMGetMem(pSSM, &uMacLoaded.au8, sizeof(uMacLoaded.au8));
1237 AssertRCReturn(rc, rc);
1238 if (memcmp(&uMacLoaded.au8, uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8)))
1239 {
1240 rc = virtioNetR3LegacyDeviceLoadExec(pDevIns, pSSM, uVersion, uPass, uMacLoaded);
1241 return rc;
1242 }
1243
1244 Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
1245
1246 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
1247 AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVEDSTATE_VERSION,
1248 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1249
1250 virtioNetR3SetVirtqNames(pThis, false /* fLegacy */);
1251
1252 pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
1253
1254 pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtqs);
1255 AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2) + 1, VERR_OUT_OF_RANGE);
1256 pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
1257 AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS , VERR_OUT_OF_RANGE);
1258
1259 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1260 pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1261
1262 /* Config checks */
1263 RTMAC macConfigured;
1264 rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
1265 AssertRCReturn(rc, rc);
1266 if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
1267 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1268 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
1269 pThis->szInst, &pThis->macConfigured, &macConfigured));
1270 memcpy(pThis->virtioNetConfig.uMacAddress.au8, macConfigured.au8, sizeof(macConfigured.au8));
1271
1272#if FEATURE_OFFERED(STATUS)
1273 uint16_t fChkStatus;
1274 pHlp->pfnSSMGetU16( pSSM, &fChkStatus);
1275 if (fChkStatus == 0xffff)
1276 {
1277 /* Dummy value in saved state because status feature wasn't enabled at the time */
1278 pThis->virtioNetConfig.uStatus = 0; /* VIRTIO_NET_S_ANNOUNCE disabled */
1279 pThis->virtioNetConfig.uStatus = !!IS_LINK_UP(pThis); /* VIRTIO_NET_IS_LINK_UP (bit 0) */
1280 }
1281 else
1282 pThis->virtioNetConfig.uStatus = fChkStatus;
1283#else
1284 uint16_t fDiscard;
1285 pHlp->pfnSSMGetU16( pSSM, &fDiscard);
1286#endif
1287
1288#if FEATURE_OFFERED(MQ)
1289 uint16_t uCheckMaxVirtqPairs;
1290 pHlp->pfnSSMGetU16( pSSM, &uCheckMaxVirtqPairs);
1291 if (uCheckMaxVirtqPairs)
1292 pThis->virtioNetConfig.uMaxVirtqPairs = uCheckMaxVirtqPairs;
1293 else
1294 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_CTRL_MQ_VQ_PAIRS;
1295#else
1296 uint16_t fDiscard;
1297 pHlp->pfnSSMGetU16( pSSM, &fDiscard);
1298#endif
1299
1300 /* Save device-specific part */
1301 pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
1302 pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
1303 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
1304 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
1305 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
1306 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
1307 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
1308
1309 pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
1310 AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1311 pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1312
1313 if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1314 memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
1315 (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
1316
1317 pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
1318 AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1319 pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1320
1321 if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1322 memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
1323 (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
1324
1325 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1326 AssertRCReturn(rc, rc);
1327 /*
1328 * Call the virtio core to let it load its state.
1329 */
1330 rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion,
1331 VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
1332 AssertRCReturn(rc, rc);
1333 /*
1334 * Since the control queue is created proactively in the constructor to accomodate worst-case
1335 * legacy guests, even though the queue may have been deducted from queue count while saving state,
1336 * we must explicitly remove queue and associated worker thread and context at this point,
1337 * or presence of bogus control queue will confuse operations.
1338 */
1339 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[CTRLQIDX];
1340 if (FEATURE_DISABLED(CTRL_VQ) || !virtioCoreIsVirtqEnabled(&pThis->Virtio, CTRLQIDX))
1341 {
1342 virtioCoreR3VirtqDetach(&pThis->Virtio, CTRLQIDX);
1343 virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
1344 pVirtq->fAttachedToVirtioCore = false;
1345 --pThis->cWorkers;
1346 }
1347 /*
1348 * Nudge queue workers
1349 */
1350 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1351 {
1352 pVirtq = &pThis->aVirtqs[uVirtqNbr];
1353 if (pVirtq->fAttachedToVirtioCore)
1354 {
1355 if (pVirtq->fHasWorker)
1356 {
1357 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
1358 Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
1359 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
1360 AssertRCReturn(rc, rc);
1361 }
1362 }
1363 }
1364 pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus; /* reflects state to guest driver */
1365 pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
1366
1367 return rc;
1368}
1369
1370/**
1371 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1372 */
1373static DECLCALLBACK(int) virtioNetR3ModernSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1374{
1375 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1376 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1377 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1378
1379 RT_NOREF(pThisCC);
1380 Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
1381
1382 /* Store a dummy MAC address that would never be actually assigned to a NIC
1383 * so that when load exec handler is called it can be easily determined
1384 * whether saved state is modern or legacy. This works because original
1385 * legacy code stored assigned NIC address as the first item of SSM state
1386 */
1387 RTMAC uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
1388 pHlp->pfnSSMPutMem(pSSM, &uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8));
1389
1390 pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
1391
1392 pHlp->pfnSSMPutU16( pSSM, pThis->cVirtqs);
1393 pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
1394
1395 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1396 pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1397 /*
1398
1399 * Save device config area (accessed via MMIO)
1400 */
1401 pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
1402 sizeof(pThis->virtioNetConfig.uMacAddress.au8));
1403#if FEATURE_OFFERED(STATUS)
1404 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uStatus);
1405#else
1406 /*
1407 * Relevant values are lower bits. Forcing this to 0xffff let's loadExec know this
1408 * feature was not enabled in saved state. VirtIO 1.0, 5.1.4
1409 */
1410 pHlp->pfnSSMPutU16( pSSM, 0xffff);
1411
1412#endif
1413#if FEATURE_OFFERED(MQ)
1414 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
1415#else
1416 /*
1417 * Legal values for max_virtqueue_pairs are 0x1 -> 0x8000 *. Forcing zero let's loadExec know this
1418 * feature was not enabled in saved state. VirtIO 1.0, 5.1.4.1
1419 */
1420 pHlp->pfnSSMPutU16( pSSM, 0);
1421#endif
1422
1423 /* Save device-specific part */
1424 pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
1425 pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
1426 pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
1427 pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
1428 pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
1429 pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
1430 pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
1431
1432 pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
1433 pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1434
1435 pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
1436 pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1437
1438 int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1439 AssertRCReturn(rc, rc);
1440
1441 /*
1442 * Call the virtio core to let it save its state.
1443 */
1444 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
1445}
1446
1447
1448/*********************************************************************************************************************************
1449* Device interface. *
1450*********************************************************************************************************************************/
1451
1452#ifdef IN_RING3
1453
1454/**
1455 * Perform 16-bit 1's compliment checksum on provided packet in accordance with VirtIO specification,
1456 * pertinent to VIRTIO_NET_F_CSUM feature, which 'offloads' the Checksum feature from the driver
1457 * to save processor cycles, which is ironic in our case, where the controller device ('network card')
1458 * is emulated on the virtualization host.
1459 *
1460 * @note See VirtIO 1.0 spec, 5.1.6.2 Packet Transmission
1461 *
1462 * @param pBuf Pointer to r/w buffer with any portion to calculate checksum for
1463 * @param cbSize Number of bytes to checksum
1464 * @param uStart Where to start the checksum within the buffer
1465 * @param uOffset Offset past uStart point in the buffer to store checksum result
1466 *
1467 */
1468DECLINLINE(void) virtioNetR3Calc16BitChecksum(uint8_t *pBuf, size_t cb, uint16_t uStart, uint16_t uOffset)
1469{
1470 AssertReturnVoid(uStart < cb);
1471 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cb);
1472
1473 uint32_t chksum = 0;
1474 uint16_t *pu = (uint16_t *)(pBuf + uStart);
1475
1476 cb -= uStart;
1477 while (cb > 1)
1478 {
1479 chksum += *pu++;
1480 cb -= 2;
1481 }
1482 if (cb)
1483 chksum += *(uint8_t *)pu;
1484 while (chksum >> 16)
1485 chksum = (chksum >> 16) + (chksum & 0xFFFF);
1486
1487 /* Store 1's compliment of calculated sum */
1488 *(uint16_t *)(pBuf + uStart + uOffset) = ~chksum;
1489}
1490
1491/**
1492 * Turns on/off the read status LED.
1493 *
1494 * @returns VBox status code.
1495 * @param pThis Pointer to the device state structure.
1496 * @param fOn New LED state.
1497 */
1498void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
1499{
1500 if (fOn)
1501 pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
1502 else
1503 pThisR3->led.Actual.s.fReading = fOn;
1504}
1505
1506/**
1507 * Turns on/off the write status LED.
1508 *
1509 * @returns VBox status code.
1510 * @param pThis Pointer to the device state structure.
1511 * @param fOn New LED state.
1512 */
1513void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
1514{
1515 if (fOn)
1516 pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
1517 else
1518 pThisR3->led.Actual.s.fWriting = fOn;
1519}
1520
1521/**
1522 * Check that the core is setup and ready and co-configured with guest virtio driver,
1523 * and verifies that the VM is running.
1524 *
1525 * @returns true if VirtIO core and device are in a running and operational state
1526 */
1527DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
1528{
1529 if (RT_LIKELY(pThis->fVirtioReady))
1530 {
1531 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1532 if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
1533 return true;
1534 }
1535 return false;
1536}
1537
1538/**
1539 * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
1540 * available. This must be called before the pfnRecieve() method is called.
1541 *
1542 * @remarks As a side effect this function enables queue notification
1543 * if it cannot receive because the queue is empty.
1544 * It disables notification if it can receive.
1545 *
1546 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1547 * @thread RX
1548 */
1549static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
1550{
1551 int rc = VERR_INVALID_STATE;
1552 Log8Func(("[%s] ", pThis->szInst));
1553 if (!virtioNetIsOperational(pThis, pDevIns))
1554 Log8(("No Rx bufs available. (VirtIO core not ready)\n"));
1555
1556 else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
1557 Log8(("[No Rx bufs available. (%s not enabled)\n", pRxVirtq->szName));
1558
1559 else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->uIdx))
1560 Log8(("No Rx bufs available. (%s empty)\n", pRxVirtq->szName));
1561
1562 else
1563 {
1564 Log8(("%s has %d empty guest bufs in avail ring\n", pRxVirtq->szName,
1565 virtioCoreVirtqAvailBufCount(pDevIns, &pThis->Virtio, pRxVirtq->uIdx)));
1566 rc = VINF_SUCCESS;
1567 }
1568 virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
1569 return rc;
1570}
1571
1572/**
1573 * Find an Rx queue that has Rx packets in it, if *any* do.
1574 *
1575 * @todo When multiqueue (MQ) mode is fully supported and tested, some kind of round-robin
1576 * or randomization scheme should probably be incorporated here.
1577 *
1578 * @returns true if Rx pkts avail on queue and sets pRxVirtq to point to queue w/pkts found
1579 * @thread RX
1580 *
1581 */
1582static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
1583{
1584 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
1585 {
1586 PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
1587 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
1588 {
1589 if (pRxVirtq)
1590 *pRxVirtq = pThisRxVirtq;
1591 return true;
1592 }
1593 }
1594 return false;
1595}
1596
1597/**
1598 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
1599 */
1600static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
1601{
1602 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1603 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1604 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1605
1606 if (!virtioNetIsOperational(pThis, pDevIns))
1607 return VERR_INTERRUPTED;
1608
1609 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1610 {
1611 Log10Func(("[%s] Rx bufs available, releasing waiter...\n", pThis->szInst));
1612 return VINF_SUCCESS;
1613 }
1614 if (!timeoutMs)
1615 return VERR_NET_NO_BUFFER_SPACE;
1616
1617 LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
1618
1619 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
1620 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
1621
1622 do {
1623 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1624 {
1625 Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
1626 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1627 return VINF_SUCCESS;
1628 }
1629 Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
1630
1631 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
1632
1633 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
1634 {
1635 LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
1636
1637 if (!virtioNetIsOperational(pThis, pDevIns))
1638 break;
1639
1640 continue;
1641 }
1642 if (RT_FAILURE(rc)) {
1643 LogFunc(("Waken due to failure %Rrc\n", rc));
1644 RTThreadSleep(1);
1645 }
1646 } while (virtioNetIsOperational(pThis, pDevIns));
1647
1648 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
1649 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1650
1651 Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
1652 return VERR_INTERRUPTED;
1653}
1654
1655/**
1656 * Sets up the GSO context according to the Virtio header.
1657 *
1658 * @param pGso The GSO context to setup.
1659 * @param pCtx The context descriptor.
1660 */
1661DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
1662{
1663 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1664
1665 if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
1666 {
1667 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
1668 return NULL;
1669 }
1670 switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
1671 {
1672 case VIRTIONET_HDR_GSO_TCPV4:
1673 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
1674 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1675 break;
1676 case VIRTIONET_HDR_GSO_TCPV6:
1677 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
1678 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1679 break;
1680 case VIRTIONET_HDR_GSO_UDP:
1681 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
1682 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
1683 break;
1684 default:
1685 return NULL;
1686 }
1687 if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
1688 pGso->offHdr2 = pPktHdr->uChksumStart;
1689 else
1690 {
1691 AssertMsgFailed(("GSO without checksum offloading!\n"));
1692 return NULL;
1693 }
1694 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1695 pGso->cbHdrsTotal = pPktHdr->uHdrLen;
1696 pGso->cbMaxSeg = pPktHdr->uGsoSize;
1697 return pGso;
1698}
1699
1700/**
1701 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
1702 */
1703static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1704{
1705 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
1706 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
1707 memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
1708 return VINF_SUCCESS;
1709}
1710
1711/**
1712 * Returns true if it is a broadcast packet.
1713 *
1714 * @returns true if destination address indicates broadcast.
1715 * @param pvBuf The ethernet packet.
1716 */
1717DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
1718{
1719 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1720 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
1721}
1722
1723/**
1724 * Returns true if it is a multicast packet.
1725 *
1726 * @remarks returns true for broadcast packets as well.
1727 * @returns true if destination address indicates multicast.
1728 * @param pvBuf The ethernet packet.
1729 */
1730DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
1731{
1732 return (*(char*)pvBuf) & 1;
1733}
1734
1735/**
1736 * Determines if the packet is to be delivered to upper layer.
1737 *
1738 * @returns true if packet is intended for this node.
1739 * @param pThis Pointer to the state structure.
1740 * @param pvBuf The ethernet packet.
1741 * @param cb Number of bytes available in the packet.
1742 */
1743static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
1744{
1745
1746RT_NOREF(cb);
1747
1748#ifdef LOG_ENABLED
1749 if (LogIs11Enabled())
1750 {
1751 char *pszType;
1752 if (virtioNetR3IsMulticast(pvBuf))
1753 pszType = (char *)"mcast";
1754 else if (virtioNetR3IsBroadcast(pvBuf))
1755 pszType = (char *)"bcast";
1756 else
1757 pszType = (char *)"ucast";
1758
1759 LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
1760 pThis->virtioNetConfig.uMacAddress.au8,
1761 pThis->fPromiscuous ? " promisc" : "",
1762 pThis->fAllMulticast ? " all-mcast" : "",
1763 pvBuf, pszType));
1764 }
1765#endif
1766
1767 if (pThis->fPromiscuous) {
1768 Log11(("\n"));
1769 return true;
1770 }
1771
1772 /* Ignore everything outside of our VLANs */
1773 uint16_t *uPtr = (uint16_t *)pvBuf;
1774
1775 /* Compare TPID with VLAN Ether Type */
1776 if ( uPtr[6] == RT_H2BE_U16(0x8100)
1777 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
1778 {
1779 Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
1780 return false;
1781 }
1782
1783 if (virtioNetR3IsBroadcast(pvBuf))
1784 {
1785 Log11(("acpt (bcast)\n"));
1786#ifdef LOG_ENABLED
1787 if (LogIs12Enabled())
1788 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1789#endif
1790 return true;
1791 }
1792 if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
1793 {
1794 Log11(("acpt (all-mcast)\n"));
1795#ifdef LOG_ENABLED
1796 if (LogIs12Enabled())
1797 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1798#endif
1799 return true;
1800 }
1801
1802 if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
1803 {
1804 Log11(("acpt (to-node)\n"));
1805#ifdef LOG_ENABLED
1806 if (LogIs12Enabled())
1807 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1808#endif
1809 return true;
1810 }
1811
1812 for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
1813 {
1814 if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
1815 {
1816 Log11(("acpt (mcast whitelist)\n"));
1817#ifdef LOG_ENABLED
1818 if (LogIs12Enabled())
1819 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1820#endif
1821 return true;
1822 }
1823 }
1824
1825 for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
1826 if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
1827 {
1828 Log11(("acpt (ucast whitelist)\n"));
1829 return true;
1830 }
1831#ifdef LOG_ENABLED
1832 if (LogIs11Enabled())
1833 Log(("... reject\n"));
1834#endif
1835
1836 return false;
1837}
1838
1839
1840/**
1841 * This handles the case where Rx packet must be transfered to guest driver via multiple buffers using
1842 * copy tactics slower than preferred method using a single virtq buf. Yet this is an available option
1843 * for guests. Although cited in the spec it's to accomodate guest that perhaps have memory constraints
1844 * wherein guest may benefit from smaller buffers (see MRG_RXBUF feature), in practice it is seen
1845 * that without MRG_RXBUF the linux guest enqueues 'huge' multi-segment buffers so that the largest
1846 * conceivable Rx packet can be contained in a single buffer, where for most transactions most of that
1847 * memory will be unfilled, so it is typically both wasteful and *slower* to avoid MRG_RXBUF.
1848 *
1849 * As an optimization, this multi-buffer copy is only used when:
1850 *
1851 * A. Guest has negotiated MRG_RXBUF
1852 * B. Next packet in the Rx avail queue isn't big enough to contain Rx pkt hdr+data.
1853 *
1854 * Architecture is defined in VirtIO 1.1 5.1.6 (Device Operations), which has improved
1855 * wording over the VirtIO 1.0 specification, but, as an implementation note, there is one
1856 * ambiguity that needs clarification:
1857 *
1858 * The VirtIO 1.1, 5.1.6.4 explains something in a potentially misleading way. And note,
1859 * the VirtIO spec makes a document-wide assertion that the distinction between
1860 * "SHOULD" and "MUST" is to be taken quite literally.
1861 *
1862 * The confusion is that VirtIO 1.1, 5.1.6.3.1 essentially says guest driver "SHOULD" populate
1863 * Rx queue with buffers large enough to accomodate full pkt hdr + data. That's a grammatical
1864 * error (dangling participle).
1865 *
1866 * In practice we MUST assume "SHOULD" strictly applies to the word *populate*, -not- to buffer
1867 * size, because ultimately buffer minimum size is predicated on configuration parameters,
1868 * specifically, when MRG_RXBUF feature is disabled, the driver *MUST* provide Rx bufs
1869 * (if and when it can provide them), that are *large enough* to hold pkt hdr + payload.
1870 *
1871 * Therefore, proper interpretation of 5.1.6.3.1 is, the guest *should* (ideally) keep Rx virtq
1872 * populated with appropriately sized buffers to *prevent starvation* (i.e. starvation may be
1873 * unavoidable thus can't be prohibited). As it would be a ludicrous to presume 5.1.6.3.1 is
1874 * giving guests leeway to violate MRG_RXBUF feature buf size constraints.
1875 *
1876 * @param pDevIns PDM instance
1877 * @param pThis Device instance
1878 * @param pvBuf Pointer to incoming GSO Rx data from downstream device
1879 * @param cb Amount of data given
1880 * @param rxPktHdr Rx pkt Header that's been massaged into VirtIO semantics
1881 * @param pRxVirtq Pointer to Rx virtq
1882 * @param pVirtqBuf Initial virtq buffer to start copying Rx hdr/pkt to guest into
1883 *
1884 */
1885static int virtioNetR3RxPktMultibufXfer(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint8_t *pvPktBuf, size_t cb,
1886 PVIRTIONETPKTHDR pRxPktHdr, PVIRTIONETVIRTQ pRxVirtq, PVIRTQBUF pVirtqBuf)
1887{
1888
1889 size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
1890 size_t cbPktHdr = pThis->cbPktHdr;
1891
1892 AssertMsgReturn(cbBufRemaining >= pThis->cbPktHdr,
1893 ("guest-provided Rx buf not large enough to store pkt hdr"), VERR_INTERNAL_ERROR);
1894
1895 Log7Func((" Sending packet header to guest...\n"));
1896
1897 /* Copy packet header to rx buf provided by caller. */
1898 size_t cbHdrEnqueued = pVirtqBuf->cbPhysReturn == cbPktHdr ? cbPktHdr : 0;
1899 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, cbHdrEnqueued);
1900
1901 /* Cache address of uNumBuffers field of pkthdr to update ex post facto */
1902 RTGCPHYS GCPhysNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
1903 uint16_t cVirtqBufsUsed = 0;
1904 cbBufRemaining -= cbPktHdr;
1905 /*
1906 * Copy packet to guest using as many buffers as necessary, tracking and handling whether
1907 * the buf containing the packet header was already written to the Rx queue's used buffer ring.
1908 */
1909 uint64_t uPktOffset = 0;
1910 while(uPktOffset < cb)
1911 {
1912 Log7Func((" Sending packet data (in buffer #%d) to guest...\n", cVirtqBufsUsed));
1913 size_t cbBounded = RT_MIN(cbBufRemaining, cb - uPktOffset);
1914 (void) virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbBounded,
1915 pvPktBuf + uPktOffset, pVirtqBuf, cbBounded + (cbPktHdr - cbHdrEnqueued) /* cbEnqueue */);
1916 ++cVirtqBufsUsed;
1917 cbBufRemaining -= cbBounded;
1918 uPktOffset += cbBounded;
1919 if (uPktOffset < cb)
1920 {
1921 cbHdrEnqueued = cbPktHdr;
1922#ifdef VIRTIO_VBUF_ON_STACK
1923 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
1924#else /* !VIRTIO_VBUF_ON_STACK */
1925 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
1926 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
1927#endif /* !VIRTIO_VBUF_ON_STACK */
1928
1929 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
1930
1931#ifdef VIRTIO_VBUF_ON_STACK
1932 AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
1933 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
1934 VERR_INTERNAL_ERROR);
1935#else /* !VIRTIO_VBUF_ON_STACK */
1936 AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
1937 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
1938 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
1939 VERR_INTERNAL_ERROR);
1940#endif /* !VIRTIO_VBUF_ON_STACK */
1941 cbBufRemaining = pVirtqBuf->cbPhysReturn;
1942 }
1943 }
1944
1945 /* Fix-up pkthdr (in guest phys. memory) with number of buffers (descriptors) that were processed */
1946 int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysNumBuffers, &cVirtqBufsUsed, sizeof(cVirtqBufsUsed));
1947 AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
1948
1949#ifndef VIRTIO_VBUF_ON_STACK
1950 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
1951#endif /* !VIRTIO_VBUF_ON_STACK */
1952 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
1953 Log7(("\n"));
1954 return rc;
1955}
1956
1957/**
1958 * Pad and store received packet.
1959 *
1960 * @remarks Make sure that the packet appears to upper layer as one coming
1961 * from real Ethernet: pad it and insert FCS.
1962 *
1963 * @returns VBox status code.
1964 * @param pDevIns The device instance.
1965 * @param pThis The virtio-net shared instance data.
1966 * @param pvBuf The available data.
1967 * @param cb Number of bytes available in the buffer.
1968 * @param pGso Pointer to Global Segmentation Offload structure
1969 * @param pRxVirtq Pointer to Rx virtqueue
1970 * @thread RX
1971 */
1972
1973static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, const void *pvBuf, size_t cb,
1974 PVIRTIONETPKTHDR pRxPktHdr, uint8_t cbPktHdr, PVIRTIONETVIRTQ pRxVirtq)
1975{
1976 RT_NOREF(pThisCC);
1977#ifdef VIRTIO_VBUF_ON_STACK
1978 VIRTQBUF_T VirtqBuf;
1979 PVIRTQBUF pVirtqBuf = &VirtqBuf;
1980 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
1981#else /* !VIRTIO_VBUF_ON_STACK */
1982 PVIRTQBUF pVirtqBuf;
1983 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
1984#endif /* !VIRTIO_VBUF_ON_STACK */
1985
1986 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
1987
1988#ifdef VIRTIO_VBUF_ON_STACK
1989 AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
1990 ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
1991 VERR_INTERNAL_ERROR);
1992#else /* !VIRTIO_VBUF_ON_STACK */
1993 AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
1994 ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
1995 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
1996 VERR_INTERNAL_ERROR);
1997#endif /* !VIRTIO_VBUF_ON_STACK */
1998 /*
1999 * Try to do fast (e.g. single-buffer) copy to guest, even if MRG_RXBUF feature is enabled
2000 */
2001 STAM_PROFILE_START(&pThis->StatReceiveStore, a);
2002 if (RT_LIKELY(FEATURE_DISABLED(MRG_RXBUF))
2003 || RT_LIKELY(pVirtqBuf->cbPhysReturn > cb + cbPktHdr))
2004 {
2005 Log7Func(("Send Rx packet header and data to guest (single-buffer copy)...\n"));
2006 pRxPktHdr->uNumBuffers = 1;
2007 rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, 0 /* cbEnqueue */);
2008 if (rc == VINF_SUCCESS)
2009 rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cb, pvBuf, pVirtqBuf, cbPktHdr + cb /* cbEnqueue */);
2010#ifndef VIRTIO_VBUF_ON_STACK
2011 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
2012#endif /* !VIRTIO_VBUF_ON_STACK */
2013 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
2014 AssertMsgReturn(rc == VINF_SUCCESS, ("%Rrc\n", rc), rc);
2015 }
2016 else
2017 {
2018 Log7Func(("Send Rx pkt to guest (merged-buffer copy [MRG_RXBUF feature])...\n"));
2019 rc = virtioNetR3RxPktMultibufXfer(pDevIns, pThis, (uint8_t *)pvBuf, cb, pRxPktHdr, pRxVirtq, pVirtqBuf);
2020 return rc;
2021 }
2022 STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
2023 return VINF_SUCCESS;
2024}
2025
2026/**
2027 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
2028 */
2029static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(
2030 PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso)
2031{
2032 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2033 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2034 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2035 VIRTIONETPKTHDR rxPktHdr = { 0, VIRTIONET_HDR_GSO_NONE, 0, 0, 0, 0, 0 };
2036
2037 if (!pThis->fVirtioReady)
2038 {
2039 LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
2040 return VERR_INTERRUPTED;
2041 }
2042 /*
2043 * If GSO (Global Segment Offloading) was received from downstream PDM network device, massage the
2044 * PDM-provided GSO parameters into VirtIO semantics, which get passed to guest virtio-net via
2045 * Rx pkt header. See VirtIO 1.1, 5.1.6 Device Operation for more information.
2046 */
2047 if (pGso)
2048 {
2049 LogFunc(("[%s] (%RTmac) \n", pThis->szInst, pvBuf));
2050
2051 rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
2052 rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
2053 rxPktHdr.uGsoSize = pGso->cbMaxSeg;
2054 rxPktHdr.uChksumStart = pGso->offHdr2;
2055
2056 switch (pGso->u8Type)
2057 {
2058 case PDMNETWORKGSOTYPE_IPV4_TCP:
2059 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
2060 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
2061 break;
2062 case PDMNETWORKGSOTYPE_IPV6_TCP:
2063 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
2064 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
2065 break;
2066 case PDMNETWORKGSOTYPE_IPV4_UDP:
2067 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
2068 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
2069 break;
2070 default:
2071 LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
2072 return VERR_NOT_SUPPORTED;
2073 }
2074 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
2075 Log2Func(("[%s] gso type=%#x, cbHdrsTotal=%u cbHdrsSeg=%u mss=%u offHdr1=%#x offHdr2=%#x\n",
2076 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2077 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2078 }
2079
2080 /*
2081 * Find a virtq with Rx bufs on avail ring, if any, and copy the packet to the guest's Rx buffer.
2082 * @todo pk: PROBABLY NOT A SOPHISTICATED ENOUGH QUEUE SELECTION ALGORTITH FOR OPTIMAL MQ (FEATURE) SUPPORT
2083 */
2084 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
2085 {
2086 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
2087 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
2088 {
2089 int rc = VINF_SUCCESS;
2090 STAM_PROFILE_START(&pThis->StatReceive, a);
2091 virtioNetR3SetReadLed(pThisCC, true);
2092 if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
2093 {
2094 /* rxPktHdr is local stack variable that should not go out of scope in this use */
2095 rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pThisCC, pvBuf, cb, &rxPktHdr, pThis->cbPktHdr, pRxVirtq);
2096 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
2097 }
2098 virtioNetR3SetReadLed(pThisCC, false);
2099 STAM_PROFILE_STOP(&pThis->StatReceive, a);
2100 return rc;
2101 }
2102 }
2103 return VERR_INTERRUPTED;
2104}
2105
2106/**
2107 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
2108 */
2109static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
2110{
2111
2112#ifdef LOG_ENABLED
2113 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2114 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2115 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2116 LogFunc(("[%s] (%RTmac)\n", pThis->szInst, pvBuf));
2117#endif
2118
2119 return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
2120}
2121
2122/*
2123 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's Rx packet receive filtering.
2124 * See VirtIO 1.0, 5.1.6.5.1
2125 *
2126 * @param pThis virtio-net instance
2127 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2128 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2129 */
2130static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2131 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2132{
2133
2134#define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
2135
2136 LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
2137 switch(pCtrlPktHdr->uCmd)
2138 {
2139 case VIRTIONET_CTRL_RX_PROMISC:
2140 break;
2141 case VIRTIONET_CTRL_RX_ALLMULTI:
2142 break;
2143 case VIRTIONET_CTRL_RX_ALLUNI:
2144 /* fallthrough */
2145 case VIRTIONET_CTRL_RX_NOMULTI:
2146 /* fallthrough */
2147 case VIRTIONET_CTRL_RX_NOUNI:
2148 /* fallthrough */
2149 case VIRTIONET_CTRL_RX_NOBCAST:
2150 AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
2151 ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
2152 VIRTIONET_ERROR);
2153 /* fall out */
2154 }
2155
2156 uint8_t fOn, fPromiscChanged = false;
2157 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
2158
2159 switch(pCtrlPktHdr->uCmd)
2160 {
2161 case VIRTIONET_CTRL_RX_PROMISC:
2162 pThis->fPromiscuous = RT_BOOL(fOn);
2163 fPromiscChanged = true;
2164 LOG_VIRTIONET_FLAG(fPromiscuous);
2165 break;
2166 case VIRTIONET_CTRL_RX_ALLMULTI:
2167 pThis->fAllMulticast = RT_BOOL(fOn);
2168 fPromiscChanged = true;
2169 LOG_VIRTIONET_FLAG(fAllMulticast);
2170 break;
2171 case VIRTIONET_CTRL_RX_ALLUNI:
2172 pThis->fAllUnicast = RT_BOOL(fOn);
2173 LOG_VIRTIONET_FLAG(fAllUnicast);
2174 break;
2175 case VIRTIONET_CTRL_RX_NOMULTI:
2176 pThis->fNoMulticast = RT_BOOL(fOn);
2177 LOG_VIRTIONET_FLAG(fNoMulticast);
2178 break;
2179 case VIRTIONET_CTRL_RX_NOUNI:
2180 pThis->fNoUnicast = RT_BOOL(fOn);
2181 LOG_VIRTIONET_FLAG(fNoUnicast);
2182 break;
2183 case VIRTIONET_CTRL_RX_NOBCAST:
2184 pThis->fNoBroadcast = RT_BOOL(fOn);
2185 LOG_VIRTIONET_FLAG(fNoBroadcast);
2186 break;
2187 }
2188
2189 if (pThisCC->pDrv && fPromiscChanged)
2190 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
2191
2192 return VIRTIONET_OK;
2193}
2194
2195/*
2196 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MAC filter tables
2197 * See VirtIO 1.0, 5.1.6.5.2
2198 *
2199 * @param pThis virtio-net instance
2200 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2201 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2202 */
2203static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2204{
2205 LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
2206
2207
2208 AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
2209 ("insufficient descriptor space for ctrl pkt hdr"),
2210 VIRTIONET_ERROR);
2211
2212 size_t cbRemaining = pVirtqBuf->cbPhysSend;
2213 switch(pCtrlPktHdr->uCmd)
2214 {
2215 case VIRTIONET_CTRL_MAC_ADDR_SET:
2216 {
2217 /* Set default Rx filter MAC */
2218 AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
2219 ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
2220
2221 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(RTMAC));
2222 break;
2223 }
2224 case VIRTIONET_CTRL_MAC_TABLE_SET:
2225 {
2226 VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
2227
2228 /* Load unicast MAC filter table */
2229 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
2230 ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2231
2232 /* Fetch count of unicast filter MACs from guest buffer */
2233 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
2234 cbRemaining -= sizeof(cMacs);
2235
2236 Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
2237
2238 if (cMacs)
2239 {
2240 uint32_t cbMacs = cMacs * sizeof(RTMAC);
2241
2242 AssertMsgReturn(cbMacs <= sizeof(pThis->aMacUnicastFilter) / sizeof(RTMAC),
2243 ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
2244
2245 AssertMsgReturn(cbRemaining >= cbMacs,
2246 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2247
2248
2249 /* Fetch unicast table contents from guest buffer */
2250 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
2251 cbRemaining -= cbMacs;
2252 }
2253 pThis->cUnicastFilterMacs = cMacs;
2254
2255 /* Load multicast MAC filter table */
2256 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
2257 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2258
2259 /* Fetch count of multicast filter MACs from guest buffer */
2260 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
2261 cbRemaining -= sizeof(cMacs);
2262
2263 Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
2264
2265 if (cMacs)
2266 {
2267 uint32_t cbMacs = cMacs * sizeof(RTMAC);
2268
2269 AssertMsgReturn(cbMacs <= sizeof(pThis->aMacMulticastFilter) / sizeof(RTMAC),
2270 ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
2271
2272 AssertMsgReturn(cbRemaining >= cbMacs,
2273 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2274
2275 /* Fetch multicast table contents from guest buffer */
2276 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
2277 cbRemaining -= cbMacs;
2278 }
2279 pThis->cMulticastFilterMacs = cMacs;
2280
2281#ifdef LOG_ENABLED
2282 LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
2283 for(unsigned i = 0; i < pThis->cUnicastFilterMacs; i++)
2284 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
2285
2286 LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
2287 for(unsigned i = 0; i < pThis->cMulticastFilterMacs; i++)
2288 LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
2289#endif
2290 break;
2291 }
2292 default:
2293 LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
2294 return VIRTIONET_ERROR;
2295 }
2296 return VIRTIONET_OK;
2297}
2298
2299/*
2300 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MQ (multiqueue) operations.
2301 * See VirtIO 1.0, 5.1.6.5.5
2302 *
2303 * @param pThis virtio-net instance
2304 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2305 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2306 */
2307static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2308{
2309 LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
2310
2311 uint16_t cVirtqPairs;
2312 switch(pCtrlPktHdr->uCmd)
2313 {
2314 case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
2315 {
2316 size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
2317
2318 AssertMsgReturn(cbRemaining > sizeof(cVirtqPairs),
2319 ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
2320
2321 /* Fetch number of virtq pairs from guest buffer */
2322 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
2323
2324 AssertMsgReturn(cVirtqPairs > VIRTIONET_MAX_QPAIRS,
2325 ("[%s] Guest CTRL MQ virtq pair count out of range [%d])\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
2326
2327 LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
2328 pThis->cVirtqPairs = cVirtqPairs;
2329 break;
2330 }
2331 default:
2332 LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
2333 return VIRTIONET_ERROR;
2334 }
2335
2336 /*
2337 * The MQ control function is invoked by the guest in an RPC like manner to change
2338 * the Rx/Tx queue pair count. If the new value exceeds the number of queues
2339 * (and associated workers) already initialized initialize only the new queues and
2340 * respective workers.
2341 */
2342 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
2343 {
2344 virtioNetR3SetVirtqNames(pThis, virtioCoreIsLegacyMode(&pThis->Virtio));
2345 int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
2346 if (RT_FAILURE(rc))
2347 {
2348 LogRelFunc(("Failed to create worker threads\n"));
2349 return VIRTIONET_ERROR;
2350 }
2351 }
2352 return VIRTIONET_OK;
2353}
2354
2355/*
2356 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's VLAN filtering.
2357 * See VirtIO 1.0, 5.1.6.5.3
2358 *
2359 * @param pThis virtio-net instance
2360 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2361 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2362 */
2363static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2364{
2365 LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
2366
2367 uint16_t uVlanId;
2368 size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
2369
2370 AssertMsgReturn(cbRemaining > sizeof(uVlanId),
2371 ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
2372
2373 /* Fetch VLAN ID from guest buffer */
2374 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
2375
2376 AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
2377 ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
2378
2379 LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
2380
2381 switch (pCtrlPktHdr->uCmd)
2382 {
2383 case VIRTIONET_CTRL_VLAN_ADD:
2384 ASMBitSet(pThis->aVlanFilter, uVlanId);
2385 break;
2386 case VIRTIONET_CTRL_VLAN_DEL:
2387 ASMBitClear(pThis->aVlanFilter, uVlanId);
2388 break;
2389 default:
2390 LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
2391 return VIRTIONET_ERROR;
2392 }
2393 return VIRTIONET_OK;
2394}
2395
2396/**
2397 * Processes control command from guest.
2398 * See VirtIO 1.0 spec, 5.1.6 "Device Operation" and 5.1.6.5 "Control Virtqueue".
2399 *
2400 * The control command is contained in a virtio buffer pulled from the virtio-net defined control queue (ctrlq).
2401 * Command type is parsed is dispatched to a command-specific device-configuration handler function (e.g. RX, MAC, VLAN, MQ
2402 * and ANNOUNCE).
2403 *
2404 * This function handles all parts of the host-side of the ctrlq round-trip buffer processing.
2405 *
2406 * Invoked by worker for virtio-net control queue to process a queued control command buffer.
2407 *
2408 * @param pDevIns PDM device instance
2409 * @param pThis virtio-net device instance
2410 * @param pThisCC virtio-net device instance
2411 * @param pVirtqBuf pointer to buffer pulled from virtq (input to this function)
2412 */
2413static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2414 PVIRTQBUF pVirtqBuf)
2415{
2416 if (!(pThis->fNegotiatedFeatures & VIRTIONET_F_CTRL_VQ))
2417 LogFunc(("[%s] WARNING: Guest using CTRL queue w/o negotiating VIRTIONET_F_CTRL_VQ feature\n", pThis->szInst));
2418
2419 LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
2420
2421 if (pVirtqBuf->cbPhysSend < 2)
2422 {
2423 LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
2424 return;
2425 }
2426 else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
2427 {
2428 LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
2429 return;
2430 }
2431
2432 /*
2433 * Allocate buffer and read in the control command
2434 */
2435 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr = (PVIRTIONET_CTRL_HDR_T)RTMemAllocZ(sizeof(VIRTIONET_CTRL_HDR_T));
2436
2437 AssertPtrReturnVoid(pCtrlPktHdr);
2438
2439 AssertMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(VIRTIONET_CTRL_HDR_T),
2440 ("DESC chain too small for CTRL pkt header"));
2441
2442 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, pCtrlPktHdr,
2443 RT_MIN(pVirtqBuf->cbPhysSend, sizeof(VIRTIONET_CTRL_HDR_T)));
2444
2445 Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, pCtrlPktHdr->uClass, pCtrlPktHdr->uCmd));
2446
2447 uint8_t uAck;
2448 switch (pCtrlPktHdr->uClass)
2449 {
2450 case VIRTIONET_CTRL_RX:
2451 uAck = virtioNetR3CtrlRx(pThis, pThisCC, pCtrlPktHdr, pVirtqBuf);
2452 break;
2453 case VIRTIONET_CTRL_MAC:
2454 uAck = virtioNetR3CtrlMac(pThis, pCtrlPktHdr, pVirtqBuf);
2455 break;
2456 case VIRTIONET_CTRL_VLAN:
2457 uAck = virtioNetR3CtrlVlan(pThis, pCtrlPktHdr, pVirtqBuf);
2458 break;
2459 case VIRTIONET_CTRL_MQ:
2460 uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, pCtrlPktHdr, pVirtqBuf);
2461 break;
2462 case VIRTIONET_CTRL_ANNOUNCE:
2463 uAck = VIRTIONET_OK;
2464 if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
2465 {
2466 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
2467 "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
2468 break;
2469 }
2470 if (pCtrlPktHdr->uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
2471 {
2472 LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
2473 break;
2474 }
2475#if FEATURE_OFFERED(STATUS)
2476 pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
2477#endif
2478 Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
2479 break;
2480 default:
2481 LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", pCtrlPktHdr->uClass));
2482 uAck = VIRTIONET_ERROR;
2483 }
2484
2485 /* Currently CTRL pkt header just returns ack, but keeping segment logic generic/flexible
2486 * in case that changes to make adapting more straightforward
2487 */
2488 int cSegs = 1;
2489
2490 /* Return CTRL packet Ack byte (result code) to guest driver */
2491 PRTSGSEG paReturnSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG));
2492 AssertMsgReturnVoid(paReturnSegs, ("Out of memory"));
2493
2494 RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
2495 memcpy(paReturnSegs, aStaticSegs, sizeof(RTSGSEG));
2496
2497 PRTSGBUF pReturnSegBuf = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
2498 AssertMsgReturnVoid(pReturnSegBuf, ("Out of memory"));
2499
2500 /* Copy segment data to malloc'd memory to avoid stack out-of-scope errors sanitizer doesn't detect */
2501 for (int i = 0; i < cSegs; i++)
2502 {
2503 void *pv = paReturnSegs[i].pvSeg;
2504 paReturnSegs[i].pvSeg = RTMemAlloc(aStaticSegs[i].cbSeg);
2505 AssertMsgReturnVoid(paReturnSegs[i].pvSeg, ("Out of memory"));
2506 memcpy(paReturnSegs[i].pvSeg, pv, aStaticSegs[i].cbSeg);
2507 }
2508
2509 RTSgBufInit(pReturnSegBuf, paReturnSegs, cSegs);
2510
2511 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, pReturnSegBuf, pVirtqBuf, true /* fFence */);
2512 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
2513
2514 for (int i = 0; i < cSegs; i++)
2515 RTMemFree(paReturnSegs[i].pvSeg);
2516
2517 RTMemFree(paReturnSegs);
2518 RTMemFree(pReturnSegBuf);
2519
2520 LogFunc(("%s Finished processing CTRL command with status %s\n",
2521 pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
2522}
2523
2524/**
2525 * Reads virtio-net pkt header from provided Phy. addr of virtio descriptor chain
2526 * (e.g. S/G segment from guest-driver provided buffer pulled from Tx virtq)
2527 * Verifies state and supported modes, sets TCP header size.
2528 *
2529 * @param pVirtio VirtIO core instance data
2530 * @param pThis virtio-net instance
2531 * @param pDevIns PDM device instance
2532 * @param GCPhys Phys. Address from where to read virtio-net pkt header
2533 * @param pPktHdr Where to store read Tx pkt hdr (virtio pkt hdr size is determined from instance configuration)
2534 * @param cbFrame Total pkt frame size to inform bounds check
2535 */
2536static int virtioNetR3ReadVirtioTxPktHdr(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
2537{
2538 int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
2539 if (RT_FAILURE(rc))
2540 return rc;
2541
2542 LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
2543 pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
2544 pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
2545
2546 if (pPktHdr->uGsoType)
2547 {
2548 /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
2549 AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2550 && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
2551 ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
2552
2553 uint32_t uTcpHdrSize;
2554 switch (pPktHdr->uGsoType)
2555 {
2556 case VIRTIONET_HDR_GSO_TCPV4:
2557 case VIRTIONET_HDR_GSO_TCPV6:
2558 uTcpHdrSize = sizeof(RTNETTCP);
2559 break;
2560 case VIRTIONET_HDR_GSO_UDP:
2561 uTcpHdrSize = 0;
2562 break;
2563 default:
2564 LogFunc(("Bad GSO type in packet header\n"));
2565 return VERR_INVALID_PARAMETER;
2566 }
2567 /* Header + MSS must not exceed the packet size. */
2568 AssertMsgReturn(RT_LIKELY(uTcpHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
2569 ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
2570 }
2571
2572 AssertMsgReturn( !(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2573 || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
2574 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
2575 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
2576 VERR_BUFFER_OVERFLOW);
2577
2578 return VINF_SUCCESS;
2579}
2580
2581/**
2582 * Transmits single GSO frame via PDM framework to downstream PDM device, to emit from virtual NIC.
2583 *
2584 * This does final prep of GSO parameters including checksum calculation if configured
2585 * (e.g. if VIRTIONET_HDR_F_NEEDS_CSUM flag is set).
2586 *
2587 * @param pThis virtio-net instance
2588 * @param pThisCC virtio-net instance
2589 * @param pSgBuf PDM S/G buffer containing pkt and hdr to transmit
2590 * @param pGso GSO parameters used for the packet
2591 * @param pPktHdr virtio-net pkt header to adapt to PDM semantics
2592 */
2593static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
2594 PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
2595{
2596
2597 virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
2598 if (pGso)
2599 {
2600 /* Some guests (RHEL) may report HdrLen excluding transport layer header!
2601 * Thus cannot use cdHdrs provided by the guest because of different ways
2602 * it gets filled out by different versions of kernels. */
2603 Log4Func(("%s HdrLen before adjustment %d.\n", pThis->szInst, pGso->cbHdrsTotal));
2604 switch (pGso->u8Type)
2605 {
2606 case PDMNETWORKGSOTYPE_IPV4_TCP:
2607 case PDMNETWORKGSOTYPE_IPV6_TCP:
2608 pGso->cbHdrsTotal = pPktHdr->uChksumStart +
2609 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
2610 AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
2611 ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
2612 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
2613 break;
2614 case PDMNETWORKGSOTYPE_IPV4_UDP:
2615 pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
2616 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
2617 break;
2618 }
2619 /* Update GSO structure embedded into the frame */
2620 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
2621 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
2622 Log4Func(("%s adjusted HdrLen to %d.\n",
2623 pThis->szInst, pGso->cbHdrsTotal));
2624 Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
2625 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2626 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2627 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
2628 }
2629 else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2630 {
2631 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
2632 /*
2633 * This is not GSO frame but checksum offloading is requested.
2634 */
2635 virtioNetR3Calc16BitChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
2636 pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
2637 }
2638
2639 return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
2640}
2641
2642/**
2643 * Non-reentrant function transmits all available packets from specified Tx virtq to downstream
2644 * PDM device (if cable is connected). For each Tx pkt, virtio-net pkt header is converted
2645 * to required GSO information (VBox host network stack semantics)
2646 *
2647 * @param pDevIns PDM device instance
2648 * @param pThis virtio-net device instance
2649 * @param pThisCC virtio-net device instance
2650 * @param pTxVirtq Address of transmit virtq
2651 * @param fOnWorkerThread Flag to PDM whether to use caller's or or PDM transmit worker's thread.
2652 */
2653static int virtioNetR3TransmitPkts(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2654 PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
2655{
2656 PVIRTIOCORE pVirtio = &pThis->Virtio;
2657
2658
2659 if (!pThis->fVirtioReady)
2660 {
2661 LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
2662 pThis->szInst, pThis->virtioNetConfig.uStatus));
2663 return VERR_IGNORED;
2664 }
2665
2666 if (!pThis->fCableConnected)
2667 {
2668 Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
2669 return VERR_IGNORED;
2670 }
2671
2672 /*
2673 * Only one thread is allowed to transmit at a time, others should skip transmission as the packets
2674 * will be picked up by the transmitting thread.
2675 */
2676 if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
2677 return VERR_IGNORED;
2678
2679 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2680 if (pDrv)
2681 {
2682 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
2683 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
2684 if (rc == VERR_TRY_AGAIN)
2685 {
2686 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2687 return VERR_TRY_AGAIN;
2688 }
2689 }
2690 int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2691 if (!cPkts)
2692 {
2693 LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
2694
2695 if (pDrv)
2696 pDrv->pfnEndXmit(pDrv);
2697
2698 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2699 return VERR_MISSING;
2700 }
2701 LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
2702
2703 virtioNetR3SetWriteLed(pThisCC, true);
2704
2705 /* Disable notifications until all available descriptors have been processed */
2706 if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX))
2707 virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, false /* fEnable */);
2708
2709 int rc;
2710#ifdef VIRTIO_VBUF_ON_STACK
2711 VIRTQBUF_T VirtqBuf;
2712 PVIRTQBUF pVirtqBuf = &VirtqBuf;
2713 while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, pVirtqBuf)) == VINF_SUCCESS)
2714#else /* !VIRTIO_VBUF_ON_STACK */
2715 PVIRTQBUF pVirtqBuf = NULL;
2716 while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, &pVirtqBuf)) == VINF_SUCCESS)
2717#endif /* !VIRTIO_VBUF_ON_STACK */
2718 {
2719 Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
2720
2721 PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
2722 PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
2723 uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
2724 size_t uFrameSize = 0;
2725
2726 AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
2727 ("Desc chain's first seg has insufficient space for pkt header!\n"),
2728 VERR_INTERNAL_ERROR);
2729
2730#ifdef VIRTIO_VBUF_ON_STACK
2731 VIRTIONETPKTHDR PktHdr;
2732 PVIRTIONETPKTHDR pPktHdr = &PktHdr;
2733#else /* !VIRTIO_VBUF_ON_STACK */
2734 PVIRTIONETPKTHDR pPktHdr = (PVIRTIONETPKTHDR)RTMemAllocZ(pThis->cbPktHdr);
2735 AssertMsgReturn(pPktHdr, ("Out of Memory\n"), VERR_NO_MEMORY);
2736#endif /* !VIRTIO_VBUF_ON_STACK */
2737
2738 /* Compute total frame size from guest (including virtio-net pkt hdr) */
2739 for (unsigned i = 0; i < cSegsFromGuest && uFrameSize < VIRTIONET_MAX_FRAME_SIZE; i++)
2740 uFrameSize += paSegsFromGuest[i].cbSeg;
2741
2742 Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uFrameSize));
2743 Assert(uFrameSize <= VIRTIONET_MAX_FRAME_SIZE);
2744
2745 /* Truncate oversized frames. */
2746 if (uFrameSize > VIRTIONET_MAX_FRAME_SIZE)
2747 uFrameSize = VIRTIONET_MAX_FRAME_SIZE;
2748
2749 if (pThisCC->pDrv)
2750 {
2751 uFrameSize -= pThis->cbPktHdr;
2752 /*
2753 * Peel off pkt header and convert to PDM/GSO semantics.
2754 */
2755 rc = virtioNetR3ReadVirtioTxPktHdr(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uFrameSize /* cbFrame */);
2756 if (RT_FAILURE(rc))
2757 return rc;
2758 virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
2759
2760 PDMNETWORKGSO Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
2761
2762 /* Allocate PDM transmit buffer to send guest provided network frame from to VBox network leaf device */
2763 PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
2764 rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uFrameSize, pGso, &pSgBufToPdmLeafDevice);
2765
2766 /*
2767 * Copy virtio-net guest S/G buffer to PDM leaf driver S/G buffer
2768 * converting from GCphys to virt memory at the same time
2769 */
2770 if (RT_SUCCESS(rc))
2771 {
2772 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
2773 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
2774
2775 size_t cbCopied = 0;
2776 size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uFrameSize;
2777 uint64_t uOffset = 0;
2778 while (cbRemain)
2779 {
2780 PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
2781 uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
2782 uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
2783 uint64_t srcSgCur = (uint64_t)pSgPhysSend->GCPhysCur;
2784 cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
2785 virtioCoreGCPhysRead(pVirtio, pDevIns,
2786 (RTGCPHYS)pSgPhysSend->GCPhysCur,
2787 ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
2788 virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
2789 cbRemain -= cbCopied;
2790 uOffset += cbCopied;
2791 }
2792
2793 LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
2794 uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
2795
2796 rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
2797 if (RT_FAILURE(rc))
2798 {
2799 LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
2800 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2801 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
2802 pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
2803 }
2804 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2805 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
2806 }
2807 else
2808 {
2809 Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
2810 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
2811#ifndef VIRTIO_VBUF_ON_STACK
2812 virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
2813#endif /* !VIRTIO_VBUF_ON_STACK */
2814 break;
2815 }
2816
2817 virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
2818
2819 /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
2820 virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
2821 virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2822 }
2823
2824#ifndef VIRTIO_VBUF_ON_STACK
2825 virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
2826 pVirtqBuf = NULL;
2827#endif /* !VIRTIO_VBUF_ON_STACK */
2828 /* Before we break the loop we need to check if the queue is empty,
2829 * re-enable notifications, and then re-check again to avoid missing
2830 * a notification for the descriptor that is added to the queue
2831 * after we have checked it on being empty, but before we re-enabled
2832 * notifications.
2833 */
2834 if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
2835 && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
2836 virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
2837 }
2838 virtioNetR3SetWriteLed(pThisCC, false);
2839
2840 if (pDrv)
2841 pDrv->pfnEndXmit(pDrv);
2842
2843 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2844 return VINF_SUCCESS;
2845}
2846
2847/**
2848 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2849 */
2850static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
2851{
2852 LogFunc(("\n"));
2853 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2854 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2855 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2856 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(0)];
2857 STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
2858
2859 (void)virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
2860}
2861
2862/**
2863 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
2864 */
2865static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
2866{
2867 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2868 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2869
2870 SET_LINK_UP(pThis);
2871 virtioNetWakeupRxBufWaiter(pDevIns);
2872
2873 if (pThisCC->pDrv)
2874 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
2875
2876 LogFunc(("[%s] Link is up\n", pThis->szInst));
2877 RT_NOREF(hTimer, pvUser);
2878}
2879
2880/**
2881 * Takes down the link temporarily if its current status is up.
2882 *
2883 * This is used during restore and when replumbing the network link.
2884 *
2885 * The temporary link outage is supposed to indicate to the OS that all network
2886 * connections have been lost and that it for instance is appropriate to
2887 * renegotiate any DHCP lease.
2888 *
2889 * @param pDevIns The device instance.
2890 * @param pThis The virtio-net shared instance data.
2891 * @param pThisCC The virtio-net ring-3 instance data.
2892 */
2893static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2894{
2895 if (IS_LINK_UP(pThis))
2896 {
2897 SET_LINK_DOWN(pThis);
2898
2899 /* Re-establish link in 5 seconds. */
2900 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
2901 AssertRC(rc);
2902
2903 LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
2904 }
2905}
2906
2907/**
2908 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
2909 */
2910static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
2911{
2912 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2913 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2914 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2915
2916 bool fRequestedLinkStateIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
2917
2918#ifdef LOG_ENABLED
2919 if (LogIs7Enabled())
2920 {
2921 LogFunc(("[%s]", pThis->szInst));
2922 switch(enmState)
2923 {
2924 case PDMNETWORKLINKSTATE_UP:
2925 Log(("UP\n"));
2926 break;
2927 case PDMNETWORKLINKSTATE_DOWN:
2928 Log(("DOWN\n"));
2929 break;
2930 case PDMNETWORKLINKSTATE_DOWN_RESUME:
2931 Log(("DOWN (RESUME)\n"));
2932 break;
2933 default:
2934 Log(("UNKNOWN)\n"));
2935 }
2936 }
2937#endif
2938
2939 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
2940 {
2941 if (IS_LINK_UP(pThis))
2942 {
2943 /*
2944 * We bother to bring the link down only if it was up previously. The UP link state
2945 * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
2946 */
2947 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
2948 if (pThisCC->pDrv)
2949 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2950 }
2951 }
2952 else if (fRequestedLinkStateIsUp != IS_LINK_UP(pThis))
2953 {
2954 if (fRequestedLinkStateIsUp)
2955 {
2956 Log(("[%s] Link is up\n", pThis->szInst));
2957 pThis->fCableConnected = true;
2958 SET_LINK_UP(pThis);
2959 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2960 }
2961 else /* Link requested to be brought down */
2962 {
2963 /* The link was brought down explicitly, make sure it won't come up by timer. */
2964 PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
2965 Log(("[%s] Link is down\n", pThis->szInst));
2966 pThis->fCableConnected = false;
2967 SET_LINK_DOWN(pThis);
2968 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2969 }
2970 if (pThisCC->pDrv)
2971 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2972 }
2973 return VINF_SUCCESS;
2974}
2975/**
2976 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
2977 */
2978static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
2979{
2980 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2981 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2982
2983 return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
2984}
2985
2986static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2987{
2988 Log10Func(("[%s]\n", pThis->szInst));
2989 int rc = VINF_SUCCESS;
2990 for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
2991 {
2992 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uIdxWorker];
2993 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
2994
2995 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2996 {
2997 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
2998 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2999 }
3000 if (pWorkerR3->pThread)
3001 {
3002 int rcThread;
3003 rc = PDMDevHlpThreadDestroy(pDevIns, pWorkerR3->pThread, &rcThread);
3004 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
3005 AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
3006 pWorkerR3->pThread = NULL;
3007 }
3008 }
3009 return rc;
3010}
3011
3012/**
3013 * Creates a worker for specified queue, along with semaphore to throttle the worker.
3014 *
3015 * @param pDevIns - PDM device instance
3016 * @param pThis - virtio-net instance
3017 * @param pWorker - Pointer to worker state
3018 * @param pWorkerR3 - Pointer to worker state
3019 * @param pVirtq - Pointer to virtq
3020 */
3021static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis,
3022 PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
3023 PVIRTIONETVIRTQ pVirtq)
3024{
3025 Log10Func(("[%s]\n", pThis->szInst));
3026 RT_NOREF(pThis);
3027
3028 int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess);
3029
3030 if (RT_FAILURE(rc))
3031 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
3032 N_("DevVirtioNET: Failed to create SUP event semaphore"));
3033
3034 LogFunc(("creating thread for queue %s\n", pVirtq->szName));
3035
3036 rc = PDMDevHlpThreadCreate(pDevIns, &pWorkerR3->pThread,
3037 (void *)pWorker, virtioNetR3WorkerThread,
3038 virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, pVirtq->szName);
3039 if (RT_FAILURE(rc))
3040 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
3041 N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
3042
3043 pWorker->fAssigned = true; /* Because worker's state in fixed-size array initialized w/empty slots */
3044
3045 LogFunc(("%s pThread: %p\n", pVirtq->szName, pWorkerR3->pThread));
3046
3047 return rc;
3048}
3049
3050static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
3051{
3052 Log10Func(("[%s]\n", pThis->szInst));
3053 int rc;
3054
3055 /* Create the Control Queue worker anyway whether or not it is feature-negotiated or utilized by the guest.
3056 * See related comment for queue construction in the device constructor function for more context.
3057 */
3058
3059 PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
3060 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis,
3061 &pThis->aWorkers[CTRLQIDX], &pThisCC->aWorkers[CTRLQIDX], pCtlVirtq);
3062 AssertRCReturn(rc, rc);
3063
3064 pCtlVirtq->fHasWorker = true;
3065
3066 for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
3067 {
3068 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
3069 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
3070
3071 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, &pThis->aWorkers[TXQIDX(uVirtqPair)],
3072 &pThisCC->aWorkers[TXQIDX(uVirtqPair)], pTxVirtq);
3073 AssertRCReturn(rc, rc);
3074
3075 pTxVirtq->fHasWorker = true;
3076 pRxVirtq->fHasWorker = false;
3077 }
3078
3079 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
3080 pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
3081
3082 pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
3083
3084 return rc;
3085}
3086
3087static void virtioNetConfigurePktHdr(PVIRTIONET pThis, uint32_t fLegacy)
3088{
3089 /* Calculate network packet header type and size based on what we know now */
3090 pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
3091 if (!fLegacy)
3092 /* Modern (e.g. >= VirtIO 1.0) device specification's pkt size rules */
3093 if (FEATURE_ENABLED(MRG_RXBUF))
3094 pThis->ePktHdrType = kVirtioNetModernPktHdrWithMrgRx;
3095 else /* Modern guest driver with MRG_RX feature disabled */
3096 pThis->ePktHdrType = kVirtioNetModernPktHdrWithoutMrgRx;
3097 else
3098 {
3099 /* Legacy (e.g. < VirtIO 1.0) device specification's pkt size rules */
3100 if (FEATURE_ENABLED(MRG_RXBUF))
3101 pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithMrgRx;
3102 else /* Legacy guest with MRG_RX feature disabled */
3103 {
3104 pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
3105 pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
3106 }
3107 }
3108}
3109
3110/**
3111 * @callback_method_impl{FNPDMTHREADDEV}
3112 */
3113static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3114{
3115 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3116 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3117 PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
3118 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[pWorker->uIdx];
3119 uint16_t uIdx = pWorker->uIdx;
3120
3121 ASMAtomicWriteBool(&pWorker->fSleeping, false);
3122
3123 Assert(pWorker->uIdx == pVirtq->uIdx);
3124
3125 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
3126 return VINF_SUCCESS;
3127
3128 LogFunc(("[%s] worker thread idx=%d started for %s (virtq idx=%d)\n", pThis->szInst, pWorker->uIdx, pVirtq->szName, pVirtq->uIdx));
3129
3130 /** @todo Race w/guest enabling/disabling guest notifications cyclically.
3131 See BugRef #8651, Comment #82 */
3132 virtioCoreVirtqEnableNotify(&pThis->Virtio, uIdx, true /* fEnable */);
3133
3134 while ( pThread->enmState != PDMTHREADSTATE_TERMINATING
3135 && pThread->enmState != PDMTHREADSTATE_TERMINATED)
3136 {
3137 if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pVirtq->uIdx))
3138 {
3139 /* Precisely coordinated atomic interlocks avoid a race condition that results in hung thread
3140 * wherein a sloppily coordinated wake-up notification during a transition into or out
3141 * of sleep leaves notifier and target mutually confused about actual & intended state.
3142 */
3143 ASMAtomicWriteBool(&pWorker->fSleeping, true);
3144 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
3145 if (!fNotificationSent)
3146 {
3147 Log10Func(("[%s] %s worker sleeping...\n\n", pThis->szInst, pVirtq->szName));
3148 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
3149
3150 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
3151 STAM_COUNTER_INC(&pThis->StatTransmitByThread);
3152 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
3153 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
3154 return VINF_SUCCESS;
3155 if (rc == VERR_INTERRUPTED)
3156 continue;
3157 ASMAtomicWriteBool(&pWorker->fNotified, false);
3158 }
3159 ASMAtomicWriteBool(&pWorker->fSleeping, false);
3160 }
3161 /*
3162 * Dispatch to the handler for the queue this worker is set up to drive
3163 */
3164 if (pVirtq->fCtlVirtq)
3165 {
3166 Log10Func(("[%s] %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
3167#ifdef VIRTIO_VBUF_ON_STACK
3168 VIRTQBUF_T VirtqBuf;
3169 PVIRTQBUF pVirtqBuf = &VirtqBuf;
3170 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, pVirtqBuf, true);
3171#else /* !VIRTIO_VBUF_ON_STACK */
3172 PVIRTQBUF pVirtqBuf = NULL;
3173 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, &pVirtqBuf, true);
3174#endif /* !VIRTIO_VBUF_ON_STACK */
3175 if (rc == VERR_NOT_AVAILABLE)
3176 {
3177 Log10Func(("[%s] %s worker woken. Nothing found in queue\n", pThis->szInst, pVirtq->szName));
3178 continue;
3179 }
3180 virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
3181#ifndef VIRTIO_VBUF_ON_STACK
3182 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
3183#endif /* !VIRTIO_VBUF_ON_STACK */
3184 }
3185 else /* Must be Tx queue */
3186 {
3187 Log10Func(("[%s] %s worker woken. Virtq has data to transmit\n", pThis->szInst, pVirtq->szName));
3188 virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
3189 }
3190 /* Note: Surprise! Rx queues aren't handled by local worker threads. Instead, the PDM network leaf driver
3191 * invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback, which waits until woken by virtioNetVirtqNotified()
3192 * indicating that guest IN buffers have been added to Rx virt queue.
3193 */
3194 }
3195 Log10(("[%s] %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
3196 return VINF_SUCCESS;
3197}
3198
3199/**
3200 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
3201 *
3202 * Called back by the core code when VirtIO's ready state has changed.
3203 */
3204static DECLCALLBACK(void) virtioNetR3StatusChg(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
3205{
3206 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
3207 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
3208
3209 pThis->fVirtioReady = fVirtioReady;
3210
3211 if (fVirtioReady)
3212 {
3213#ifdef LOG_ENABLED
3214 Log(("\n%-23s: %s *** VirtIO Ready ***\n\n", __FUNCTION__, pThis->szInst));
3215 virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
3216#endif
3217 pThis->fResetting = false;
3218 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(pVirtio);
3219 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
3220
3221 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3222 {
3223 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
3224 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
3225
3226 Assert(pWorker->uIdx == uVirtqNbr);
3227 RT_NOREF(pWorker);
3228
3229 Assert(pVirtq->uIdx == pWorker->uIdx);
3230
3231 (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
3232 pVirtq->fAttachedToVirtioCore = true;
3233 if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
3234 virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
3235 }
3236 }
3237 else
3238 {
3239 Log(("\n%-23s: %s VirtIO is resetting ***\n", __FUNCTION__, pThis->szInst));
3240
3241 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
3242 Log7(("%-23s: %s Link is %s\n", __FUNCTION__, pThis->szInst, pThis->fCableConnected ? "up" : "down"));
3243
3244 pThis->fPromiscuous = true;
3245 pThis->fAllMulticast = false;
3246 pThis->fAllUnicast = false;
3247 pThis->fNoMulticast = false;
3248 pThis->fNoUnicast = false;
3249 pThis->fNoBroadcast = false;
3250 pThis->uIsTransmitting = 0;
3251 pThis->cUnicastFilterMacs = 0;
3252 pThis->cMulticastFilterMacs = 0;
3253
3254 memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
3255 memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
3256 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
3257
3258 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
3259
3260 for (uint16_t uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3261 {
3262 virtioCoreR3VirtqDetach(&pThis->Virtio, uVirtqNbr);
3263 pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore = false;
3264 }
3265 }
3266}
3267
3268/**
3269 * @callback_method_impl{VIRTIOCORER3,pfnFeatureNegotiationComplete}
3270 */
3271static DECLCALLBACK(void) pfnFeatureNegotiationComplete(PVIRTIOCORE pVirtio, uint64_t fDriverFeatures, uint32_t fLegacy)
3272{
3273 PVIRTIONET pThis = PDMDEVINS_2_DATA(pVirtio->pDevInsR3, PVIRTIONET);
3274
3275 LogFunc(("[Feature Negotiation Complete] Guest Driver version is: %s\n", fLegacy ? "legacy" : "modern"));
3276 virtioNetConfigurePktHdr(pThis, fLegacy);
3277 virtioNetR3SetVirtqNames(pThis, fLegacy);
3278
3279 /* Senseless for modern guest to use control queue in this case. (See Note 1 in PDM-invoked device constructor) */
3280 if (!fLegacy && !(fDriverFeatures & VIRTIONET_F_CTRL_VQ))
3281 virtioNetR3VirtqDestroy(pVirtio, &pThis->aVirtqs[CTRLQIDX]);
3282}
3283
3284#endif /* IN_RING3 */
3285
3286/**
3287 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
3288 *
3289 * The VM is suspended at this point.
3290 */
3291static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3292{
3293 RT_NOREF(fFlags);
3294
3295 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3296 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3297
3298 Log7Func(("[%s]\n", pThis->szInst));
3299 RT_NOREF(pThis);
3300
3301 AssertLogRelReturnVoid(iLUN == 0);
3302
3303 pThisCC->pDrvBase = NULL;
3304 pThisCC->pDrv = NULL;
3305}
3306
3307/**
3308 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
3309 *
3310 * This is called when we change block driver.
3311 */
3312static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3313{
3314 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3315 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3316
3317 Log7Func(("[%s]", pThis->szInst));
3318 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
3319
3320 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
3321 if (RT_SUCCESS(rc))
3322 {
3323 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
3324 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
3325 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
3326 }
3327 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
3328 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
3329 Log(("[%s] No attached driver!\n", pThis->szInst));
3330
3331 RT_NOREF2(pThis, fFlags);
3332 return rc;
3333}
3334
3335/**
3336 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
3337 */
3338static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
3339{
3340 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
3341 if (iLUN)
3342 return VERR_PDM_LUN_NOT_FOUND;
3343 *ppLed = &pThisR3->led;
3344 return VINF_SUCCESS;
3345}
3346
3347/**
3348 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3349 */
3350static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
3351{
3352 PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
3353 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
3354 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
3355 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
3356 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
3357 return NULL;
3358}
3359
3360/**
3361 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
3362 */
3363static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
3364{
3365 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
3366
3367 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3368 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3369
3370 Log(("[%s] Destroying instance\n", pThis->szInst));
3371 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
3372 {
3373 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
3374 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
3375 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
3376 }
3377
3378 virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
3379 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
3380 return VINF_SUCCESS;
3381}
3382
3383/**
3384 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
3385 *
3386 * Notes about revising originally VirtIO 1.0+ only virtio-net device emulator to be "transitional",
3387 * a VirtIO term meaning this now interoperates with both "legacy" (e.g. pre-1.0) and "modern" (1.0+)
3388 * guest virtio-net drivers. The changes include migrating VMs saved using prior DevVirtioNet.cpp (0.95)
3389 * saveExec/loadExec semantics to use 1.0 save/load semantics.
3390 *
3391 * Regardless of the 1.0 spec's overall helpful guidance for implementing transitional devices,
3392 * A bit is left to the imagination, e.g. some things have to be determined deductively
3393 * (AKA "the hard way").
3394 *
3395 * Case in point: According to VirtIO 0.95 ("legacy") specification, section 2.2.1, "historically"
3396 * drivers may start driving prior to feature negotiation and prior to drivers setting DRIVER_OK
3397 * status, "provided driver doesn't use features that alter early use of this device". Interpreted
3398 * here to mean a virtio-net driver must respect default settings (such as implicit pkt header default
3399 * size, as determined per Note 1 below).
3400 *
3401 * ----------------------------------------------------------------------------------------------
3402 * Transitional device initialization Note 1: Identifying default value for network Rx pkt hdr size.
3403 * (VirtIO 1.0 specification section 5.1.6.1)
3404 *
3405 * Guest virtio legacy drivers may begin operations prematurely, regardless of early spec's
3406 * initialization sequence (see note 2 below). Legacy drivers implicitly default to using the
3407 * (historically) shortest-length network packet header *unless* VIRTIONET_F_MRG_RXBUF feature is
3408 * negotiated. If feature negotiation phase is [optionally] enacted by a legacy guest (i.e. we strictly
3409 * enforce full initialization protocol for modern guests), virtioNetConfigurePktHdr() is invoked again to
3410 * finalize device's network packet header size. Best-guess at default packet header size is deduced, e.g.
3411 * isn't documented, as follows: A legacy guest with VIRTIONET_F_MRG_RXBUF not-yet-negotiated is the only
3412 * case where network I/O could possibly occur with any reasonable assumption about packet type/size,
3413 * because logically other permutations couldn't possibly be inferred until feature negotiation
3414 * is complete. Specifically, those cases are:
3415 *
3416 * 1. A modern driver (detected only when VIRTIONET_F_VERSION_1 feature is ack'd by guest, and,
3417 * simultaneously, VIRTIONET_F_MRG_RXBUF feature is accepted or declined (determining network receive-packet
3418 * processing behavior).
3419 *
3420 * 2. A legacy driver that has agreed to use VIRTIONET_F_MRG_RXBUF feature, resulting in a two-byte larger pkt hdr,
3421 * (as well as deciding Rx packet processing behavior).
3422 *
3423 * ----------------------------------------------------------------------------------------------
3424 * Transitional device initialization Note 2: Creating unnegotiated control queue.
3425 * (VirtIO 1.0 spec, sections 5.1.5 and 5.1.6.5)
3426 *
3427 * Create all queues immediately, prior to feature negotiation, including control queue (irrespective
3428 * of the fact it's too early in initialization for control feature to be approved by guest). This
3429 * transitional device must deal with legacy guests which *can* (and on linux have been seen to) use
3430 * the control queue prior to feature negotiation.
3431 *
3432 * The initial assumption is *modern" guest virtio-net drivers out in the wild could never reasonably
3433 * attempt something as obviously risky as using ctrlq without first acking VIRTIO_NET_F_CTRL_VQ
3434 * feature to establish it. For now, we create the control queue proactively to accomodate a potentially
3435 * badly behaved but officially sanctioned legacy virtio-net driver, but *destroy* that same queue
3436 * if a driver announces as 'modern' during feature finalization yet leaves VIRTIO_NET_F_CTRL_VQ un-ack'd.
3437 */
3438static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3439{
3440 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3441 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3442 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3443 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3444
3445 /*
3446 * Quickly initialize state data to ensure destructor always works.
3447 */
3448 Log7Func(("PDM device instance: %d\n", iInstance));
3449 RTStrPrintf(pThis->szInst, sizeof(pThis->szInst), "virtio-net #%d", iInstance);
3450
3451 pThisCC->pDevIns = pDevIns;
3452 pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
3453 pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
3454 pThisCC->led.u32Magic = PDMLED_MAGIC;
3455
3456 /* Interfaces */
3457 pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
3458 pThisCC->INetworkDown.pfnReceive = virtioNetR3NetworkDown_Receive;
3459 pThisCC->INetworkDown.pfnReceiveGso = virtioNetR3NetworkDown_ReceiveGso;
3460 pThisCC->INetworkDown.pfnXmitPending = virtioNetR3NetworkDown_XmitPending;
3461 pThisCC->INetworkConfig.pfnGetMac = virtioNetR3NetworkConfig_GetMac;
3462 pThisCC->INetworkConfig.pfnGetLinkState = virtioNetR3NetworkConfig_GetLinkState;
3463 pThisCC->INetworkConfig.pfnSetLinkState = virtioNetR3NetworkConfig_SetLinkState;
3464
3465 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
3466
3467 /*
3468 * Validate configuration.
3469 */
3470 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo|Legacy", "");
3471
3472 /* Get config params */
3473 int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
3474 if (RT_FAILURE(rc))
3475 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
3476
3477 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
3478 if (RT_FAILURE(rc))
3479 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
3480
3481 uint32_t uStatNo = iInstance;
3482 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
3483 if (RT_FAILURE(rc))
3484 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
3485
3486 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
3487 if (RT_FAILURE(rc))
3488 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
3489
3490 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
3491
3492 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
3493 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
3494 pThis->szInst, pThis->cMsLinkUpDelay / 1000));
3495
3496 Log(("[%s] Link up delay is set to %u seconds\n", pThis->szInst, pThis->cMsLinkUpDelay / 1000));
3497
3498 /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
3499 memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
3500
3501 Log(("Using MAC address for %s: %2x:%2x:%2x:%2x:%2x:%2x\n", pThis->szInst,
3502 pThis->macConfigured.au8[0], pThis->macConfigured.au8[1], pThis->macConfigured.au8[2],
3503 pThis->macConfigured.au8[3], pThis->macConfigured.au8[4], pThis->macConfigured.au8[5]));
3504
3505 LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
3506
3507 /*
3508 * Configure Virtio core (generic Virtio queue and infrastructure management) parameters.
3509 */
3510# if FEATURE_OFFERED(STATUS)
3511 pThis->virtioNetConfig.uStatus = 0;
3512# endif
3513
3514 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
3515 pThisCC->Virtio.pfnFeatureNegotiationComplete = pfnFeatureNegotiationComplete;
3516 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3517 pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChg;
3518 pThisCC->Virtio.pfnDevCapRead = virtioNetR3DevCapRead;
3519 pThisCC->Virtio.pfnDevCapWrite = virtioNetR3DevCapWrite;
3520
3521 VIRTIOPCIPARAMS VirtioPciParams;
3522 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
3523 VirtioPciParams.uClassBase = PCI_CLASS_BASE_NETWORK_CONTROLLER;
3524 VirtioPciParams.uClassSub = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
3525 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
3526 VirtioPciParams.uSubsystemId = PCI_DEVICE_ID_VIRTIONET_HOST; /* VirtIO 1.0 allows PCI Device ID here */
3527 VirtioPciParams.uInterruptLine = 0x00;
3528 VirtioPciParams.uInterruptPin = 0x01;
3529
3530 /* Create semaphore used to synchronize/throttle the downstream LUN's Rx waiter thread. */
3531 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
3532 if (RT_FAILURE(rc))
3533 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
3534
3535 pThis->fOfferLegacy = VIRTIONET_TRANSITIONAL_ENABLE_FLAG;
3536 virtioNetConfigurePktHdr(pThis, pThis->fOfferLegacy); /* set defaults */
3537
3538 /* Initialize VirtIO core. (*pfnStatusChanged)() callback occurs when both host VirtIO core & guest driver are ready) */
3539 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
3540 VIRTIONET_HOST_FEATURES_OFFERED, pThis->fOfferLegacy,
3541 &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
3542 if (RT_FAILURE(rc))
3543 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
3544
3545 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
3546 if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
3547 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
3548 pThis->cVirtqPairs = pThis->virtioNetConfig.uMaxVirtqPairs;
3549 pThis->cVirtqs += pThis->cVirtqPairs * 2 + 1;
3550 pThis->aVirtqs[CTRLQIDX].fCtlVirtq = true;
3551
3552 virtioNetR3SetVirtqNames(pThis, pThis->fOfferLegacy);
3553 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3554 {
3555 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
3556 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
3557 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
3558 pVirtq->uIdx = pWorker->uIdx = pWorkerR3->uIdx = uVirtqNbr;
3559 }
3560 /*
3561 * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
3562 */
3563 rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
3564 if (RT_FAILURE(rc))
3565 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
3566
3567 /* Create Link Up Timer */
3568 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL,
3569 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
3570 "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
3571 /*
3572 * Attach network driver instance
3573 */
3574 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
3575 if (RT_SUCCESS(rc))
3576 {
3577 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
3578 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
3579 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
3580 }
3581 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
3582 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
3583 {
3584 Log(("[%s] No attached driver!\n", pThis->szInst));
3585 AssertRCReturn(rc, rc);
3586 }
3587 /*
3588 * Status driver
3589 */
3590 PPDMIBASE pUpBase;
3591 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
3592 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
3593 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
3594
3595 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
3596 /*
3597 * Register saved state.
3598 */
3599 rc = PDMDevHlpSSMRegister(pDevIns, VIRTIONET_SAVEDSTATE_VERSION, sizeof(*pThis),
3600 virtioNetR3ModernSaveExec, virtioNetR3ModernDeviceLoadExec);
3601 AssertRCReturn(rc, rc);
3602 /*
3603 * Statistics and debug stuff.
3604 * The /Public/ bits are official and used by session info in the GUI.
3605 */
3606# ifdef VBOX_WITH_STATISTICS
3607 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3608 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
3609 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3610 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
3611 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
3612 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
3613
3614 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
3615 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
3616 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
3617 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
3618 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
3619 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
3620 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
3621 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
3622 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
3623 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
3624 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
3625 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
3626 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
3627 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
3628# endif
3629 /*
3630 * Register the debugger info callback (ignore errors).
3631 */
3632 char szTmp[128];
3633 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "virtio-net", "Display virtio-net info (help, net, features, state, pointers, queues, all)", virtioNetR3Info);
3634 if (RT_FAILURE(rc))
3635 LogRel(("Failed to register DBGF info for device %s\n", szTmp));
3636 return rc;
3637}
3638
3639#else /* !IN_RING3 */
3640
3641/**
3642 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3643 */
3644static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
3645{
3646 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3647 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3648 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3649 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3650 return virtioCoreRZInit(pDevIns, &pThis->Virtio);
3651}
3652
3653#endif /* !IN_RING3 */
3654
3655/**
3656 * The device registration structure.
3657 */
3658const PDMDEVREG g_DeviceVirtioNet =
3659{
3660 /* .uVersion = */ PDM_DEVREG_VERSION,
3661 /* .uReserved0 = */ 0,
3662 /* .szName = */ "virtio-net",
3663 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
3664 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
3665 /* .cMaxInstances = */ ~0U,
3666 /* .uSharedVersion = */ 42,
3667 /* .cbInstanceShared = */ sizeof(VIRTIONET),
3668 /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
3669 /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
3670 /* .cMaxPciDevices = */ 1,
3671 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
3672 /* .pszDescription = */ "Virtio Host NET.\n",
3673#if defined(IN_RING3)
3674 /* .pszRCMod = */ "VBoxDDRC.rc",
3675 /* .pszR0Mod = */ "VBoxDDR0.r0",
3676 /* .pfnConstruct = */ virtioNetR3Construct,
3677 /* .pfnDestruct = */ virtioNetR3Destruct,
3678 /* .pfnRelocate = */ NULL,
3679 /* .pfnMemSetup = */ NULL,
3680 /* .pfnPowerOn = */ NULL,
3681 /* .pfnReset = */ NULL,
3682 /* .pfnSuspend = */ virtioNetWakeupRxBufWaiter,
3683 /* .pfnResume = */ NULL,
3684 /* .pfnAttach = */ virtioNetR3Attach,
3685 /* .pfnDetach = */ virtioNetR3Detach,
3686 /* .pfnQueryInterface = */ NULL,
3687 /* .pfnInitComplete = */ NULL,
3688 /* .pfnPowerOff = */ virtioNetWakeupRxBufWaiter,
3689 /* .pfnSoftReset = */ NULL,
3690 /* .pfnReserved0 = */ NULL,
3691 /* .pfnReserved1 = */ NULL,
3692 /* .pfnReserved2 = */ NULL,
3693 /* .pfnReserved3 = */ NULL,
3694 /* .pfnReserved4 = */ NULL,
3695 /* .pfnReserved5 = */ NULL,
3696 /* .pfnReserved6 = */ NULL,
3697 /* .pfnReserved7 = */ NULL,
3698#elif defined(IN_RING0)
3699 /* .pfnEarlyConstruct = */ NULL,
3700 /* .pfnConstruct = */ virtioNetRZConstruct,
3701 /* .pfnDestruct = */ NULL,
3702 /* .pfnFinalDestruct = */ NULL,
3703 /* .pfnRequest = */ NULL,
3704 /* .pfnReserved0 = */ NULL,
3705 /* .pfnReserved1 = */ NULL,
3706 /* .pfnReserved2 = */ NULL,
3707 /* .pfnReserved3 = */ NULL,
3708 /* .pfnReserved4 = */ NULL,
3709 /* .pfnReserved5 = */ NULL,
3710 /* .pfnReserved6 = */ NULL,
3711 /* .pfnReserved7 = */ NULL,
3712#elif defined(IN_RC)
3713 /* .pfnConstruct = */ virtioNetRZConstruct,
3714 /* .pfnReserved0 = */ NULL,
3715 /* .pfnReserved1 = */ NULL,
3716 /* .pfnReserved2 = */ NULL,
3717 /* .pfnReserved3 = */ NULL,
3718 /* .pfnReserved4 = */ NULL,
3719 /* .pfnReserved5 = */ NULL,
3720 /* .pfnReserved6 = */ NULL,
3721 /* .pfnReserved7 = */ NULL,
3722#else
3723# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3724#endif
3725 /* .uVersionEnd = */ PDM_DEVREG_VERSION
3726};
3727
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