VirtualBox

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

Last change on this file since 84087 was 84048, checked in by vboxsync, 5 years ago

Network/DevVirtioNet.cpp changes pertaining to release mode testing simply adding the PDM_DEVREG_FLAGS_RZ. Next to ensure that DevVirtioNet_1.0.cpp and Virtio_1_0.cpp both make full use of ring 0 features

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 124.4 KB
Line 
1/* $Id: DevVirtioNet_1_0.cpp 84048 2020-04-28 13:41:41Z vboxsync $ $Revision: 84048 $ $Date: 2020-04-28 13:41:41 +0000 (Tue, 28 Apr 2020) $ $Author: vboxsync $ */
2
3/** @file
4 * VBox storage devices - Virtio NET Driver
5 *
6 * Log-levels used:
7 * - Level 1: The most important (but usually rare) things to note
8 * - Level 2: NET command logging
9 * - Level 3: Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
10 * - Level 6: Device <-> Guest Driver negotation, traffic, notifications and state handling
11 * - Level 12: Brief formatted hex dumps of I/O data
12 */
13
14/*
15 * Copyright (C) 2006-2020 Oracle Corporation
16 *
17 * This file is part of VirtualBox Open Source Edition (OSE), as
18 * available from http://www.virtualbox.org. This file is free software;
19 * you can redistribute it and/or modify it under the terms of the GNU
20 * General Public License (GPL) as published by the Free Software
21 * Foundation, in version 2 as it comes in the "COPYING" file of the
22 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30//#define LOG_GROUP LOG_GROUP_DRV_NET
31#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
32#define VIRTIONET_WITH_GSO
33#include <iprt/types.h>
34
35#include <VBox/vmm/pdmdev.h>
36#include <VBox/vmm/stam.h>
37#include <VBox/vmm/pdmcritsect.h>
38#include <VBox/vmm/pdmnetifs.h>
39#include <VBox/msi.h>
40#include <VBox/version.h>
41//#include <VBox/asm.h>
42#include <VBox/log.h>
43#include <iprt/errcore.h>
44#include <iprt/assert.h>
45#include <iprt/string.h>
46#include <VBox/sup.h>
47#ifdef IN_RING3
48#include <VBox/VBoxPktDmp.h>
49#endif
50#ifdef IN_RING3
51# include <iprt/alloc.h>
52# include <iprt/memcache.h>
53# include <iprt/semaphore.h>
54# include <iprt/sg.h>
55# include <iprt/param.h>
56# include <iprt/uuid.h>
57#endif
58#include "../VirtIO/Virtio_1_0.h"
59
60#include "VBoxDD.h"
61
62/* After debugging single instance case, restore instance name logging */
63#define INSTANCE(pState) (char *)(pState->szInstanceName ? "" : "") // Avoid requiring RT_NOREF in some funcs
64
65#define VIRTIONET_SAVED_STATE_VERSION UINT32_C(1)
66#define VIRTIONET_MAX_QPAIRS 1
67#define VIRTIONET_MAX_QUEUES (VIRTIONET_MAX_QPAIRS * 2 + 1)
68#define VIRTIONET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP pkt size + Ethernet header with VLAN tag */
69#define VIRTIONET_MAC_FILTER_LEN 32
70#define VIRTIONET_MAX_VLAN_ID (1 << 12)
71#define VIRTIONET_PREALLOCATE_RX_SEG_COUNT 32
72
73#define VIRTQNAME(idxQueue) (pThis->aszVirtqNames[idxQueue])
74#define CBVIRTQNAME(idxQueue) RTStrNLen(VIRTQNAME(idxQueue), sizeof(VIRTQNAME(idxQueue)))
75#define FEATURE_ENABLED(feature) RT_BOOL(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature)
76#define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
77#define FEATURE_OFFERED(feature) VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
78
79#define SET_LINK_UP(pState) \
80 LogFunc(("SET_LINK_UP\n")); \
81 pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
82 virtioCoreNotifyConfigChanged(&pThis->Virtio)
83
84#define SET_LINK_DOWN(pState) \
85 LogFunc(("SET_LINK_DOWN\n")); \
86 pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
87 virtioCoreNotifyConfigChanged(&pThis->Virtio)
88
89#define IS_LINK_UP(pState) (pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
90#define IS_LINK_DOWN(pState) !IS_LINK_UP(pState)
91
92/* Macros to calculate queue specific index number VirtIO 1.0, 5.1.2 */
93#define IS_TX_QUEUE(n) ((n) != CTRLQIDX && ((n) & 1))
94#define IS_RX_QUEUE(n) ((n) != CTRLQIDX && !IS_TX_QUEUE(n))
95#define IS_CTRL_QUEUE(n) ((n) == CTRLQIDX)
96#define RXQIDX(qPairIdx) (qPairIdx * 2)
97#define TXQIDX(qPairIdx) (qPairIdx * 2 + 1)
98#define CTRLQIDX (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
99
100#define LUN0 0
101
102/*
103 * Glossary of networking acronyms used in the following bit definitions:
104 *
105 * GSO = Generic Segmentation Offload
106 * TSO = TCP Segmentation Offload
107 * UFO = UDP Fragmentation Offload
108 * ECN = Explicit Congestion Notification
109 */
110
111/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
112 * @{ */
113#define VIRTIONET_F_CSUM RT_BIT_64(0) /**< Handle packets with partial checksum */
114#define VIRTIONET_F_GUEST_CSUM RT_BIT_64(1) /**< Handles packets with partial checksum */
115#define VIRTIONET_F_CTRL_GUEST_OFFLOADS RT_BIT_64(2) /**< Control channel offloads reconfig support */
116#define VIRTIONET_F_MAC RT_BIT_64(5) /**< Device has given MAC address */
117#define VIRTIONET_F_GUEST_TSO4 RT_BIT_64(7) /**< Driver can receive TSOv4 */
118#define VIRTIONET_F_GUEST_TSO6 RT_BIT_64(8) /**< Driver can receive TSOv6 */
119#define VIRTIONET_F_GUEST_ECN RT_BIT_64(9) /**< Driver can receive TSO with ECN */
120#define VIRTIONET_F_GUEST_UFO RT_BIT_64(10) /**< Driver can receive UFO */
121#define VIRTIONET_F_HOST_TSO4 RT_BIT_64(11) /**< Device can receive TSOv4 */
122#define VIRTIONET_F_HOST_TSO6 RT_BIT_64(12) /**< Device can receive TSOv6 */
123#define VIRTIONET_F_HOST_ECN RT_BIT_64(13) /**< Device can receive TSO with ECN */
124#define VIRTIONET_F_HOST_UFO RT_BIT_64(14) /**< Device can receive UFO */
125#define VIRTIONET_F_MRG_RXBUF RT_BIT_64(15) /**< Driver can merge receive buffers */
126#define VIRTIONET_F_STATUS RT_BIT_64(16) /**< Config status field is available */
127#define VIRTIONET_F_CTRL_VQ RT_BIT_64(17) /**< Control channel is available */
128#define VIRTIONET_F_CTRL_RX RT_BIT_64(18) /**< Control channel RX mode + MAC addr filtering */
129#define VIRTIONET_F_CTRL_VLAN RT_BIT_64(19) /**< Control channel VLAN filtering */
130#define VIRTIONET_F_CTRL_RX_EXTRA RT_BIT_64(20) /**< Control channel RX mode extra functions */
131#define VIRTIONET_F_GUEST_ANNOUNCE RT_BIT_64(21) /**< Driver can send gratuitous packets */
132#define VIRTIONET_F_MQ RT_BIT_64(22) /**< Support ultiqueue with auto receive steering */
133#define VIRTIONET_F_CTRL_MAC_ADDR RT_BIT_64(23) /**< Set MAC address through control channel */
134/** @} */
135
136#ifdef VIRTIONET_WITH_GSO
137# define VIRTIONET_HOST_FEATURES_GSO \
138 VIRTIONET_F_CSUM \
139 | VIRTIONET_F_HOST_TSO4 \
140 | VIRTIONET_F_HOST_TSO6 \
141 | VIRTIONET_F_HOST_UFO \
142 | VIRTIONET_F_GUEST_TSO4 \
143 | VIRTIONET_F_GUEST_TSO6 \
144 | VIRTIONET_F_GUEST_UFO \
145 | VIRTIONET_F_GUEST_CSUM /* @bugref(4796) Guest must handle partial chksums */
146#else
147# define VIRTIONET_HOST_FEATURES_GSO
148#endif
149
150#define VIRTIONET_HOST_FEATURES_OFFERED \
151 VIRTIONET_F_STATUS \
152 | VIRTIONET_F_GUEST_ANNOUNCE \
153 | VIRTIONET_F_MAC \
154 | VIRTIONET_F_CTRL_VQ \
155 | VIRTIONET_F_CTRL_RX \
156 | VIRTIONET_F_CTRL_VLAN \
157 | VIRTIONET_HOST_FEATURES_GSO \
158 | VIRTIONET_F_MRG_RXBUF \
159 | VIRTIO_F_EVENT_IDX /** @todo Trying this experimentally as potential workaround for bug
160 * where virtio seems to expect interrupt for Rx/Used even though
161 * its set the used ring flag in the Rx queue to skip the notification by device */
162
163#define PCI_DEVICE_ID_VIRTIONET_HOST 0x1041 /**< Informs guest driver of type of VirtIO device */
164#define PCI_CLASS_BASE_NETWORK_CONTROLLER 0x02 /**< PCI Network device class */
165#define PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER 0x00 /**< PCI NET Controller subclass */
166#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
167#define VIRTIONET_PCI_CLASS 0x01 /**< Base class Mass Storage? */
168
169
170/*********************************************************************************************************************************
171* Structures and Typedefs *
172*********************************************************************************************************************************/
173/**
174 * Virtio Net Host Device device-specific configuration (VirtIO 1.0, 5.1.4)
175 * VBox VirtIO core issues callback to this VirtIO device-specific implementation to handle
176 * MMIO accesses to device-specific configuration parameters.
177 */
178
179#pragma pack(1)
180typedef struct virtio_net_config
181{
182 RTMAC uMacAddress; /**< mac */
183#if FEATURE_OFFERED(STATUS)
184 uint16_t uStatus; /**< status */
185#endif
186#if FEATURE_OFFERED(MQ)
187 uint16_t uMaxVirtqPairs; /**< max_virtq_pairs */
188#endif
189} VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
190#pragma pack()
191
192#define VIRTIONET_F_LINK_UP 1 /**< config status: Link is up */
193#define VIRTIONET_F_ANNOUNCE 2 /**< config status: Announce */
194
195/** @name VirtIO 1.0 NET Host Device device specific control types
196 * @{ */
197#define VIRTIONET_HDR_F_NEEDS_CSUM 1 /**< flags: Packet needs checksum */
198#define VIRTIONET_HDR_GSO_NONE 0 /**< gso_type: No Global Segmentation Offset */
199#define VIRTIONET_HDR_GSO_TCPV4 1 /**< gso_type: Global Segment Offset for TCPV4 */
200#define VIRTIONET_HDR_GSO_UDP 3 /**< gso_type: Global Segment Offset for UDP */
201#define VIRTIONET_HDR_GSO_TCPV6 4 /**< gso_type: Global Segment Offset for TCPV6 */
202#define VIRTIONET_HDR_GSO_ECN 0x80 /**< gso_type: Explicit Congestion Notification */
203/** @} */
204
205/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
206#pragma pack(1)
207struct virtio_net_pkt_hdr {
208 uint8_t uFlags; /**< flags */
209 uint8_t uGsoType; /**< gso_type */
210 uint16_t uHdrLen; /**< hdr_len */
211 uint16_t uGsoSize; /**< gso_size */
212 uint16_t uChksumStart; /**< Chksum_start */
213 uint16_t uChksumOffset; /**< Chksum_offset */
214 uint16_t uNumBuffers; /**< num_buffers */
215};
216#pragma pack()
217typedef virtio_net_pkt_hdr VIRTIONET_PKT_HDR_T, *PVIRTIONET_PKT_HDR_T;
218AssertCompileSize(VIRTIONET_PKT_HDR_T, 12);
219
220/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
221#pragma pack(1)
222struct virtio_net_ctrl_hdr {
223 uint8_t uClass; /**< class */
224 uint8_t uCmd; /**< command */
225};
226#pragma pack()
227typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
228
229typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
230
231/* Command entry fAck values */
232#define VIRTIONET_OK 0
233#define VIRTIONET_ERROR 1
234
235/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
236 * @{ */
237#define VIRTIONET_CTRL_RX 0 /**< Control class: Receive filtering */
238#define VIRTIONET_CTRL_RX_PROMISC 0 /**< Promiscuous mode */
239#define VIRTIONET_CTRL_RX_ALLMULTI 1 /**< All-multicast receive */
240#define VIRTIONET_CTRL_RX_ALLUNI 2 /**< All-unicast receive */
241#define VIRTIONET_CTRL_RX_NOMULTI 3 /**< No multicast receive */
242#define VIRTIONET_CTRL_RX_NOUNI 4 /**< No unicast receive */
243#define VIRTIONET_CTRL_RX_NOBCAST 5 /**< No broadcast receive */
244/** @} */
245
246typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
247typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
248typedef uint8_t VIRTIONET_CTRL_MAC_ENTRIES[][6];
249
250/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
251 * @{ */
252#define VIRTIONET_CTRL_MAC 1 /**< Control class: MAC address filtering */
253#define VIRTIONET_CTRL_MAC_TABLE_SET 0 /**< Set MAC table */
254#define VIRTIONET_CTRL_MAC_ADDR_SET 1 /**< Set default MAC address */
255/** @} */
256
257/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
258 * @{ */
259#define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */
260#define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */
261#define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */
262/** @} */
263
264/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
265 * @{ */
266#define VIRTIONET_CTRL_ANNOUNCE 3 /**< Control class: Gratuitous Packet Sending */
267#define VIRTIONET_CTRL_ANNOUNCE_ACK 0 /**< Gratuitous Packet Sending ACK */
268/** @} */
269
270struct virtio_net_ctrl_mq {
271 uint16_t uVirtqueuePairs; /**< virtqueue_pairs */
272};
273
274/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
275 * @{ */
276#define VIRTIONET_CTRL_MQ 4 /**< Control class: Receive steering */
277#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET 0 /**< Set number of TX/RX queues */
278#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN 1 /**< Minimum number of TX/RX queues */
279#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 /**< Maximum number of TX/RX queues */
280/** @} */
281
282uint64_t uOffloads; /**< offloads */
283
284/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
285 * @{ */
286#define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 /**< Control class: Offloads state configuration */
287#define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 /** Apply new offloads configuration */
288/** @} */
289
290/**
291 * Worker thread context, shared state.
292 */
293typedef struct VIRTIONETWORKER
294{
295 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
296} VIRTIONETWORKER;
297/** Pointer to a VirtIO SCSI worker. */
298typedef VIRTIONETWORKER *PVIRTIONETWORKER;
299
300/**
301 * Worker thread context, ring-3 state.
302 */
303typedef struct VIRTIONETWORKERR3
304{
305 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
306 bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
307 bool volatile fNotified; /**< Flags whether worker thread notified */
308 uint16_t idxQueue; /**< Index of associated queue */
309} VIRTIONETWORKERR3;
310/** Pointer to a VirtIO SCSI worker. */
311typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
312
313/**
314 * VirtIO Host NET device state, shared edition.
315 *
316 * @extends VIRTIOCORE
317 */
318typedef struct VIRTIONET
319{
320 /** The core virtio state. */
321 VIRTIOCORE Virtio;
322
323 /** Virtio device-specific configuration */
324 VIRTIONET_CONFIG_T virtioNetConfig;
325
326 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
327 VIRTIONETWORKER aWorkers[VIRTIONET_MAX_QUEUES];
328
329 /** Track which VirtIO queues we've attached to */
330 bool afQueueAttached[VIRTIONET_MAX_QUEUES];
331
332 /** Device-specific spec-based VirtIO VIRTQNAMEs */
333 char aszVirtqNames[VIRTIONET_MAX_QUEUES][VIRTIO_MAX_QUEUE_NAME_SIZE];
334
335 /** Instance name */
336 char szInstanceName[16];
337
338 uint16_t cVirtqPairs;
339
340 uint16_t cVirtQueues;
341
342 uint16_t cWorkers;
343
344 uint64_t fNegotiatedFeatures;
345
346 SUPSEMEVENT hTxEvent;
347
348 /** Indicates transmission in progress -- only one thread is allowed. */
349 uint32_t uIsTransmitting;
350
351 /** MAC address obtained from the configuration. */
352 RTMAC macConfigured;
353
354 /** Default MAC address which rx filtering accepts */
355 RTMAC rxFilterMacDefault;
356
357 /** True if physical cable is attached in configuration. */
358 bool fCableConnected;
359
360 /** virtio-net-1-dot-0 (in milliseconds). */
361 uint32_t cMsLinkUpDelay;
362
363 uint32_t alignment;
364
365 /** Number of packet being sent/received to show in debug log. */
366 uint32_t uPktNo;
367
368 /** N/A: */
369 bool volatile fLeafWantsRxBuffers;
370
371 SUPSEMEVENT hEventRxDescAvail;
372
373 /** Flags whether VirtIO core is in ready state */
374 uint8_t fVirtioReady;
375
376 /** Resetting flag */
377 uint8_t fResetting;
378
379 /** Quiescing I/O activity flag */
380 uint8_t fQuiescing;
381
382
383 /** Promiscuous mode -- RX filter accepts all packets. */
384 uint8_t fPromiscuous;
385
386 /** All multicast mode -- RX filter accepts all multicast packets. */
387 uint8_t fAllMulticast;
388
389 /** All unicast mode -- RX filter accepts all unicast packets. */
390 uint8_t fAllUnicast;
391
392 /** No multicast mode - Supresses multicast receive */
393 uint8_t fNoMulticast;
394
395 /** No unicast mode - Suppresses unicast receive */
396 uint8_t fNoUnicast;
397
398 /** No broadcast mode - Supresses broadcast receive */
399 uint8_t fNoBroadcast;
400
401 /** The number of actually used slots in aMacMulticastFilter. */
402 uint32_t cMulticastFilterMacs;
403
404 /** Array of MAC multicast addresses accepted by RX filter. */
405 RTMAC aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
406
407 /** The number of actually used slots in aMacUniicastFilter. */
408 uint32_t cUnicastFilterMacs;
409
410 /** Array of MAC unicast addresses accepted by RX filter. */
411 RTMAC aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
412
413 /** Bit array of VLAN filter, one bit per VLAN ID. */
414 uint8_t aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
415
416 /** @name Statistic
417 * @{ */
418 STAMCOUNTER StatReceiveBytes;
419 STAMCOUNTER StatTransmitBytes;
420 STAMCOUNTER StatReceiveGSO;
421 STAMCOUNTER StatTransmitPackets;
422 STAMCOUNTER StatTransmitGSO;
423 STAMCOUNTER StatTransmitCSum;
424#ifdef VBOX_WITH_STATISTICS
425 STAMPROFILE StatReceive;
426 STAMPROFILE StatReceiveStore;
427 STAMPROFILEADV StatTransmit;
428 STAMPROFILE StatTransmitSend;
429 STAMPROFILE StatRxOverflow;
430 STAMCOUNTER StatRxOverflowWakeup;
431 STAMCOUNTER StatTransmitByNetwork;
432 STAMCOUNTER StatTransmitByThread;
433 /** @} */
434#endif
435} VIRTIONET;
436/** Pointer to the shared state of the VirtIO Host NET device. */
437typedef VIRTIONET *PVIRTIONET;
438
439/**
440 * VirtIO Host NET device state, ring-3 edition.
441 *
442 * @extends VIRTIOCORER3
443 */
444typedef struct VIRTIONETR3
445{
446 /** The core virtio ring-3 state. */
447 VIRTIOCORER3 Virtio;
448
449 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
450 VIRTIONETWORKERR3 aWorkers[VIRTIONET_MAX_QUEUES];
451
452 /** The device instance.
453 * @note This is _only_ for use when dealing with interface callbacks. */
454 PPDMDEVINSR3 pDevIns;
455
456 /** Status LUN: Base interface. */
457 PDMIBASE IBase;
458
459 /** Status LUN: LED port interface. */
460 PDMILEDPORTS ILeds;
461
462 /** Status LUN: LED connector (peer). */
463 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
464
465 /** Status: LED */
466 PDMLED led;
467
468 /** Attached network driver. */
469 R3PTRTYPE(PPDMIBASE) pDrvBase;
470
471 /** Network port interface (down) */
472 PDMINETWORKDOWN INetworkDown;
473
474 /** Network config port interface (main). */
475 PDMINETWORKCONFIG INetworkConfig;
476
477 /** Connector of attached network driver. */
478 R3PTRTYPE(PPDMINETWORKUP) pDrv;
479
480 R3PTRTYPE(PPDMTHREAD) pTxThread;
481
482 /** Link Up(/Restore) Timer. */
483 TMTIMERHANDLE hLinkUpTimer;
484
485 /** Queue to send tasks to R3. - HC ptr */
486 R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
487
488 /** True if in the process of quiescing I/O */
489 uint32_t fQuiescing;
490
491 /** For which purpose we're quiescing. */
492 VIRTIOVMSTATECHANGED enmQuiescingFor;
493
494} VIRTIONETR3;
495
496
497/** Pointer to the ring-3 state of the VirtIO Host NET device. */
498typedef VIRTIONETR3 *PVIRTIONETR3;
499
500/**
501 * VirtIO Host NET device state, ring-0 edition.
502 */
503typedef struct VIRTIONETR0
504{
505 /** The core virtio ring-0 state. */
506 VIRTIOCORER0 Virtio;
507} VIRTIONETR0;
508/** Pointer to the ring-0 state of the VirtIO Host NET device. */
509typedef VIRTIONETR0 *PVIRTIONETR0;
510
511
512/**
513 * VirtIO Host NET device state, raw-mode edition.
514 */
515typedef struct VIRTIONETRC
516{
517 /** The core virtio raw-mode state. */
518 VIRTIOCORERC Virtio;
519} VIRTIONETRC;
520/** Pointer to the ring-0 state of the VirtIO Host NET device. */
521typedef VIRTIONETRC *PVIRTIONETRC;
522
523
524/** @typedef VIRTIONETCC
525 * The instance data for the current context. */
526typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
527
528/** @typedef PVIRTIONETCC
529 * Pointer to the instance data for the current context. */
530typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
531
532#ifdef IN_RING3 /* spans most of the file, at the moment. */
533
534/**
535 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
536 */
537static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
538{
539 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
540 Log10Func(("%s\n", INSTANCE(pThis)));
541 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[(uintptr_t)pThread->pvUser].hEvtProcess);
542}
543
544/**
545 * Wakeup the RX thread.
546 */
547static void virtioNetR3WakeupRxBufWaiter(PPDMDEVINS pDevIns)
548{
549 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
550
551 AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
552
553 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
554
555 Log10Func(("%s Waking downstream driver's Rx buf waiter thread\n", INSTANCE(pThis)));
556 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
557 AssertRC(rc);
558}
559
560DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis)
561{
562 for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
563 {
564 RTStrPrintf(pThis->aszVirtqNames[RXQIDX(qPairIdx)], VIRTIO_MAX_QUEUE_NAME_SIZE, "receiveq<%d>", qPairIdx);
565 RTStrPrintf(pThis->aszVirtqNames[TXQIDX(qPairIdx)], VIRTIO_MAX_QUEUE_NAME_SIZE, "transmitq<%d>", qPairIdx);
566 }
567 RTStrCopy(pThis->aszVirtqNames[CTRLQIDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "controlq");
568}
569
570/**
571 * Dump a packet to debug log.
572 *
573 * @param pThis The virtio-net shared instance data.
574 * @param pbPacket The packet.
575 * @param cb The size of the packet.
576 * @param pszText A string denoting direction of packet transfer.
577 */
578DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
579{
580// if (!LogIs12Enabled())
581// return;
582
583 vboxEthPacketDump(INSTANCE(pThis), pszText, pbPacket, (uint32_t)cb);
584}
585
586void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONET_PKT_HDR_T pRxPktHdr,
587 uint16_t cDescs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS gcPhysRxBuf, uint8_t cbRxBuf)
588{
589#ifdef LOG_ENABLED
590 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
591 pRxPktHdr->uNumBuffers = cDescs;
592 if (pRxPktHdr)
593 {
594 LogFunc(("-------------------------------------------------------------------\n"));
595 LogFunc(("rxPktHdr\n"
596 " uFlags ......... %2.2x\n"
597 " uGsoType ....... %2.2x\n"
598 " uHdrLen ........ %4.4x\n"
599 " uGsoSize ....... %4.4x\n"
600 " uChksumStart ... %4.4x\n"
601 " uChksumOffset .. %4.4x\n"
602 " uNumBuffers .... %4.4x\n",
603 pRxPktHdr->uFlags,
604 pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
605 pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset, pRxPktHdr->uNumBuffers));
606
607 virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONET_PKT_HDR_T), 0, "Dump of virtual rPktHdr");
608 }
609 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
610 LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
611 virtioCoreGcPhysHexDump(pDevIns, gcPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
612 LogFunc(("-------------------------------------------------------------------\n"));
613#else
614 RT_NOREF7(pDevIns, pRxPktHdr, cDescs, pvBuf, cb, gcPhysRxBuf, cbRxBuf);
615#endif
616}
617
618
619DECLINLINE(void) virtioNetPrintFeatures(VIRTIONET *pThis)
620{
621#ifdef LOG_ENABLED
622 static struct
623 {
624 uint64_t fFeatureBit;
625 const char *pcszDesc;
626 } const s_aFeatures[] =
627 {
628 { VIRTIONET_F_CSUM, " CSUM Host handles packets with partial checksum.\n" },
629 { VIRTIONET_F_GUEST_CSUM, " GUEST_CSUM Guest handles packets with partial checksum.\n" },
630 { VIRTIONET_F_CTRL_GUEST_OFFLOADS, " CTRL_GUEST_OFFLOADS Control channel offloads reconfiguration support.\n" },
631 { VIRTIONET_F_MAC, " MAC Host has given MAC address.\n" },
632 { VIRTIONET_F_GUEST_TSO4, " GUEST_TSO4 Guest can receive TSOv4.\n" },
633 { VIRTIONET_F_GUEST_TSO6, " GUEST_TSO6 Guest can receive TSOv6.\n" },
634 { VIRTIONET_F_GUEST_ECN, " GUEST_ECN Guest can receive TSO with ECN.\n" },
635 { VIRTIONET_F_GUEST_UFO, " GUEST_UFO Guest can receive UFO.\n" },
636 { VIRTIONET_F_HOST_TSO4, " HOST_TSO4 Host can receive TSOv4.\n" },
637 { VIRTIONET_F_HOST_TSO6, " HOST_TSO6 Host can receive TSOv6.\n" },
638 { VIRTIONET_F_HOST_ECN, " HOST_ECN Host can receive TSO with ECN.\n" },
639 { VIRTIONET_F_HOST_UFO, " HOST_UFO Host can receive UFO.\n" },
640 { VIRTIONET_F_MRG_RXBUF, " MRG_RXBUF Guest can merge receive buffers.\n" },
641 { VIRTIONET_F_STATUS, " STATUS Configuration status field is available.\n" },
642 { VIRTIONET_F_CTRL_VQ, " CTRL_VQ Control channel is available.\n" },
643 { VIRTIONET_F_CTRL_RX, " CTRL_RX Control channel RX mode support.\n" },
644 { VIRTIONET_F_CTRL_VLAN, " CTRL_VLAN Control channel VLAN filtering.\n" },
645 { VIRTIONET_F_GUEST_ANNOUNCE, " GUEST_ANNOUNCE Guest can send gratuitous packets.\n" },
646 { VIRTIONET_F_MQ, " MQ Host supports multiqueue with automatic receive steering.\n" },
647 { VIRTIONET_F_CTRL_MAC_ADDR, " CTRL_MAC_ADDR Set MAC address through control channel.\n" }
648 };
649
650#define MAXLINE 80
651 /* Display as a single buf to prevent interceding log messages */
652 uint64_t fFeaturesOfferedMask = VIRTIONET_HOST_FEATURES_OFFERED;
653 uint16_t cbBuf = RT_ELEMENTS(s_aFeatures) * 132;
654 char *pszBuf = (char *)RTMemAllocZ(cbBuf);
655 Assert(pszBuf);
656 char *cp = pszBuf;
657 for (unsigned i = 0; i < RT_ELEMENTS(s_aFeatures); ++i)
658 {
659 bool isOffered = fFeaturesOfferedMask & s_aFeatures[i].fFeatureBit;
660 bool isNegotiated = pThis->fNegotiatedFeatures & s_aFeatures[i].fFeatureBit;
661 cp += RTStrPrintf(cp, cbBuf - (cp - pszBuf), " %s %s %s",
662 isOffered ? "+" : "-", isNegotiated ? "x" : " ", s_aFeatures[i].pcszDesc);
663 }
664 Log3(("VirtIO Net Features Configuration\n\n"
665 " Offered Accepted Feature Description\n"
666 " ------- -------- ------- -----------\n"
667 "%s\n", pszBuf));
668 RTMemFree(pszBuf);
669
670#else /* !LOG_ENABLED */
671 RT_NOREF(pThis);
672#endif /* !LOG_ENABLED */
673}
674
675/*
676 * Checks whether negotiated features have required flag combinations.
677 * See VirtIO 1.0 specification, Section 5.1.3.1 */
678DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
679{
680 uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
681 || fFeatures & VIRTIONET_F_GUEST_TSO6
682 || fFeatures & VIRTIONET_F_GUEST_UFO;
683
684 uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
685 || fFeatures & VIRTIONET_F_HOST_TSO6
686 || fFeatures & VIRTIONET_F_HOST_UFO;
687
688 uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
689 || fFeatures & VIRTIONET_F_CTRL_VLAN
690 || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
691 || fFeatures & VIRTIONET_F_MQ
692 || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
693
694 if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
695 return false;
696
697 if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
698 return false;
699
700 if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
701 return false;
702
703 if ( fFeatures & VIRTIONET_F_GUEST_ECN
704 && !( fFeatures & VIRTIONET_F_GUEST_TSO4
705 || fFeatures & VIRTIONET_F_GUEST_TSO6))
706 return false;
707
708 if ( fFeatures & VIRTIONET_F_HOST_ECN
709 && !( fFeatures & VIRTIONET_F_HOST_TSO4
710 || fFeatures & VIRTIONET_F_HOST_TSO6))
711 return false;
712 return true;
713}
714
715/**
716 * Resolves to boolean true if uOffset matches a field offset and size exactly,
717 * (or if 64-bit field, if it accesses either 32-bit part as a 32-bit access)
718 * Assumption is this critereon is mandated by VirtIO 1.0, Section 4.1.3.1)
719 * (Easily re-written to allow unaligned bounded access to a field).
720 *
721 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
722 * @result - true or false
723 */
724#define MATCH_NET_CONFIG(member) \
725 ( ( RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member) == 8 \
726 && ( offConfig == RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) \
727 || offConfig == RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) + sizeof(uint32_t)) \
728 && cb == sizeof(uint32_t)) \
729 || ( offConfig >= RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) \
730 && offConfig + cb <= RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) \
731 + RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member)) )
732
733#ifdef LOG_ENABLED
734# define LOG_NET_CONFIG_ACCESSOR(member) \
735 virtioCoreLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member), \
736 pv, cb, offIntra, fWrite, false, 0);
737#else
738# define LOG_NET_CONFIG_ACCESSOR(member) do { } while (0)
739#endif
740
741#define NET_CONFIG_ACCESSOR(member) \
742 do \
743 { \
744 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIONET_CONFIG_T, member); \
745 if (fWrite) \
746 memcpy((char *)&pThis->virtioNetConfig.member + offIntra, pv, cb); \
747 else \
748 memcpy(pv, (const char *)&pThis->virtioNetConfig.member + offIntra, cb); \
749 LOG_NET_CONFIG_ACCESSOR(member); \
750 } while(0)
751
752#define NET_CONFIG_ACCESSOR_READONLY(member) \
753 do \
754 { \
755 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIONET_CONFIG_T, member); \
756 if (fWrite) \
757 LogFunc(("%s Guest attempted to write readonly virtio_pci_common_cfg.%s\n", INSTANCE(pThis), #member)); \
758 else \
759 { \
760 memcpy(pv, (const char *)&pThis->virtioNetConfig.member + offIntra, cb); \
761 LOG_NET_CONFIG_ACCESSOR(member); \
762 } \
763 } while(0)
764
765
766static int virtioNetR3CfgAccessed(PVIRTIONET pThis, uint32_t offConfig, void *pv, uint32_t cb, bool fWrite)
767{
768 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
769
770 if (MATCH_NET_CONFIG(uMacAddress))
771 NET_CONFIG_ACCESSOR_READONLY(uMacAddress);
772#if FEATURE_OFFERED(STATUS)
773 else
774 if (MATCH_NET_CONFIG(uStatus))
775 NET_CONFIG_ACCESSOR_READONLY(uStatus);
776#endif
777#if FEATURE_OFFERED(MQ)
778 else
779 if (MATCH_NET_CONFIG(uMaxVirtqPairs))
780 NET_CONFIG_ACCESSOR_READONLY(uMaxVirtqPairs);
781#endif
782 else
783 {
784 LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n", INSTANCE(pThis), offConfig, offConfig, cb));
785 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
786 }
787 return VINF_SUCCESS;
788}
789
790#undef NET_CONFIG_ACCESSOR_READONLY
791#undef NET_CONFIG_ACCESSOR
792#undef LOG_ACCESSOR
793#undef MATCH_NET_CONFIG
794
795/**
796 * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
797 */
798static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
799{
800 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
801
802 LogFunc(("%s uOffset: %d, cb: %d\n", INSTANCE(pThis), uOffset, cb));
803 RT_NOREF(pThis);
804 return virtioNetR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
805}
806
807/**
808 * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
809 */
810static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
811{
812 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
813
814 Log10Func(("%s uOffset: %d, cb: %d: %.*Rhxs\n", INSTANCE(pThis), uOffset, cb, RT_MAX(cb, 8) , pv));
815 RT_NOREF(pThis);
816 return virtioNetR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
817}
818
819
820/*********************************************************************************************************************************
821* Misc *
822*********************************************************************************************************************************/
823
824/**
825 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
826 */
827static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
828{
829 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
830
831 /* Parse arguments. */
832 RT_NOREF2(pThis, pszArgs); //bool fVerbose = pszArgs && strstr(pszArgs, "verbose") != NULL;
833
834 /* Show basic information. */
835 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
836 pDevIns->pReg->szName,
837 pDevIns->iInstance);
838}
839
840
841/*********************************************************************************************************************************
842* Saved state *
843*********************************************************************************************************************************/
844
845/**
846 * @callback_method_impl{FNSSMDEVLOADEXEC}
847 */
848static DECLCALLBACK(int) virtioNetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
849{
850 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
851 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
852 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
853
854 RT_NOREF(pThisCC);
855 Log7Func(("%s LOAD EXEC!!\n", INSTANCE(pThis)));
856
857 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
858 AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVED_STATE_VERSION,
859 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
860
861 virtioNetR3SetVirtqNames(pThis);
862
863 pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
864
865 pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtQueues);
866 pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
867
868 for (int idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
869 pHlp->pfnSSMGetBool(pSSM, &pThis->afQueueAttached[idxQueue]);
870
871 int rc;
872
873 if (uPass == SSM_PASS_FINAL)
874 {
875
876 /* Load config area */
877#if FEATURE_OFFERED(STATUS)
878 /* config checks */
879 RTMAC macConfigured;
880 rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
881 AssertRCReturn(rc, rc);
882 if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
883 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
884 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
885 INSTANCE(pThis), &pThis->macConfigured, &macConfigured));
886#endif
887#if FEATURE_OFFERED(MQ)
888 pHlp->pfnSSMGetU16( pSSM, &pThis->virtioNetConfig.uMaxVirtqPairs);
889#endif
890 /* Save device-specific part */
891 pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
892 pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
893 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
894 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
895 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
896 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
897 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
898
899 pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
900 pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
901
902 if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
903 memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
904 (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
905
906 pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
907 pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
908
909 if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
910 memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
911 (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
912
913 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
914 AssertRCReturn(rc, rc);
915 }
916
917 /*
918 * Call the virtio core to let it load its state.
919 */
920 rc = virtioCoreR3LoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
921
922 /*
923 * Nudge queue workers
924 */
925 for (int idxWorker = 0; idxWorker < pThis->cWorkers; idxWorker++)
926 {
927 uint16_t idxQueue = pThisCC->aWorkers[idxWorker].idxQueue;
928 if (pThis->afQueueAttached[idxQueue])
929 {
930 Log7Func(("%s Waking %s worker.\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
931 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[idxWorker].hEvtProcess);
932 AssertRCReturn(rc, rc);
933 }
934 }
935 return rc;
936}
937
938/**
939 * @callback_method_impl{FNSSMDEVSAVEEXEC}
940 */
941static DECLCALLBACK(int) virtioNetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
942{
943 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
944 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
945 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
946
947 RT_NOREF(pThisCC);
948 Log7Func(("%s SAVE EXEC!!\n", INSTANCE(pThis)));
949
950 pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
951
952 pHlp->pfnSSMPutU16( pSSM, pThis->cVirtQueues);
953 pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
954
955 for (int idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
956 pHlp->pfnSSMPutBool(pSSM, pThis->afQueueAttached[idxQueue]);
957
958 /* Save config area */
959#if FEATURE_OFFERED(STATUS)
960 pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
961 sizeof(pThis->virtioNetConfig.uMacAddress.au8));
962#endif
963#if FEATURE_OFFERED(MQ)
964 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
965#endif
966
967 /* Save device-specific part */
968 pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
969 pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
970 pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
971 pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
972 pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
973 pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
974 pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
975
976 pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
977 pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
978
979 pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
980 pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
981
982 int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
983 AssertRCReturn(rc, rc);
984
985 /*
986 * Call the virtio core to let it save its state.
987 */
988 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
989}
990
991
992/*********************************************************************************************************************************
993* Device interface. *
994*********************************************************************************************************************************/
995/*xx*/
996/**
997 * @callback_method_impl{FNPDMDEVASYNCNOTIFY}
998 */
999static DECLCALLBACK(bool) virtioNetR3DeviceQuiesced(PPDMDEVINS pDevIns)
1000{
1001 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1002 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1003
1004 /** @todo create test to conclusively determine I/O has been quiesced and add it here: */
1005
1006 Log7Func(("%s Device I/O activity quiesced: %s\n",
1007 INSTANCE(pThis), virtioCoreGetStateChangeText(pThisCC->enmQuiescingFor)));
1008
1009 virtioCoreR3VmStateChanged(&pThis->Virtio, pThisCC->enmQuiescingFor);
1010
1011 pThis->fResetting = false;
1012 pThisCC->fQuiescing = false;
1013
1014 return true;
1015}
1016
1017/**
1018 * Worker for virtioNetR3Reset() and virtioNetR3SuspendOrPowerOff().
1019 */
1020static void virtioNetR3QuiesceDevice(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmQuiescingFor)
1021{
1022 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1023 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1024
1025 RT_NOREF(pThis);
1026
1027 /* Prevent worker threads from removing/processing elements from virtq's */
1028 pThisCC->fQuiescing = true;
1029 pThisCC->enmQuiescingFor = enmQuiescingFor;
1030
1031 /*
1032 * Wake downstream network driver thread that's waiting for Rx buffers to be available
1033 * to tell it that's going to happen...
1034 */
1035 virtioNetR3WakeupRxBufWaiter(pDevIns);
1036
1037 PDMDevHlpSetAsyncNotification(pDevIns, virtioNetR3DeviceQuiesced);
1038
1039 /* If already quiesced invoke async callback. */
1040 if (!ASMAtomicReadBool(&pThis->fLeafWantsRxBuffers))
1041 PDMDevHlpAsyncNotificationCompleted(pDevIns);
1042
1043 /** @todo make sure Rx and Tx are really quiesced (how do we synchronize w/downstream driver?) */
1044}
1045
1046/**
1047 * @interface_method_impl{PDMDEVREGR3,pfnReset}
1048 */
1049static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
1050{
1051 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1052 Log7Func(("%s\n", INSTANCE(pThis)));
1053 pThis->fResetting = true;
1054 virtioNetR3QuiesceDevice(pDevIns, kvirtIoVmStateChangedReset);
1055}
1056
1057/**
1058 * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
1059 */
1060static DECLCALLBACK(void) virtioNetR3SuspendOrPowerOff(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmType)
1061{
1062
1063 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1064 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1065 Log7Func(("%s\n", INSTANCE(pThis)));
1066
1067 RT_NOREF2(pThis, pThisCC);
1068
1069 virtioNetR3QuiesceDevice(pDevIns, enmType);
1070 virtioNetR3WakeupRxBufWaiter(pDevIns);
1071}
1072
1073/**
1074 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
1075 */
1076static DECLCALLBACK(void) virtioNetR3PowerOff(PPDMDEVINS pDevIns)
1077{
1078 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1079 Log7Func(("%s\n", INSTANCE(pThis)));
1080 RT_NOREF(pThis);
1081 virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedPowerOff);
1082}
1083
1084/**
1085 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
1086 */
1087static DECLCALLBACK(void) virtioNetR3Suspend(PPDMDEVINS pDevIns)
1088{
1089 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1090 Log7Func(("%s \n", INSTANCE(pThis)));
1091 RT_NOREF(pThis);
1092 virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedSuspend);
1093}
1094
1095/**
1096 * @interface_method_impl{PDMDEVREGR3,pfnResume}
1097 *
1098 * Just process the VM device-related state change itself.
1099 * Unlike SCSI driver, there are no packets to redo. No I/O was halted or saved while
1100 * quiescing for pfnSuspend(). Any packets in process were simply dropped by the upper
1101 * layer driver, presumably to be retried or cause erring out at the upper layers
1102 * of the network stack.
1103 */
1104static DECLCALLBACK(void) virtioNetR3Resume(PPDMDEVINS pDevIns)
1105{
1106 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1107 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1108 Log7Func(("\n"));
1109
1110 pThisCC->fQuiescing = false;
1111
1112 /* Ensure guest is working the queues */
1113 virtioCoreR3VmStateChanged(&pThis->Virtio, kvirtIoVmStateChangedResume);
1114}
1115
1116#ifdef IN_RING3
1117
1118DECLINLINE(uint16_t) virtioNetR3Checkum16(const void *pvBuf, size_t cb)
1119{
1120 uint32_t chksum = 0;
1121 uint16_t *pu = (uint16_t *)pvBuf;
1122
1123 while (cb > 1)
1124 {
1125 chksum += *pu++;
1126 cb -= 2;
1127 }
1128 if (cb)
1129 chksum += *(uint8_t*)pu;
1130 while (chksum >> 16)
1131 chksum = (chksum >> 16) + (chksum & 0xFFFF);
1132 return ~chksum;
1133}
1134
1135DECLINLINE(void) virtioNetR3CompleteChecksum(uint8_t *pBuf, size_t cbSize, uint16_t uStart, uint16_t uOffset)
1136{
1137 AssertReturnVoid(uStart < cbSize);
1138 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cbSize);
1139 *(uint16_t *)(pBuf + uStart + uOffset) = virtioNetR3Checkum16(pBuf + uStart, cbSize - uStart);
1140}
1141
1142/**
1143 * Turns on/off the read status LED.
1144 *
1145 * @returns VBox status code.
1146 * @param pThis Pointer to the device state structure.
1147 * @param fOn New LED state.
1148 */
1149void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
1150{
1151 Log10Func(("%s\n", fOn ? "on" : "off"));
1152 if (fOn)
1153 pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
1154 else
1155 pThisR3->led.Actual.s.fReading = fOn;
1156}
1157
1158/**
1159 * Turns on/off the write status LED.
1160 *
1161 * @returns VBox status code.
1162 * @param pThis Pointer to the device state structure.
1163 * @param fOn New LED state.
1164 */
1165void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
1166{
1167 Log10Func(("%s\n", fOn ? "on" : "off"));
1168 if (fOn)
1169 pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
1170 else
1171 pThisR3->led.Actual.s.fWriting = fOn;
1172}
1173
1174/**
1175 * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
1176 * available. This must be called before the pfnRecieve() method is called.
1177 *
1178 * @remarks As a side effect this function enables queue notification
1179 * if it cannot receive because the queue is empty.
1180 * It disables notification if it can receive.
1181 *
1182 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1183 * @thread RX
1184 */
1185static int virtioNetR3IsRxQueuePrimed(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint16_t idxRxQueue)
1186{
1187#define LOGPARAMS INSTANCE(pThis), VIRTQNAME(idxRxQueue)
1188
1189 if (!pThis->fVirtioReady)
1190 {
1191 Log8Func(("%s %s VirtIO not ready (rc = VERR_NET_NO_BUFFER_SPACE)\n", LOGPARAMS));
1192 }
1193 else if (!virtioCoreIsQueueEnabled(&pThis->Virtio, idxRxQueue))
1194 {
1195 Log8Func(("%s %s queue not enabled (rc = VERR_NET_NO_BUFFER_SPACE)\n", LOGPARAMS));
1196 }
1197 else if (virtioCoreQueueIsEmpty(pDevIns, &pThis->Virtio, idxRxQueue))
1198 {
1199 Log8Func(("%s %s queue is empty (rc = VERR_NET_NO_BUFFER_SPACE)\n", LOGPARAMS));
1200 virtioCoreQueueSetNotify(&pThis->Virtio, idxRxQueue, true);
1201 }
1202 else
1203 {
1204 Log8Func(("%s %s ready with available buffers\n", LOGPARAMS));
1205 virtioCoreQueueSetNotify(&pThis->Virtio, idxRxQueue, false);
1206 return VINF_SUCCESS;
1207 }
1208 return VERR_NET_NO_BUFFER_SPACE;
1209}
1210
1211
1212static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis)
1213{
1214 /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
1215 selection algorithm feasible or even necessary to prevent starvation? */
1216 for (int idxQueuePair = 0; idxQueuePair < pThis->cVirtqPairs; idxQueuePair++)
1217 if (RT_SUCCESS(virtioNetR3IsRxQueuePrimed(pDevIns, pThis, RXQIDX(idxQueuePair))))
1218 return true;
1219 return false;
1220}
1221/*
1222 * Returns true if VirtIO core and device are in a running and operational state
1223 */
1224DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
1225{
1226 if (!pThis->fVirtioReady)
1227 return false;
1228
1229 if (pThis->fQuiescing)
1230 return false;
1231
1232 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1233 if (!RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
1234 return false;
1235
1236 return true;
1237}
1238
1239/**
1240 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
1241 */
1242static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
1243{
1244 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1245 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1246 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1247
1248 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis))
1249 {
1250 Log10Func(("%s Rx bufs now available, releasing waiter...\n", INSTANCE(pThis)));
1251 return VINF_SUCCESS;
1252 }
1253 if (!timeoutMs)
1254 return VERR_NET_NO_BUFFER_SPACE;
1255
1256 LogFlowFunc(("%s timeoutMs=%u\n", INSTANCE(pThis), timeoutMs));
1257
1258 ASMAtomicXchgBool(&pThis->fLeafWantsRxBuffers, true);
1259 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
1260
1261 do {
1262 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis))
1263 {
1264 Log10Func(("%s Rx bufs now available, releasing waiter...\n", INSTANCE(pThis)));
1265 return VINF_SUCCESS;
1266 }
1267 Log9Func(("%s Starved for guest Rx bufs, waiting %u ms ...\n",
1268 INSTANCE(pThis), timeoutMs));
1269
1270 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
1271
1272 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
1273 continue;
1274
1275 if (RT_FAILURE(rc))
1276 RTThreadSleep(1);
1277
1278 } while (virtioNetIsOperational(pThis, pDevIns));
1279
1280 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
1281 ASMAtomicXchgBool(&pThis->fLeafWantsRxBuffers, false);
1282
1283 Log7Func(("%s Wait for Rx buffers available was interrupted\n", INSTANCE(pThis)));
1284 return VERR_INTERRUPTED;
1285}
1286
1287
1288/**
1289 * Sets up the GSO context according to the Virtio header.
1290 *
1291 * @param pGso The GSO context to setup.
1292 * @param pCtx The context descriptor.
1293 */
1294DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONET_PKT_HDR_T const *pPktHdr)
1295{
1296 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1297
1298 if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
1299 {
1300 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
1301 return NULL;
1302 }
1303 switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
1304 {
1305 case VIRTIONET_HDR_GSO_TCPV4:
1306 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
1307 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1308 break;
1309 case VIRTIONET_HDR_GSO_TCPV6:
1310 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
1311 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1312 break;
1313 case VIRTIONET_HDR_GSO_UDP:
1314 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
1315 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
1316 break;
1317 default:
1318 return NULL;
1319 }
1320 if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
1321 pGso->offHdr2 = pPktHdr->uChksumStart;
1322 else
1323 {
1324 AssertMsgFailed(("GSO without checksum offloading!\n"));
1325 return NULL;
1326 }
1327 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1328 pGso->cbHdrsTotal = pPktHdr->uHdrLen;
1329 pGso->cbMaxSeg = pPktHdr->uGsoSize;
1330 return pGso;
1331}
1332
1333/**
1334 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
1335 */
1336static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1337{
1338 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
1339 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
1340 memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
1341 return VINF_SUCCESS;
1342}
1343
1344/**
1345 * Returns true if it is a broadcast packet.
1346 *
1347 * @returns true if destination address indicates broadcast.
1348 * @param pvBuf The ethernet packet.
1349 */
1350DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
1351{
1352 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1353 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
1354}
1355
1356/**
1357 * Returns true if it is a multicast packet.
1358 *
1359 * @remarks returns true for broadcast packets as well.
1360 * @returns true if destination address indicates multicast.
1361 * @param pvBuf The ethernet packet.
1362 */
1363DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
1364{
1365 return (*(char*)pvBuf) & 1;
1366}
1367/**
1368 * Determines if the packet is to be delivered to upper layer.
1369 *
1370 * @returns true if packet is intended for this node.
1371 * @param pThis Pointer to the state structure.
1372 * @param pvBuf The ethernet packet.
1373 * @param cb Number of bytes available in the packet.
1374 */
1375static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
1376{
1377
1378 if (LogIs11Enabled())
1379 {
1380 char *pszType;
1381 if (virtioNetR3IsMulticast(pvBuf))
1382 pszType = (char *)"Multicast";
1383 else if (virtioNetR3IsBroadcast(pvBuf))
1384 pszType = (char *)"Broadcast";
1385 else
1386 pszType = (char *)"Unicast";
1387
1388 LogFunc(("%s node(%RTmac %s%s), pkt(%RTmac %s)",
1389 INSTANCE(pThis), pThis->virtioNetConfig.uMacAddress.au8,
1390 pThis->fPromiscuous ? "promiscuous" : "",
1391 pThis->fAllMulticast ? " all-multicast" : "",
1392 pvBuf, pszType));
1393 }
1394
1395 if (pThis->fPromiscuous)
1396 return true;
1397
1398 /* Ignore everything outside of our VLANs */
1399 uint16_t *uPtr = (uint16_t *)pvBuf;
1400
1401 /* Compare TPID with VLAN Ether Type */
1402 if ( uPtr[6] == RT_H2BE_U16(0x8100)
1403 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
1404 {
1405 Log11Func(("\n%s not our VLAN, returning false\n", INSTANCE(pThis)));
1406 return false;
1407 }
1408
1409 if (virtioNetR3IsBroadcast(pvBuf))
1410 {
1411 Log11(("... accept (broadcast)\n"));
1412 if (LogIs12Enabled())
1413 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1414 return true;
1415 }
1416 if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
1417 {
1418 Log11(("... accept (all-multicast mode)\n"));
1419 if (LogIs12Enabled())
1420 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1421 return true;
1422 }
1423
1424 if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
1425 {
1426 Log11((". . . accept (direct to this node)\n"));
1427 if (LogIs12Enabled())
1428 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1429 return true;
1430 }
1431
1432 for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
1433 {
1434 if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
1435 {
1436 Log11(("... accept (in multicast array)\n"));
1437 if (LogIs12Enabled())
1438 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1439 return true;
1440 }
1441 }
1442
1443 for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
1444 if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
1445 {
1446 Log11(("... accept (in unicast array)\n"));
1447 return true;
1448 }
1449
1450 if (LogIs12Enabled())
1451 Log(("... reject\n"));
1452
1453 return false;
1454}
1455
1456static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, const void *pvBuf, size_t cb,
1457 VIRTIONET_PKT_HDR_T *rxPktHdr, uint16_t cSegsAllocated,
1458 PRTSGBUF pVirtSegBufToGuest, PRTSGSEG paVirtSegsToGuest,
1459 uint16_t idxRxQueue)
1460{
1461 uint8_t fAddPktHdr = true;
1462 RTGCPHYS gcPhysPktHdrNumBuffers = 0;
1463 uint16_t cDescs;
1464 uint32_t uOffset;
1465 for (cDescs = uOffset = 0; uOffset < cb; )
1466 {
1467 PVIRTIO_DESC_CHAIN_T pDescChain = NULL;
1468
1469 int rc = virtioCoreR3QueueGet(pDevIns, &pThis->Virtio, RXQIDX(idxRxQueue), &pDescChain, true);
1470 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
1471
1472 /** @todo Find a better way to deal with this */
1473 AssertMsgReturnStmt(rc == VINF_SUCCESS && pDescChain->cbPhysReturn,
1474 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
1475 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain),
1476 VERR_INTERNAL_ERROR);
1477
1478 /* Length of first seg of guest Rx buf should never be less than sizeof(virtio_net_pkt_hdr).
1479 * Otherwise code has to become more complicated, e.g. locate & cache seg idx & offset of
1480 * virtio_net_header.num_buffers, to defer updating (in gcPhys). Re-visit if needed */
1481
1482 AssertMsgReturnStmt(pDescChain->pSgPhysReturn->paSegs[0].cbSeg >= sizeof(VIRTIONET_PKT_HDR_T),
1483 ("Desc chain's first seg has insufficient space for pkt header!\n"),
1484 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain),
1485 VERR_INTERNAL_ERROR);
1486
1487 uint32_t cbDescChainLeft = pDescChain->cbPhysReturn;
1488 uint8_t cbHdr = sizeof(VIRTIONET_PKT_HDR_T);
1489
1490 /* Fill the Guest Rx buffer with data received from the interface */
1491 for (uint16_t cSegs = 0; uOffset < cb && cbDescChainLeft; )
1492 {
1493 if (fAddPktHdr)
1494 {
1495 /* Lead with packet header */
1496 paVirtSegsToGuest[0].cbSeg = cbHdr;
1497 paVirtSegsToGuest[0].pvSeg = RTMemAlloc(cbHdr);
1498 AssertReturn(paVirtSegsToGuest[0].pvSeg, VERR_NO_MEMORY);
1499 cbDescChainLeft -= cbHdr;
1500
1501 memcpy(paVirtSegsToGuest[0].pvSeg, rxPktHdr, cbHdr);
1502
1503 /* Calculate & cache addr of field to update after final value is known, in gcPhys mem */
1504 gcPhysPktHdrNumBuffers = pDescChain->pSgPhysReturn->paSegs[0].gcPhys
1505 + RT_UOFFSETOF(VIRTIONET_PKT_HDR_T, uNumBuffers);
1506 fAddPktHdr = false;
1507 cSegs++;
1508 }
1509
1510 if (cSegs >= cSegsAllocated)
1511 {
1512 cSegsAllocated <<= 1; /* double allocation size */
1513 paVirtSegsToGuest = (PRTSGSEG)RTMemRealloc(paVirtSegsToGuest, sizeof(RTSGSEG) * cSegsAllocated);
1514 if (!paVirtSegsToGuest)
1515 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain);
1516 AssertReturn(paVirtSegsToGuest, VERR_NO_MEMORY);
1517 }
1518
1519 /* Append remaining Rx pkt or as much current desc chain has room for */
1520 uint32_t cbCropped = RT_MIN(cb, cbDescChainLeft);
1521 paVirtSegsToGuest[cSegs].cbSeg = cbCropped;
1522 paVirtSegsToGuest[cSegs].pvSeg = ((uint8_t *)pvBuf) + uOffset;
1523 cbDescChainLeft -= cbCropped;
1524 uOffset += cbCropped;
1525 cDescs++;
1526 cSegs++;
1527 RTSgBufInit(pVirtSegBufToGuest, paVirtSegsToGuest, cSegs);
1528 Log7Func(("Send Rx pkt to guest...\n"));
1529 STAM_PROFILE_START(&pThis->StatReceiveStore, a);
1530 virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, idxRxQueue,
1531 pVirtSegBufToGuest, pDescChain, true);
1532 STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
1533
1534 if (FEATURE_DISABLED(MRG_RXBUF))
1535 break;
1536 }
1537
1538 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain);
1539 }
1540
1541 if (uOffset < cb)
1542 {
1543 LogFunc(("%s Packet did not fit into RX queue (packet size=%u)!\n", INSTANCE(pThis), cb));
1544 return VERR_TOO_MUCH_DATA;
1545 }
1546
1547 /* Fix-up pkthdr (in guest phys. memory) with number buffers (descriptors) processed */
1548
1549 int rc = PDMDevHlpPCIPhysWrite(pDevIns, gcPhysPktHdrNumBuffers, &cDescs, sizeof(cDescs));
1550 AssertMsgRCReturn(rc,
1551 ("Failure updating descriptor count in pkt hdr in guest physical memory\n"),
1552 rc);
1553
1554 /** @todo WHY *must* we *force* notifying guest that we filled its Rx buffer(s)?
1555 * If we don't notify the guest, it doesn't detect it and stalls, even though
1556 * guest is responsible for setting the used-ring flag in the Rx queue that tells
1557 * us to skip the notification interrupt! Obviously forcing the interrupt is
1558 * non-optimal performance-wise and seems to contradict the Virtio spec.
1559 * Is that a bug in the linux virtio_net.c driver? */
1560
1561 virtioCoreQueueSync(pDevIns, &pThis->Virtio, RXQIDX(idxRxQueue), /* fForce */ true);
1562
1563 return VINF_SUCCESS;
1564}
1565
1566
1567/**
1568 * Pad and store received packet.
1569 *
1570 * @remarks Make sure that the packet appears to upper layer as one coming
1571 * from real Ethernet: pad it and insert FCS.
1572 *
1573 * @returns VBox status code.
1574 * @param pDevIns The device instance.
1575 * @param pThis The virtio-net shared instance data.
1576 * @param pvBuf The available data.
1577 * @param cb Number of bytes available in the buffer.
1578 * @param pGso Pointer to Global Segmentation Offload structure
1579 * @param idxRxQueue Rx queue to work with
1580 * @thread RX
1581 */
1582static int virtioNetR3HandleRxPacket(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1583 const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso, uint16_t idxRxQueue)
1584{
1585 RT_NOREF(pThisCC);
1586
1587 LogFunc(("%s (%RTmac) pGso %s\n", INSTANCE(pThis), pvBuf, pGso ? "present" : "not present"));
1588 VIRTIONET_PKT_HDR_T rxPktHdr = { 0 };
1589
1590 if (pGso)
1591 {
1592 Log2Func(("%s gso type=%x cbPktHdrsTotal=%u cbPktHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
1593 INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal,
1594 pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1595
1596 rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
1597 switch (pGso->u8Type)
1598 {
1599 case PDMNETWORKGSOTYPE_IPV4_TCP:
1600 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
1601 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1602 break;
1603 case PDMNETWORKGSOTYPE_IPV6_TCP:
1604 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
1605 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1606 break;
1607 case PDMNETWORKGSOTYPE_IPV4_UDP:
1608 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
1609 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
1610 break;
1611 default:
1612 return VERR_INVALID_PARAMETER;
1613 }
1614 rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
1615 rxPktHdr.uGsoSize = pGso->cbMaxSeg;
1616 rxPktHdr.uChksumStart = pGso->offHdr2;
1617 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
1618 }
1619 else
1620 {
1621 rxPktHdr.uFlags = 0;
1622 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_NONE;
1623 }
1624
1625 uint16_t cSegsAllocated = VIRTIONET_PREALLOCATE_RX_SEG_COUNT;
1626
1627 PRTSGBUF pVirtSegBufToGuest = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
1628 AssertReturn(pVirtSegBufToGuest, VERR_NO_MEMORY);
1629
1630 PRTSGSEG paVirtSegsToGuest = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * cSegsAllocated);
1631 AssertReturnStmt(paVirtSegsToGuest, RTMemFree(pVirtSegBufToGuest), VERR_NO_MEMORY);
1632
1633 int rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pvBuf, cb, &rxPktHdr, cSegsAllocated,
1634 pVirtSegBufToGuest, paVirtSegsToGuest, idxRxQueue);
1635
1636 RTMemFree(paVirtSegsToGuest);
1637 RTMemFree(pVirtSegBufToGuest);
1638
1639 Log7(("\n"));
1640 return rc;
1641}
1642
1643/**
1644 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
1645 */
1646static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf,
1647 size_t cb, PCPDMNETWORKGSO pGso)
1648{
1649 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1650 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1651 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1652
1653 if (!pThis->fVirtioReady)
1654 {
1655 LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
1656 return VERR_INTERRUPTED;
1657 }
1658 if (pThis->fQuiescing)
1659 {
1660 LogRelFunc(("Quiescing I/O for suspend or power off, aborting downstream receive\n"));
1661 return VERR_INTERRUPTED;
1662 }
1663
1664 if (pGso)
1665 {
1666 uint32_t uFeatures = pThis->fNegotiatedFeatures;
1667
1668 switch (pGso->u8Type)
1669 {
1670 case PDMNETWORKGSOTYPE_IPV4_TCP:
1671 uFeatures &= VIRTIONET_F_GUEST_TSO4;
1672 break;
1673 case PDMNETWORKGSOTYPE_IPV6_TCP:
1674 uFeatures &= VIRTIONET_F_GUEST_TSO6;
1675 break;
1676 case PDMNETWORKGSOTYPE_IPV4_UDP:
1677 case PDMNETWORKGSOTYPE_IPV6_UDP:
1678 uFeatures &= VIRTIONET_F_GUEST_UFO;
1679 break;
1680 default:
1681 uFeatures = 0;
1682 break;
1683 }
1684 if (!uFeatures)
1685 {
1686 Log2Func(("GSO type (0x%x) not supported\n", INSTANCE(pThis), pGso->u8Type));
1687 return VERR_NOT_SUPPORTED;
1688 }
1689 }
1690
1691 Log10Func(("%s pvBuf=%p cb=%3u pGso=%p ...", INSTANCE(pThis), pvBuf, cb, pGso));
1692
1693 /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
1694 selection algorithm feasible or even necessary to prevent starvation? */
1695
1696 for (int idxQueuePair = 0; idxQueuePair < pThis->cVirtqPairs; idxQueuePair++)
1697 {
1698 if (RT_SUCCESS(!virtioNetR3IsRxQueuePrimed(pDevIns, pThis, RXQIDX(idxQueuePair))))
1699 {
1700 /* Drop packets if VM is not running or cable is disconnected. */
1701 if (!virtioNetIsOperational(pThis, pDevIns) || !IS_LINK_UP(pThis))
1702 return VINF_SUCCESS;
1703
1704 STAM_PROFILE_START(&pThis->StatReceive, a);
1705 virtioNetR3SetReadLed(pThisCC, true);
1706
1707 int rc = VINF_SUCCESS;
1708 if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
1709 {
1710 rc = virtioNetR3HandleRxPacket(pDevIns, pThis, pThisCC, pvBuf, cb, pGso, RXQIDX(idxQueuePair));
1711 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
1712 }
1713
1714 virtioNetR3SetReadLed(pThisCC, false);
1715 STAM_PROFILE_STOP(&pThis->StatReceive, a);
1716 return rc;
1717 }
1718 }
1719 return VERR_INTERRUPTED;
1720}
1721
1722/**
1723 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
1724 */
1725static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
1726{
1727 return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
1728}
1729
1730/* Read physical bytes from the out segment(s) of descriptor chain */
1731static void virtioNetR3PullChain(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIO_DESC_CHAIN_T pDescChain, void *pv, uint16_t cb)
1732{
1733 uint8_t *pb = (uint8_t *)pv;
1734 uint16_t cbLim = RT_MIN(pDescChain->cbPhysSend, cb);
1735 while (cbLim)
1736 {
1737 size_t cbSeg = cbLim;
1738 RTGCPHYS GCPhys = virtioCoreSgBufGetNextSegment(pDescChain->pSgPhysSend, &cbSeg);
1739 PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pb, cbSeg);
1740 pb += cbSeg;
1741 cbLim -= cbSeg;
1742 pDescChain->cbPhysSend -= cbSeg;
1743 }
1744 LogFunc(("%s Pulled %d/%d bytes from desc chain (%d bytes left)\n",
1745 INSTANCE(pThis), cb - cbLim, cb, pDescChain->cbPhysSend));
1746 RT_NOREF(pThis);
1747}
1748
1749static uint8_t virtioNetR3CtrlRx(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1750 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1751{
1752
1753#define LOG_VIRTIONET_FLAG(fld) LogFunc(("%s Setting %s=%d\n", INSTANCE(pThis), #fld, pThis->fld))
1754
1755 LogFunc(("%s Processing CTRL Rx command\n", INSTANCE(pThis)));
1756 RT_NOREF(pThis);
1757 switch(pCtrlPktHdr->uCmd)
1758 {
1759 case VIRTIONET_CTRL_RX_PROMISC:
1760 break;
1761 case VIRTIONET_CTRL_RX_ALLMULTI:
1762 break;
1763 case VIRTIONET_CTRL_RX_ALLUNI:
1764 /* fallthrough */
1765 case VIRTIONET_CTRL_RX_NOMULTI:
1766 /* fallthrough */
1767 case VIRTIONET_CTRL_RX_NOUNI:
1768 /* fallthrough */
1769 case VIRTIONET_CTRL_RX_NOBCAST:
1770 AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
1771 ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
1772 VIRTIONET_ERROR);
1773 /* fall out */
1774 }
1775
1776 uint8_t fOn, fPromiscChanged = false;
1777 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &fOn, RT_MIN(pDescChain->cbPhysSend, sizeof(fOn)));
1778
1779 switch(pCtrlPktHdr->uCmd)
1780 {
1781 case VIRTIONET_CTRL_RX_PROMISC:
1782 pThis->fPromiscuous = RT_BOOL(fOn);
1783 fPromiscChanged = true;
1784 LOG_VIRTIONET_FLAG(fPromiscuous);
1785 break;
1786 case VIRTIONET_CTRL_RX_ALLMULTI:
1787 pThis->fAllMulticast = RT_BOOL(fOn);
1788 fPromiscChanged = true;
1789 LOG_VIRTIONET_FLAG(fAllMulticast);
1790 break;
1791 case VIRTIONET_CTRL_RX_ALLUNI:
1792 pThis->fAllUnicast = RT_BOOL(fOn);
1793 LOG_VIRTIONET_FLAG(fAllUnicast);
1794 break;
1795 case VIRTIONET_CTRL_RX_NOMULTI:
1796 pThis->fNoMulticast = RT_BOOL(fOn);
1797 LOG_VIRTIONET_FLAG(fNoMulticast);
1798 break;
1799 case VIRTIONET_CTRL_RX_NOUNI:
1800 pThis->fNoUnicast = RT_BOOL(fOn);
1801 LOG_VIRTIONET_FLAG(fNoUnicast);
1802 break;
1803 case VIRTIONET_CTRL_RX_NOBCAST:
1804 pThis->fNoBroadcast = RT_BOOL(fOn);
1805 LOG_VIRTIONET_FLAG(fNoBroadcast);
1806 break;
1807 }
1808
1809 if (pThisCC->pDrv && fPromiscChanged)
1810 {
1811 uint8_t fPromiscuous = pThis->fPromiscuous | pThis->fAllMulticast;
1812 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, fPromiscuous);
1813 }
1814
1815 return VIRTIONET_OK;
1816}
1817
1818static uint8_t virtioNetR3CtrlMac(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1819 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1820{
1821 LogFunc(("%s Processing CTRL MAC command\n", INSTANCE(pThis)));
1822
1823 RT_NOREF(pThisCC);
1824
1825#define ASSERT_CTRL_ADDR_SET(v) \
1826 AssertMsgReturn((v), ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR)
1827
1828#define ASSERT_CTRL_TABLE_SET(v) \
1829 AssertMsgReturn((v), ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR)
1830
1831 AssertMsgReturn(pDescChain->cbPhysSend >= sizeof(*pCtrlPktHdr),
1832 ("insufficient descriptor space for ctrl pkt hdr"),
1833 VIRTIONET_ERROR);
1834
1835 size_t cbRemaining = pDescChain->cbPhysSend;
1836 switch(pCtrlPktHdr->uCmd)
1837 {
1838 case VIRTIONET_CTRL_MAC_ADDR_SET:
1839 {
1840 /* Set default Rx filter MAC */
1841 ASSERT_CTRL_ADDR_SET(cbRemaining >= sizeof(VIRTIONET_CTRL_MAC_TABLE_LEN));
1842 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &pThis->rxFilterMacDefault, sizeof(VIRTIONET_CTRL_MAC_TABLE_LEN));
1843 break;
1844 }
1845 case VIRTIONET_CTRL_MAC_TABLE_SET:
1846 {
1847 VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
1848
1849 /* Load unicast MAC filter table */
1850 ASSERT_CTRL_TABLE_SET(cbRemaining >= sizeof(cMacs));
1851 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &cMacs, sizeof(cMacs));
1852 cbRemaining -= sizeof(cMacs);
1853 Log7Func(("%s Guest provided %d unicast MAC Table entries\n", INSTANCE(pThis), cMacs));
1854 if (cMacs)
1855 {
1856 uint32_t cbMacs = cMacs * sizeof(RTMAC);
1857 ASSERT_CTRL_TABLE_SET(cbRemaining >= cbMacs);
1858 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &pThis->aMacUnicastFilter, cbMacs);
1859 cbRemaining -= cbMacs;
1860 }
1861 pThis->cUnicastFilterMacs = cMacs;
1862
1863 /* Load multicast MAC filter table */
1864 ASSERT_CTRL_TABLE_SET(cbRemaining >= sizeof(cMacs));
1865 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &cMacs, sizeof(cMacs));
1866 cbRemaining -= sizeof(cMacs);
1867 Log10Func(("%s Guest provided %d multicast MAC Table entries\n", INSTANCE(pThis), cMacs));
1868 if (cMacs)
1869 {
1870 uint32_t cbMacs = cMacs * sizeof(RTMAC);
1871 ASSERT_CTRL_TABLE_SET(cbRemaining >= cbMacs);
1872 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &pThis->aMacMulticastFilter, cbMacs);
1873 cbRemaining -= cbMacs;
1874 }
1875 pThis->cMulticastFilterMacs = cMacs;
1876
1877#ifdef LOG_ENABLED
1878 LogFunc(("%s unicast MACs:\n", INSTANCE(pThis)));
1879 for(unsigned i = 0; i < cMacs; i++)
1880 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
1881
1882 LogFunc(("%s multicast MACs:\n", INSTANCE(pThis)));
1883 for(unsigned i = 0; i < cMacs; i++)
1884 LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
1885#endif
1886 }
1887 }
1888 return VIRTIONET_OK;
1889}
1890
1891static uint8_t virtioNetR3CtrlVlan(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1892 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1893{
1894 LogFunc(("%s Processing CTRL VLAN command\n", INSTANCE(pThis)));
1895
1896 RT_NOREF(pThisCC);
1897
1898 uint16_t uVlanId;
1899 uint16_t cbRemaining = pDescChain->cbPhysSend - sizeof(*pCtrlPktHdr);
1900 AssertMsgReturn(cbRemaining > sizeof(uVlanId),
1901 ("DESC chain too small for VIRTIO_NET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
1902 virtioNetR3PullChain(pDevIns, pThis, pDescChain, &uVlanId, sizeof(uVlanId));
1903 AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
1904 ("%s VLAN ID out of range (VLAN ID=%u)\n", INSTANCE(pThis), uVlanId), VIRTIONET_ERROR);
1905 LogFunc(("%s uCommand=%u VLAN ID=%u\n", INSTANCE(pThis), pCtrlPktHdr->uCmd, uVlanId));
1906 switch (pCtrlPktHdr->uCmd)
1907 {
1908 case VIRTIONET_CTRL_VLAN_ADD:
1909 ASMBitSet(pThis->aVlanFilter, uVlanId);
1910 break;
1911 case VIRTIONET_CTRL_VLAN_DEL:
1912 ASMBitClear(pThis->aVlanFilter, uVlanId);
1913 break;
1914 default:
1915 return VIRTIONET_ERROR;
1916 }
1917 return VIRTIONET_OK;
1918}
1919
1920static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1921 PVIRTIO_DESC_CHAIN_T pDescChain)
1922{
1923 LogFunc(("%s Received CTRL packet from guest\n", INSTANCE(pThis)));
1924
1925 if (pDescChain->cbPhysSend < 2)
1926 {
1927 LogFunc(("%s CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", INSTANCE(pThis)));
1928 return;
1929 }
1930 else if (pDescChain->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
1931 {
1932 LogFunc(("%s Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", INSTANCE(pThis)));
1933 return;
1934 }
1935
1936 /*
1937 * Allocate buffer and read in the control command
1938 */
1939 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr = (PVIRTIONET_CTRL_HDR_T)RTMemAllocZ(sizeof(VIRTIONET_CTRL_HDR_T));
1940
1941 AssertPtrReturnVoid(pCtrlPktHdr);
1942
1943 AssertMsgReturnVoid(pDescChain->cbPhysSend >= sizeof(VIRTIONET_CTRL_HDR_T),
1944 ("DESC chain too small for CTRL pkt header"));
1945
1946 virtioNetR3PullChain(pDevIns, pThis, pDescChain, pCtrlPktHdr,
1947 RT_MIN(pDescChain->cbPhysSend, sizeof(VIRTIONET_CTRL_HDR_T)));
1948
1949 Log7Func(("%s CTRL COMMAND: class=%d command=%d\n", INSTANCE(pThis), pCtrlPktHdr->uClass, pCtrlPktHdr->uCmd));
1950
1951 uint8_t uAck;
1952 switch (pCtrlPktHdr->uClass)
1953 {
1954 case VIRTIONET_CTRL_RX:
1955 uAck = virtioNetR3CtrlRx(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
1956 break;
1957 case VIRTIONET_CTRL_MAC:
1958 uAck = virtioNetR3CtrlMac(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
1959 break;
1960 case VIRTIONET_CTRL_VLAN:
1961 uAck = virtioNetR3CtrlVlan(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
1962 break;
1963 case VIRTIONET_CTRL_ANNOUNCE:
1964 uAck = VIRTIONET_OK;
1965 if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
1966 {
1967 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
1968 "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", INSTANCE(pThis)));
1969 break;
1970 }
1971 if (pCtrlPktHdr->uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
1972 {
1973 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", INSTANCE(pThis)));
1974 break;
1975 }
1976 pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
1977 Log7Func(("%s Clearing VIRTIONET_F_ANNOUNCE in config status\n", INSTANCE(pThis)));
1978 break;
1979
1980 default:
1981 LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", pCtrlPktHdr->uClass));
1982 uAck = VIRTIONET_ERROR;
1983 }
1984
1985 /* Currently CTRL pkt header just returns ack, but keeping segment logic generic/flexible
1986 * in case that changes to make adapting more straightforward */
1987 int cSegs = 1;
1988
1989 /* Return CTRL packet Ack byte (result code) to guest driver */
1990 PRTSGSEG paReturnSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG));
1991 AssertMsgReturnVoid(paReturnSegs, ("Out of memory"));
1992
1993 RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
1994 memcpy(paReturnSegs, aStaticSegs, sizeof(RTSGSEG));
1995
1996 PRTSGBUF pReturnSegBuf = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
1997 AssertMsgReturnVoid(pReturnSegBuf, ("Out of memory"));
1998
1999 /* Copy segment data to malloc'd memory to avoid stack out-of-scope errors sanitizer doesn't detect */
2000 for (int i = 0; i < cSegs; i++)
2001 {
2002 void *pv = paReturnSegs[i].pvSeg;
2003 paReturnSegs[i].pvSeg = RTMemAlloc(aStaticSegs[i].cbSeg);
2004 AssertMsgReturnVoid(paReturnSegs[i].pvSeg, ("Out of memory"));
2005 memcpy(paReturnSegs[i].pvSeg, pv, aStaticSegs[i].cbSeg);
2006 }
2007
2008 RTSgBufInit(pReturnSegBuf, paReturnSegs, cSegs);
2009
2010 virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, CTRLQIDX, pReturnSegBuf, pDescChain, true);
2011 virtioCoreQueueSync(pDevIns, &pThis->Virtio, CTRLQIDX, false);
2012
2013 for (int i = 0; i < cSegs; i++)
2014 RTMemFree(paReturnSegs[i].pvSeg);
2015
2016 RTMemFree(paReturnSegs);
2017 RTMemFree(pReturnSegBuf);
2018
2019 LogFunc(("%s Finished processing CTRL command with status %s\n",
2020 INSTANCE(pThis), uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
2021}
2022
2023static int virtioNetR3ReadHeader(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONET_PKT_HDR_T pPktHdr, uint32_t cbFrame)
2024{
2025 int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pPktHdr, sizeof(*pPktHdr));
2026 if (RT_FAILURE(rc))
2027 return rc;
2028
2029 Log(("virtio-net: header (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
2030 pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
2031 pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
2032
2033 if (pPktHdr->uGsoType)
2034 {
2035 uint32_t uMinHdrSize;
2036
2037 /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
2038 AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2039 && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
2040 ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
2041
2042 switch (pPktHdr->uGsoType)
2043 {
2044 case VIRTIONET_HDR_GSO_TCPV4:
2045 case VIRTIONET_HDR_GSO_TCPV6:
2046 uMinHdrSize = sizeof(RTNETTCP);
2047 break;
2048 case VIRTIONET_HDR_GSO_UDP:
2049 uMinHdrSize = 0;
2050 break;
2051 default:
2052 LogFunc(("Bad GSO type in packet header\n"));
2053 return VERR_INVALID_PARAMETER;
2054 }
2055 /* Header + MSS must not exceed the packet size. */
2056 AssertMsgReturn(RT_LIKELY(uMinHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
2057 ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
2058 }
2059
2060 AssertMsgReturn( !pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM
2061 || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
2062 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
2063 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
2064 VERR_BUFFER_OVERFLOW);
2065
2066 return VINF_SUCCESS;
2067}
2068
2069static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
2070 PPDMNETWORKGSO pGso, PVIRTIONET_PKT_HDR_T pPktHdr)
2071{
2072 virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
2073 if (pGso)
2074 {
2075 /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
2076 /*
2077 * We cannot use cdHdrs provided by the guest because of different ways
2078 * it gets filled out by different versions of kernels.
2079 */
2080 //if (pGso->cbHdrs < pPktHdr->uCSumStart + pPktHdr->uCSumOffset + 2)
2081 {
2082 Log4Func(("%s HdrLen before adjustment %d.\n",
2083 INSTANCE(pThis), pGso->cbHdrsTotal));
2084 switch (pGso->u8Type)
2085 {
2086 case PDMNETWORKGSOTYPE_IPV4_TCP:
2087 case PDMNETWORKGSOTYPE_IPV6_TCP:
2088 pGso->cbHdrsTotal = pPktHdr->uChksumStart +
2089 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
2090 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
2091 break;
2092 case PDMNETWORKGSOTYPE_IPV4_UDP:
2093 pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
2094 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
2095 break;
2096 }
2097 /* Update GSO structure embedded into the frame */
2098 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
2099 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
2100 Log4Func(("%s adjusted HdrLen to %d.\n",
2101 INSTANCE(pThis), pGso->cbHdrsTotal));
2102 }
2103 Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
2104 INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2105 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2106 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
2107 }
2108 else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2109 {
2110 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
2111 /*
2112 * This is not GSO frame but checksum offloading is requested.
2113 */
2114 virtioNetR3CompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
2115 pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
2116 }
2117
2118 return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, false);
2119}
2120
2121static void virtioNetR3TransmitPendingPackets(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2122 uint16_t idxTxQueue, bool fOnWorkerThread)
2123{
2124
2125 PVIRTIOCORE pVirtio = &pThis->Virtio;
2126
2127 /*
2128 * Only one thread is allowed to transmit at a time, others should skip
2129 * transmission as the packets will be picked up by the transmitting
2130 * thread.
2131 */
2132 if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
2133 return;
2134
2135 if (!pThis->fVirtioReady)
2136 {
2137 LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x).\n",
2138 INSTANCE(pThis), pThis->virtioNetConfig.uStatus));
2139 return;
2140 }
2141
2142 if (!pThis->fCableConnected)
2143 {
2144 Log(("%s Ignoring transmit requests while cable is disconnected.\n", INSTANCE(pThis)));
2145 return;
2146 }
2147
2148 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2149 if (pDrv)
2150 {
2151 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
2152 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
2153 if (rc == VERR_TRY_AGAIN)
2154 {
2155 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2156 return;
2157 }
2158 }
2159
2160 int cPkts = virtioCoreR3QueuePendingCount(pVirtio->pDevIns, pVirtio, idxTxQueue);
2161 if (!cPkts)
2162 {
2163 LogFunc(("%s No packets to send found on %s\n", INSTANCE(pThis), VIRTQNAME(idxTxQueue)));
2164
2165 if (pDrv)
2166 pDrv->pfnEndXmit(pDrv);
2167
2168 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2169 return;
2170 }
2171 LogFunc(("%s About to transmit %d pending packets\n", INSTANCE(pThis), cPkts));
2172
2173 virtioNetR3SetWriteLed(pThisCC, true);
2174
2175 int rc;
2176 PVIRTIO_DESC_CHAIN_T pDescChain = NULL;
2177 while ((rc = virtioCoreR3QueuePeek(pVirtio->pDevIns, pVirtio, idxTxQueue, &pDescChain)) == VINF_SUCCESS)
2178 {
2179 Log10Func(("%s fetched descriptor chain from %s\n", INSTANCE(pThis), VIRTQNAME(idxTxQueue)));
2180
2181 PVIRTIOSGBUF pSgPhysSend = pDescChain->pSgPhysSend;
2182 PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
2183 uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
2184
2185 VIRTIONET_PKT_HDR_T PktHdr;
2186 uint32_t uSize = 0;
2187
2188 Assert(paSegsFromGuest[0].cbSeg >= sizeof(PktHdr));
2189
2190 /* Compute total frame size. */
2191 for (unsigned i = 0; i < cSegsFromGuest && uSize < VIRTIONET_MAX_FRAME_SIZE; i++)
2192 uSize += paSegsFromGuest[i].cbSeg;
2193
2194 Log5Func(("%s complete frame is %u bytes.\n", INSTANCE(pThis), uSize));
2195 Assert(uSize <= VIRTIONET_MAX_FRAME_SIZE);
2196
2197 /* Truncate oversized frames. */
2198 if (uSize > VIRTIONET_MAX_FRAME_SIZE)
2199 uSize = VIRTIONET_MAX_FRAME_SIZE;
2200
2201 if (pThisCC->pDrv)
2202 {
2203 PDMNETWORKGSO Gso;
2204 PPDMNETWORKGSO pGso = virtioNetR3SetupGsoCtx(&Gso, &PktHdr);
2205 uint64_t uOffset;
2206
2207 /** @todo Optimize away the extra copying! (lazy bird) */
2208 PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
2209 rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uSize, pGso, &pSgBufToPdmLeafDevice);
2210 if (RT_SUCCESS(rc))
2211 {
2212 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
2213 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
2214
2215 uSize -= sizeof(PktHdr);
2216 rc = virtioNetR3ReadHeader(pDevIns, paSegsFromGuest[0].gcPhys, &PktHdr, uSize);
2217 if (RT_FAILURE(rc))
2218 return;
2219 virtioCoreSgBufAdvance(pSgPhysSend, sizeof(PktHdr));
2220
2221 size_t cbCopied = 0;
2222 size_t cbTotal = 0;
2223 size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uSize;
2224 uOffset = 0;
2225 while (cbRemain)
2226 {
2227 PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
2228 uint64_t srcSgStart = (uint64_t)paSeg->gcPhys;
2229 uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
2230 uint64_t srcSgCur = (uint64_t)pSgPhysSend->gcPhysCur;
2231 cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
2232 PDMDevHlpPCIPhysRead(pDevIns,
2233 (RTGCPHYS)pSgPhysSend->gcPhysCur,
2234 ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
2235 virtioCoreSgBufAdvance(pSgPhysSend, cbCopied);
2236 cbRemain -= cbCopied;
2237 uOffset += cbCopied;
2238 cbTotal += cbCopied;
2239 }
2240
2241 LogFunc((".... Copied %lu bytes to %lu byte guest buffer, residual=%lu\n",
2242 cbTotal, pDescChain->cbPhysSend, pDescChain->cbPhysSend - cbTotal));
2243
2244 rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, &PktHdr);
2245 if (RT_FAILURE(rc))
2246 {
2247 LogFunc(("%s Failed to transmit frame, rc = %Rrc\n", INSTANCE(pThis), rc));
2248 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2249 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
2250 pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
2251 }
2252 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2253 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
2254 }
2255 else
2256 {
2257 Log4Func(("Failed to allocate S/G buffer: size=%u rc=%Rrc\n", uSize, rc));
2258 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
2259 virtioCoreR3DescChainRelease(pVirtio, pDescChain);
2260 break;
2261 }
2262
2263 /* Remove this descriptor chain from the available ring */
2264 virtioCoreR3QueueSkip(pVirtio, idxTxQueue);
2265
2266 /* No data to return to guest, but call is needed put elem (e.g. desc chain) on used ring */
2267 virtioCoreR3QueuePut(pVirtio->pDevIns, pVirtio, idxTxQueue, NULL, pDescChain, false);
2268
2269 /* Update used ring idx and notify guest that we've transmitted the data it sent */
2270 virtioCoreQueueSync(pVirtio->pDevIns, pVirtio, idxTxQueue, false);
2271 }
2272
2273 virtioCoreR3DescChainRelease(pVirtio, pDescChain);
2274 pDescChain = NULL;
2275 }
2276 virtioNetR3SetWriteLed(pThisCC, false);
2277
2278 if (pDrv)
2279 pDrv->pfnEndXmit(pDrv);
2280
2281 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2282}
2283
2284/**
2285 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2286 */
2287static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
2288{
2289 LogFunc(("\n"));
2290 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2291 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2292 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2293
2294 STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
2295
2296 /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
2297 selection algorithm feasible or even necessary */
2298 virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, TXQIDX(0), false /*fOnWorkerThread*/);
2299}
2300
2301/**
2302 * @callback_method_impl{VIRTIOCORER3,pfnQueueNotified}
2303 */
2304static DECLCALLBACK(void) virtioNetR3QueueNotified(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint16_t idxQueue)
2305{
2306 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
2307 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
2308 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2309
2310 uint16_t idxWorker;
2311 if (idxQueue == CTRLQIDX)
2312 idxWorker = pThis->cWorkers - 1;
2313 else
2314 idxWorker = idxQueue / 2;
2315
2316 PVIRTIONETWORKER pWorker = &pThis->aWorkers[idxWorker];
2317 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[idxWorker];
2318 AssertReturnVoid(idxQueue < pThis->cVirtQueues);
2319
2320#ifdef LOG_ENABLED
2321 RTLogFlush(NULL);
2322#endif
2323
2324 Log10Func(("%s %s has available buffers\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2325
2326 if (IS_RX_QUEUE(idxQueue))
2327 {
2328 Log10Func(("%s Receive buffers have been added, waking Rx thread.\n",
2329 INSTANCE(pThis)));
2330 virtioNetR3WakeupRxBufWaiter(pDevIns);
2331 }
2332 else
2333 {
2334 /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
2335 if (!ASMAtomicXchgBool(&pWorkerR3->fNotified, true))
2336 {
2337 if (ASMAtomicReadBool(&pWorkerR3->fSleeping))
2338 {
2339 Log10Func(("%s waking %s worker.\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2340 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
2341 AssertRC(rc);
2342 }
2343 }
2344 }
2345}
2346
2347/**
2348 * @callback_method_impl{FNPDMTHREADDEV}
2349 */
2350static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
2351{
2352 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2353 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2354 uint16_t const idxWorker = (uint16_t)(uintptr_t)pThread->pvUser;
2355 PVIRTIONETWORKER pWorker = &pThis->aWorkers[idxWorker];
2356 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[idxWorker];
2357 uint16_t const idxQueue = pWorkerR3->idxQueue;
2358
2359 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
2360 {
2361 return VINF_SUCCESS;
2362 }
2363 LogFunc(("%s worker thread started for %s\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2364 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, false);
2365 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
2366 {
2367 if (virtioCoreQueueIsEmpty(pDevIns, &pThis->Virtio, idxQueue))
2368 {
2369 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, true);
2370 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
2371 ASMAtomicWriteBool(&pWorkerR3->fSleeping, true);
2372 bool fNotificationSent = ASMAtomicXchgBool(&pWorkerR3->fNotified, false);
2373 if (!fNotificationSent)
2374 {
2375 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, true);
2376 Log10Func(("%s %s worker sleeping...\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2377 Assert(ASMAtomicReadBool(&pWorkerR3->fSleeping));
2378 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
2379 STAM_COUNTER_INC(&pThis->StatTransmitByThread);
2380 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
2381 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
2382 return VINF_SUCCESS;
2383 if (rc == VERR_INTERRUPTED)
2384 {
2385 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, false);
2386 continue;
2387 }
2388 Log10Func(("%s %s worker woken\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2389 ASMAtomicWriteBool(&pWorkerR3->fNotified, false);
2390 }
2391 ASMAtomicWriteBool(&pWorkerR3->fSleeping, false);
2392 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, false);
2393 }
2394
2395 /* Dispatch to the handler for the queue this worker is set up to drive */
2396
2397 if (!pThisCC->fQuiescing)
2398 {
2399 if (IS_CTRL_QUEUE(idxQueue))
2400 {
2401 Log10Func(("%s fetching next descriptor chain from %s\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2402 PVIRTIO_DESC_CHAIN_T pDescChain = NULL;
2403 int rc = virtioCoreR3QueueGet(pDevIns, &pThis->Virtio, idxQueue, &pDescChain, true);
2404 if (rc == VERR_NOT_AVAILABLE)
2405 {
2406 Log10Func(("%s Nothing found in %s\n", INSTANCE(pThis), VIRTQNAME(idxQueue)));
2407 continue;
2408 }
2409 virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pDescChain);
2410 virtioCoreR3DescChainRelease(&pThis->Virtio, pDescChain);
2411 }
2412 else if (IS_TX_QUEUE(idxQueue))
2413 {
2414 Log10Func(("%s Notified of data to transmit\n", INSTANCE(pThis)));
2415 virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC,
2416 idxQueue, true /* fOnWorkerThread */);
2417 }
2418
2419 /* Rx queues aren't handled by our worker threads. Instead, the PDM network
2420 * leaf driver invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback,
2421 * which waits until notified directly by virtioNetR3QueueNotified()
2422 * that guest IN buffers have been added to receive virt queue.
2423 */
2424 }
2425 }
2426 return VINF_SUCCESS;
2427}
2428
2429DECLINLINE(int) virtioNetR3CsEnter(PPDMDEVINS pDevIns, PVIRTIONET pThis, int rcBusy)
2430{
2431 RT_NOREF(pDevIns, pThis, rcBusy);
2432 /* Original DevVirtioNet uses CS in attach/detach/link-up timer/tx timer/transmit */
2433 LogFunc(("%s CS unimplemented. What does the critical section protect in orig driver??", INSTANCE(pThis)));
2434 return VINF_SUCCESS;
2435}
2436
2437DECLINLINE(void) virtioNetR3CsLeave(PPDMDEVINS pDevIns, PVIRTIONET pThis)
2438{
2439 RT_NOREF(pDevIns, pThis);
2440 LogFunc(("%s CS unimplemented. What does the critical section protect in orig driver??", INSTANCE(pThis)));
2441}
2442
2443
2444/**
2445 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
2446 */
2447static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
2448{
2449 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2450 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2451 RT_NOREF(pTimer, pvUser);
2452
2453 int rc = virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
2454 AssertRCReturnVoid(rc);
2455
2456 SET_LINK_UP(pThis);
2457
2458 virtioNetR3WakeupRxBufWaiter(pDevIns);
2459
2460 virtioNetR3CsLeave(pDevIns, pThis);
2461
2462 LogFunc(("%s Link is up\n", INSTANCE(pThis)));
2463
2464 if (pThisCC->pDrv)
2465 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
2466}
2467
2468/**
2469 * Takes down the link temporarily if it's current status is up.
2470 *
2471 * This is used during restore and when replumbing the network link.
2472 *
2473 * The temporary link outage is supposed to indicate to the OS that all network
2474 * connections have been lost and that it for instance is appropriate to
2475 * renegotiate any DHCP lease.
2476 *
2477 * @param pDevIns The device instance.
2478 * @param pThis The virtio-net shared instance data.
2479 * @param pThisCC The virtio-net ring-3 instance data.
2480 */
2481static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2482{
2483 if (IS_LINK_UP(pThis))
2484 {
2485 SET_LINK_DOWN(pThis);
2486
2487 /* Restore the link back in 5 seconds. */
2488 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
2489 AssertRC(rc);
2490
2491 LogFunc(("%s Link is down temporarily\n", INSTANCE(pThis)));
2492 }
2493}
2494
2495/**
2496 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
2497 */
2498static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
2499{
2500 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2501 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2502
2503 return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
2504}
2505
2506/**
2507 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
2508 */
2509static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
2510{
2511 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
2512 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2513 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2514
2515 bool fCachedLinkIsUp = IS_LINK_UP(pThis);
2516 bool fActiveLinkIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
2517
2518 Log7Func(("%s enmState=%d\n", INSTANCE(pThis), enmState));
2519 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
2520 {
2521 if (fCachedLinkIsUp)
2522 {
2523 /*
2524 * We bother to bring the link down only if it was up previously. The UP link state
2525 * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
2526 */
2527 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
2528 if (pThisCC->pDrv)
2529 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2530 }
2531 }
2532 else if (fActiveLinkIsUp != fCachedLinkIsUp)
2533 {
2534 if (fCachedLinkIsUp)
2535 {
2536 Log(("%s Link is up\n", INSTANCE(pThis)));
2537 pThis->fCableConnected = true;
2538 SET_LINK_UP(pThis);
2539 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2540 }
2541 else /* cached Link state is down */
2542 {
2543 /* The link was brought down explicitly, make sure it won't come up by timer. */
2544 PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
2545 Log(("%s Link is down\n", INSTANCE(pThis)));
2546 pThis->fCableConnected = false;
2547 SET_LINK_DOWN(pThis);
2548 virtioCoreNotifyConfigChanged(&pThis->Virtio);
2549 }
2550 if (pThisCC->pDrv)
2551 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2552 }
2553 return VINF_SUCCESS;
2554}
2555
2556static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2557{
2558 Log10Func(("%s\n", INSTANCE(pThis)));
2559 int rc = VINF_SUCCESS;
2560 for (unsigned idxWorker = 0; idxWorker < pThis->cWorkers; idxWorker++)
2561 {
2562 PVIRTIONETWORKER pWorker = &pThis->aWorkers[idxWorker];
2563 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2564 {
2565 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
2566 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2567 }
2568 if (pThisCC->aWorkers[idxWorker].pThread)
2569 {
2570 int rcThread;
2571 rc = PDMDevHlpThreadDestroy(pDevIns, pThisCC->aWorkers[idxWorker].pThread, &rcThread);
2572 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
2573 AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
2574 pThisCC->aWorkers[idxWorker].pThread = NULL;
2575 }
2576 }
2577 return rc;
2578}
2579
2580static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2581 uint16_t idxWorker, uint16_t idxQueue)
2582{
2583 Log10Func(("%s\n", INSTANCE(pThis)));
2584 int rc = VINF_SUCCESS;
2585 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->aWorkers[idxWorker].hEvtProcess);
2586
2587 if (RT_FAILURE(rc))
2588 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2589 N_("DevVirtioNET: Failed to create SUP event semaphore"));
2590
2591 LogFunc(("creating thread, idxWorker=%d, idxQueue=%d\n", idxWorker, idxQueue));
2592 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->aWorkers[idxWorker].pThread,
2593 (void *)(uintptr_t)idxWorker, virtioNetR3WorkerThread,
2594 virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, VIRTQNAME(idxQueue));
2595 if (rc != VINF_SUCCESS)
2596 {
2597 LogRel(("Error creating thread for Virtual Queue %s: %Rrc\n", VIRTQNAME(idxQueue), rc));
2598 return rc;
2599 }
2600 pThisCC->aWorkers[idxWorker].idxQueue = idxQueue;
2601 pThis->afQueueAttached[idxQueue] = true;
2602 return rc;
2603}
2604
2605static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
2606{
2607 Log10Func(("%s\n", INSTANCE(pThis)));
2608
2609 int rc;
2610 uint16_t idxWorker = 0;
2611 for (uint16_t idxQueuePair = 0; idxQueuePair < pThis->cVirtqPairs; idxQueuePair++)
2612 {
2613 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, pThisCC, idxWorker, TXQIDX(idxQueuePair));
2614 AssertRCReturn(rc, rc);
2615 idxWorker++;
2616 }
2617 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, pThisCC, idxWorker, CTRLQIDX);
2618 pThis->cWorkers = idxWorker + 1;
2619 return rc;
2620}
2621/**
2622 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
2623 */
2624static DECLCALLBACK(void) virtioNetR3StatusChanged(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
2625{
2626 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
2627 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
2628
2629 pThis->fVirtioReady = fVirtioReady;
2630
2631 if (fVirtioReady)
2632 {
2633 LogFunc(("%s VirtIO ready\n-----------------------------------------------------------------------------------------\n",
2634 INSTANCE(pThis)));
2635 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
2636
2637 pThis->fResetting = false;
2638 pThisCC->fQuiescing = false;
2639 pThis->fNegotiatedFeatures = virtioCoreGetAcceptedFeatures(pVirtio);
2640#ifdef LOG_ENABLED
2641 virtioPrintFeatures(pVirtio);
2642 virtioNetPrintFeatures(pThis);
2643#endif
2644 for (unsigned idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
2645 {
2646 (void) virtioCoreR3QueueAttach(&pThis->Virtio, idxQueue, VIRTQNAME(idxQueue));
2647 pThis->afQueueAttached[idxQueue] = true;
2648 virtioCoreQueueIsEmpty(pThisCC->pDevIns, &pThis->Virtio, idxQueue);
2649 virtioCoreQueueSetNotify(&pThis->Virtio, idxQueue, true);
2650 }
2651 }
2652 else
2653 {
2654 LogFunc(("%s VirtIO is resetting\n", INSTANCE(pThis)));
2655
2656 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
2657 Log7Func(("%s Link is %s\n", INSTANCE(pThis), pThis->fCableConnected ? "up" : "down"));
2658
2659 pThis->fPromiscuous = true;
2660 pThis->fAllMulticast = false;
2661 pThis->fAllUnicast = false;
2662 pThis->fNoMulticast = false;
2663 pThis->fNoUnicast = false;
2664 pThis->fNoBroadcast = false;
2665 pThis->uIsTransmitting = 0;
2666 pThis->cUnicastFilterMacs = 0;
2667 pThis->cMulticastFilterMacs = 0;
2668
2669 memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
2670 memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
2671 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
2672
2673 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
2674
2675 for (unsigned idxQueue = 0; idxQueue < pThis->cVirtQueues; idxQueue++)
2676 pThis->afQueueAttached[idxQueue] = false;
2677 }
2678}
2679#endif /* IN_RING3 */
2680
2681/**
2682 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
2683 *
2684 * One harddisk at one port has been unplugged.
2685 * The VM is suspended at this point.
2686 */
2687static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2688{
2689 RT_NOREF(fFlags);
2690
2691 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2692 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2693
2694 Log7Func(("%s\n", INSTANCE(pThis)));
2695 AssertLogRelReturnVoid(iLUN == 0);
2696
2697 int rc = virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
2698 AssertMsgRCReturnVoid(rc, ("Failed to enter critical section"));
2699
2700 /*
2701 * Zero important members.
2702 */
2703 pThisCC->pDrvBase = NULL;
2704 pThisCC->pDrv = NULL;
2705
2706 virtioNetR3CsLeave(pDevIns, pThis);
2707}
2708
2709/**
2710 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
2711 *
2712 * This is called when we change block driver.
2713 */
2714static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2715{
2716 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2717 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2718
2719 RT_NOREF(fFlags);
2720
2721 Log7Func(("%s", INSTANCE(pThis)));
2722
2723 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
2724
2725 int rc = virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
2726 AssertMsgRCReturn(rc, ("Failed to enter critical section"), rc);
2727
2728 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pDevIns->IBase, &pThisCC->pDrvBase, "Network Port");
2729 if (RT_SUCCESS(rc))
2730 {
2731 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2732 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2733 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
2734 }
2735 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2736 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2737 Log(("%s No attached driver!\n", INSTANCE(pThis)));
2738
2739 virtioNetR3CsLeave(pDevIns, pThis);
2740 return rc;
2741
2742 AssertRelease(!pThisCC->pDrvBase);
2743 return rc;
2744}
2745
2746/**
2747 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
2748 */
2749static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2750{
2751 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
2752 if (iLUN)
2753 return VERR_PDM_LUN_NOT_FOUND;
2754 *ppLed = &pThisR3->led;
2755 return VINF_SUCCESS;
2756}
2757
2758
2759/**
2760 * @interface_method_impl{PDMIBASE,pfnQueryInterface,
2761 */
2762static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2763{
2764 PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
2765
2766 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
2767 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
2768 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
2769 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
2770 return NULL;
2771}
2772
2773/**
2774 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
2775 */
2776static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
2777{
2778 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2779
2780 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2781 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2782
2783 Log(("%s Destroying instance\n", INSTANCE(pThis)));
2784
2785 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
2786 {
2787 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
2788 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
2789 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
2790 }
2791
2792 virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
2793
2794 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
2795
2796 return VINF_SUCCESS;
2797}
2798
2799/**
2800 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2801 */
2802static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2803{
2804 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2805 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2806 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2807 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2808
2809 /*
2810 * Quick initialization of the state data, making sure that the destructor always works.
2811 */
2812 Log7Func(("PDM device instance: %d\n", iInstance));
2813
2814
2815 RTStrPrintf(pThis->szInstanceName, sizeof(pThis->szInstanceName), "VIRTIONET", iInstance);
2816
2817 pThisCC->pDevIns = pDevIns;
2818
2819 pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
2820 pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
2821 pThisCC->led.u32Magic = PDMLED_MAGIC;
2822
2823 /* Interfaces */
2824 pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
2825 pThisCC->INetworkDown.pfnReceive = virtioNetR3NetworkDown_Receive;
2826 pThisCC->INetworkDown.pfnReceiveGso = virtioNetR3NetworkDown_ReceiveGso;
2827 pThisCC->INetworkDown.pfnXmitPending = virtioNetR3NetworkDown_XmitPending;
2828 pThisCC->INetworkConfig.pfnGetMac = virtioNetR3NetworkConfig_GetMac;
2829 pThisCC->INetworkConfig.pfnGetLinkState = virtioNetR3NetworkConfig_GetLinkState;
2830 pThisCC->INetworkConfig.pfnSetLinkState = virtioNetR3NetworkConfig_SetLinkState;
2831
2832 /*
2833 * Validate configuration.
2834 */
2835 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo", "");
2836
2837 /* Get config params */
2838 int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
2839 if (RT_FAILURE(rc))
2840 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
2841
2842 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
2843 if (RT_FAILURE(rc))
2844 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
2845
2846 uint32_t uStatNo = iInstance;
2847 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
2848 if (RT_FAILURE(rc))
2849 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
2850
2851 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
2852 if (RT_FAILURE(rc))
2853 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
2854
2855 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
2856
2857 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
2858 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
2859 INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
2860
2861 Log(("%s Link up delay is set to %u seconds\n", INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
2862
2863 /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
2864 memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
2865
2866 /*
2867 * Do core virtio initialization.
2868 */
2869
2870#if FEATURE_OFFERED(STATUS)
2871 pThis->virtioNetConfig.uStatus = 0;
2872#endif
2873
2874#if FEATURE_OFFERED(MQ)
2875 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
2876#endif
2877
2878 /* Initialize the generic Virtio core: */
2879 pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChanged;
2880 pThisCC->Virtio.pfnQueueNotified = virtioNetR3QueueNotified;
2881 pThisCC->Virtio.pfnDevCapRead = virtioNetR3DevCapRead;
2882 pThisCC->Virtio.pfnDevCapWrite = virtioNetR3DevCapWrite;
2883
2884 VIRTIOPCIPARAMS VirtioPciParams;
2885 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
2886 VirtioPciParams.uClassBase = PCI_CLASS_BASE_NETWORK_CONTROLLER;
2887 VirtioPciParams.uClassSub = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
2888 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2889 VirtioPciParams.uSubsystemId = PCI_DEVICE_ID_VIRTIONET_HOST; /* VirtIO 1.0 spec allows PCI Device ID here */
2890 VirtioPciParams.uInterruptLine = 0x00;
2891 VirtioPciParams.uInterruptPin = 0x01;
2892
2893 /*
2894 * Initialize VirtIO core. This will result in a "status changed" callback
2895 * when VirtIO is ready, at which time the Rx queue and ctrl queue worker threads will be created.
2896 */
2897 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, INSTANCE(pThis),
2898 VIRTIONET_HOST_FEATURES_OFFERED,
2899 &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
2900 if (RT_FAILURE(rc))
2901 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
2902
2903 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
2904 if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
2905 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
2906
2907 pThis->cVirtqPairs = pThis->fNegotiatedFeatures & VIRTIONET_F_MQ
2908 ? pThis->virtioNetConfig.uMaxVirtqPairs : 1;
2909
2910 pThis->cVirtQueues += pThis->cVirtqPairs * 2 + 1;
2911
2912 /* Create Link Up Timer */
2913 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL, TMTIMER_FLAGS_NO_CRIT_SECT,
2914 "VirtioNet Link Up Timer", &pThisCC->hLinkUpTimer);
2915
2916 /*
2917 * Initialize queues.
2918 */
2919 virtioNetR3SetVirtqNames(pThis);
2920
2921 /*
2922 * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
2923 */
2924 rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
2925 if (RT_FAILURE(rc))
2926 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to worker threads"));
2927
2928 /*
2929 * Create the semaphore that will be used to synchronize/throttle
2930 * the downstream LUN's Rx waiter thread.
2931 */
2932 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
2933 if (RT_FAILURE(rc))
2934 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
2935
2936 /*
2937 * Attach network driver instance
2938 */
2939 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
2940 if (RT_SUCCESS(rc))
2941 {
2942 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2943 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2944 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
2945 }
2946 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2947 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2948 Log(("%s No attached driver!\n", INSTANCE(pThis)));
2949
2950 /*
2951 * Status driver
2952 */
2953 PPDMIBASE pUpBase;
2954 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
2955 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
2956 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2957
2958 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
2959
2960 /*
2961 * Register saved state.
2962 */
2963 rc = PDMDevHlpSSMRegister(pDevIns, VIRTIONET_SAVED_STATE_VERSION, sizeof(*pThis),
2964 virtioNetR3SaveExec, virtioNetR3LoadExec);
2965 AssertRCReturn(rc, rc);
2966
2967
2968
2969 /*
2970 * Statistics and debug stuff.
2971 * The /Public/ bits are official and used by session info in the GUI.
2972 */
2973 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
2974 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
2975 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
2976 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
2977 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
2978 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
2979
2980 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
2981 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
2982 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
2983 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
2984 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
2985 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
2986# ifdef VBOX_WITH_STATISTICS
2987 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
2988 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
2989 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
2990 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
2991 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
2992 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
2993 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
2994 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
2995# endif
2996
2997 /*
2998 * Register the debugger info callback (ignore errors).
2999 */
3000 char szTmp[128];
3001 RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
3002 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-net info", virtioNetR3Info);
3003 return rc;
3004}
3005
3006#else /* !IN_RING3 */
3007
3008/**
3009 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3010 */
3011static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
3012{
3013 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3014 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3015 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3016
3017LogRelFunc(("\n"));
3018 return virtioCoreRZInit(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
3019}
3020
3021#endif /* !IN_RING3 */
3022
3023
3024
3025/**
3026 * The device registration structure.
3027 */
3028const PDMDEVREG g_DeviceVirtioNet_1_0 =
3029{
3030 /* .uVersion = */ PDM_DEVREG_VERSION,
3031 /* .uReserved0 = */ 0,
3032 /* .szName = */ "virtio-net-1-dot-0",
3033 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ
3034 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
3035 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
3036 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
3037 /* .cMaxInstances = */ ~0U,
3038 /* .uSharedVersion = */ 42,
3039 /* .cbInstanceShared = */ sizeof(VIRTIONET),
3040 /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
3041 /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
3042 /* .cMaxPciDevices = */ 1,
3043 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
3044 /* .pszDescription = */ "Virtio Host NET.\n",
3045#if defined(IN_RING3)
3046 /* .pszRCMod = */ "VBoxDDRC.rc",
3047 /* .pszR0Mod = */ "VBoxDDR0.r0",
3048 /* .pfnConstruct = */ virtioNetR3Construct,
3049 /* .pfnDestruct = */ virtioNetR3Destruct,
3050 /* .pfnRelocate = */ NULL,
3051 /* .pfnMemSetup = */ NULL,
3052 /* .pfnPowerOn = */ NULL,
3053 /* .pfnReset = */ virtioNetR3Reset,
3054 /* .pfnSuspend = */ virtioNetR3Suspend,
3055 /* .pfnResume = */ virtioNetR3Resume,
3056 /* .pfnAttach = */ virtioNetR3Attach,
3057 /* .pfnDetach = */ virtioNetR3Detach,
3058 /* .pfnQueryInterface = */ NULL,
3059 /* .pfnInitComplete = */ NULL,
3060 /* .pfnPowerOff = */ virtioNetR3PowerOff,
3061 /* .pfnSoftReset = */ NULL,
3062 /* .pfnReserved0 = */ NULL,
3063 /* .pfnReserved1 = */ NULL,
3064 /* .pfnReserved2 = */ NULL,
3065 /* .pfnReserved3 = */ NULL,
3066 /* .pfnReserved4 = */ NULL,
3067 /* .pfnReserved5 = */ NULL,
3068 /* .pfnReserved6 = */ NULL,
3069 /* .pfnReserved7 = */ NULL,
3070#elif defined(IN_RING0)
3071 /* .pfnEarlyConstruct = */ NULL,
3072 /* .pfnConstruct = */ virtioNetRZConstruct,
3073 /* .pfnDestruct = */ NULL,
3074 /* .pfnFinalDestruct = */ NULL,
3075 /* .pfnRequest = */ NULL,
3076 /* .pfnReserved0 = */ NULL,
3077 /* .pfnReserved1 = */ NULL,
3078 /* .pfnReserved2 = */ NULL,
3079 /* .pfnReserved3 = */ NULL,
3080 /* .pfnReserved4 = */ NULL,
3081 /* .pfnReserved5 = */ NULL,
3082 /* .pfnReserved6 = */ NULL,
3083 /* .pfnReserved7 = */ NULL,
3084#elif defined(IN_RC)
3085 /* .pfnConstruct = */ virtioNetRZConstruct,
3086 /* .pfnReserved0 = */ NULL,
3087 /* .pfnReserved1 = */ NULL,
3088 /* .pfnReserved2 = */ NULL,
3089 /* .pfnReserved3 = */ NULL,
3090 /* .pfnReserved4 = */ NULL,
3091 /* .pfnReserved5 = */ NULL,
3092 /* .pfnReserved6 = */ NULL,
3093 /* .pfnReserved7 = */ NULL,
3094#else
3095# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3096#endif
3097 /* .uVersionEnd = */ PDM_DEVREG_VERSION
3098};
3099
Note: See TracBrowser for help on using the repository browser.

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