VirtualBox

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

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

Network/DevVirtioNet_1_0.cpp: Builds cleanly. Finished initial tying together of events/notifications. See BugRef(8651) #52

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