VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevVirtioNet.cpp@ 98523

Last change on this file since 98523 was 98121, checked in by vboxsync, 22 months ago

Devices/DevVirtioNet: Fix logic for attaching driver at device construct time which caused VM start errors before when no driver was attached. Add a missing safety check if pDrv is NULL. Also fix some indentation issues and a duplicate message.

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

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