VirtualBox

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

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

Fix flowerbox. Even though this code has guards that prevent it from building some kind of lint was done on Windows build machines.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 81.7 KB
Line 
1/* $Id: DevVirtioNet_1_0.cpp 82776 2020-01-16 12:01:53Z vboxsync $ $Revision: 82776 $ $Date: 2020-01-16 12:01:53 +0000 (Thu, 16 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#define VIRTIONET_WITH_MERGEABLE_RX_BUFS
34
35#include <VBox/vmm/pdmdev.h>
36#include <VBox/vmm/pdmcritsect.h>
37#include <VBox/vmm/pdmnetifs.h>
38#include <VBox/msi.h>
39#include <VBox/version.h>
40//#include <VBox/asm.h>
41#include <VBox/log.h>
42#include <iprt/errcore.h>
43#include <iprt/assert.h>
44#include <iprt/string.h>
45#include <VBox/sup.h>
46#ifdef IN_RING3
47#include <VBox/VBoxPktDmp.h>
48#endif
49#ifdef IN_RING3
50# include <iprt/alloc.h>
51# include <iprt/memcache.h>
52# include <iprt/semaphore.h>
53# include <iprt/sg.h>
54# include <iprt/param.h>
55# include <iprt/uuid.h>
56#endif
57#include "../VirtIO/Virtio_1_0.h"
58
59//#include "VBoxNET.h"
60#include "VBoxDD.h"
61
62/*
63 * GSO = Generic Segmentation Offload
64 * TSO = TCP Segmentation Offload
65 * UDP = UDP Fragmentation Offset
66
67/*********************************************************************************************************************************
68* Defined Constants And Macros *
69*********************************************************************************************************************************/
70/** The current saved state version. */
71#define VIRTIONET_SAVED_STATE_VERSION UINT32_C(1)
72#define VIRTIONET_MAX_QPAIRS 512
73#define VIRTIONET_MAX_QUEUES (VIRTIONET_MAX_QPAIRS * 2 + 1)
74#define VIRTIONET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP pkt size + Ethernet header with VLAN tag */
75#define VIRTIONET_MAC_FILTER_LEN 32
76#define VIRTIONET_MAX_VLAN_ID (1 << 12)
77#define VIRTIONET_PREALLOCATE_RX_SEG_COUNT 32
78
79#define QUEUE_NAME(a_pVirtio, a_idxQueue) ((a_pVirtio)->virtqState[(a_idxQueue)].szVirtqName)
80#define VIRTQNAME(qIdx) (pThis->aszVirtqNames[qIdx])
81#define CBVIRTQNAME(qIdx) RTStrNLen(VIRTQNAME(qIdx), sizeof(VIRTQNAME(qIdx)))
82#define FEATURE_ENABLED(feature) (pThis->fNegotiatedFeatures & VIRTIONET_F_##feature)
83#define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
84
85/* Macros to calculate queue specific index number VirtIO 1.0, 5.1.2 */
86#define RXQIDX(qPairIdx) (qPairIdx * 2)
87#define TXQIDX(qPairIdx) (qPairIdx * 2 + 1)
88#define CTRLQIDX ((pThis->fNegotiatedFeatures & VIRTIONET_F_MQ) ? VIRTIONET_MAX_QPAIRS * 2 + 2 : 4)
89
90#define RXVIRTQNAME(qPairIdx) (pThis->aszVirtqNames[RXQIDX(qPairIdx)])
91#define TXVIRTQNAME(qPairIdx) (pThis->aszVirtqNames[TXQIDX(qPairIdx)])
92#define CTLVIRTQNAME(qPairIdx) (pThis->aszVirtqNames[CTRLQIDX])
93
94#define LUN0 0
95
96
97/*
98 * Glossary of networking acronyms used in the following bit definitions:
99 *
100 * GSO = Generic Segmentation Offload
101 * TSO = TCP Segmentation Offload
102 * UFO = UDP Fragmentation Offload
103 * ECN = Explicit Congestion Notification
104 */
105
106/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
107 * @{ */
108#define VIRTIONET_F_CSUM RT_BIT_64(0) /**< Handle packets with partial checksum */
109#define VIRTIONET_F_GUEST_CSUM RT_BIT_64(1) /**< Handles packets with partial checksum */
110#define VIRTIONET_F_CTRL_GUEST_OFFLOADS RT_BIT_64(2) /**< Control channel offloads reconfig support */
111#define VIRTIONET_F_MAC RT_BIT_64(5) /**< Device has given MAC address */
112#define VIRTIONET_F_GUEST_TSO4 RT_BIT_64(7) /**< Driver can receive TSOv4 */
113#define VIRTIONET_F_GUEST_TSO6 RT_BIT_64(8) /**< Driver can receive TSOv6 */
114#define VIRTIONET_F_GUEST_ECN RT_BIT_64(9) /**< Driver can receive TSO with ECN */
115#define VIRTIONET_F_GUEST_UFO RT_BIT_64(10) /**< Driver can receive UFO */
116#define VIRTIONET_F_HOST_TSO4 RT_BIT_64(11) /**< Device can receive TSOv4 */
117#define VIRTIONET_F_HOST_TSO6 RT_BIT_64(12) /**< Device can receive TSOv6 */
118#define VIRTIONET_F_HOST_ECN RT_BIT_64(13) /**< Device can receive TSO with ECN */
119#define VIRTIONET_F_HOST_UFO RT_BIT_64(14) /**< Device can receive UFO */
120#define VIRTIONET_F_MRG_RXBUF RT_BIT_64(15) /**< Driver can merge receive buffers */
121#define VIRTIONET_F_STATUS RT_BIT_64(16) /**< Config status field is available */
122#define VIRTIONET_F_CTRL_VQ RT_BIT_64(17) /**< Control channel is available */
123#define VIRTIONET_F_CTRL_RX RT_BIT_64(18) /**< Control channel RX mode + MAC addr filtering */
124#define VIRTIONET_F_CTRL_VLAN RT_BIT_64(19) /**< Control channel VLAN filtering */
125#define VIRTIONET_F_CTRL_RX_EXTRA RT_BIT_64(20) /**< Control channel RX mode extra functions */
126#define VIRTIONET_F_GUEST_ANNOUNCE RT_BIT_64(21) /**< Driver can send gratuitous packets */
127#define VIRTIONET_F_MQ RT_BIT_64(22) /**< Support ultiqueue with auto receive steering */
128#define VIRTIONET_F_CTRL_MAC_ADDR RT_BIT_64(23) /**< Set MAC address through control channel */
129/** @} */
130
131#ifdef VIRTIONET_WITH_GSO
132# define VIRTIONET_HOST_FEATURES_GSO \
133 VIRTIONET_F_CSUM \
134 | VIRTIONET_F_HOST_TSO4 \
135 | VIRTIONET_F_HOST_TSO6 \
136 | VIRTIONET_F_HOST_UFO \
137 | VIRTIONET_F_GUEST_TSO4 \
138 | VIRTIONET_F_GUEST_TSO6 \
139 | VIRTIONET_F_GUEST_UFO \
140 | VIRTIONET_F_GUEST_CSUM /* @bugref(4796) Guest must handle partial chksums */
141#else
142# define VIRTIONET_HOST_FEATURES_GSO
143#endif
144
145#define VIRTIONET_HOST_FEATURES_OFFERED \
146 VIRTIONET_F_MAC \
147 | VIRTIONET_F_STATUS \
148 | VIRTIONET_F_CTRL_VQ \
149 | VIRTIONET_F_CTRL_RX \
150 | VIRTIONET_F_CTRL_VLAN \
151 | VIRTIONET_HOST_FEATURES_GSO
152
153#define PCI_DEVICE_ID_VIRTIONET_HOST 0x1041 /**< Informs guest driver of type of VirtIO device */
154#define PCI_CLASS_BASE_NETWORK_CONTROLLER 0x02 /**< PCI Network device class */
155#define PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER 0x00 /**< PCI NET Controller subclass */
156#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
157#define VIRTIONET_PCI_CLASS 0x01 /**< Base class Mass Storage? */
158
159
160/*********************************************************************************************************************************
161* Structures and Typedefs *
162*********************************************************************************************************************************/
163/**
164 * Virtio Net Host Device device-specific configuration (VirtIO 1.0, 5.1.4)
165 * VBox VirtIO core issues callback to this VirtIO device-specific implementation to handle
166 * MMIO accesses to device-specific configuration parameters.
167 */
168typedef struct virtio_net_config
169{
170 uint8_t uMacAddress[6]; /**< mac */
171#if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_STATUS
172 uint16_t uStatus; /**< status */
173#endif
174#if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_MQ
175 uint16_t uMaxVirtqPairs; /**< max_virtq_pairs */
176#endif
177} VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
178
179#define VIRTIONET_F_LINK_UP RT_BIT_16(1) /**< config status: Link is up */
180#define VIRTIONET_F_ANNOUNCE RT_BIT_16(2) /**< config status: Announce */
181
182/** @name VirtIO 1.0 NET Host Device device specific control types
183 * @{ */
184#define VIRTIONET_HDR_F_NEEDS_CSUM 1 /**< Packet needs checksum */
185#define VIRTIONET_HDR_GSO_NONE 0 /**< No Global Segmentation Offset */
186#define VIRTIONET_HDR_GSO_TCPV4 1 /**< Global Segment Offset for TCPV4 */
187#define VIRTIONET_HDR_GSO_UDP 3 /**< Global Segment Offset for UDP */
188#define VIRTIONET_HDR_GSO_TCPV6 4 /**< Global Segment Offset for TCPV6 */
189#define VIRTIONET_HDR_GSO_ECN 0x80 /**< Explicit Congestion Notification */
190/** @} */
191
192/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
193#pragma pack(1)
194struct virtio_net_hdr {
195 uint8_t uFlags; /**< flags */
196 uint8_t uGsoType; /**< gso_type */
197 uint16_t uHdrLen; /**< hdr_len */
198 uint16_t uGsoSize; /**< gso_size */
199 uint16_t uChksumStart; /**< Chksum_start */
200 uint16_t uChksumOffset; /**< Chksum_offset */
201 uint16_t uNumBuffers; /**< num_buffers */
202};
203#pragma pack()
204typedef virtio_net_hdr VIRTIONET_PKT_HDR_T, *PVIRTIONET_PKT_HDR_T;
205AssertCompileSize(VIRTIONET_PKT_HDR_T, 12);
206
207/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
208#pragma pack(1)
209struct virtio_net_ctrl_hdr {
210 uint8_t uClass; /**< class */
211 uint8_t uCmd; /**< command */
212 uint8_t uCmdSpecific; /**< command specific */
213};
214#pragma pack()
215typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
216
217typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
218
219/* Command entry fAck values */
220#define VIRTIONET_OK 0
221#define VIRTIONET_ERROR 1
222
223/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
224 * @{ */
225#define VIRTIONET_CTRL_RX 0 /**< Control class: Receive filtering */
226#define VIRTIONET_CTRL_RX_PROMISC 0 /**< Promiscuous mode */
227#define VIRTIONET_CTRL_RX_ALLMULTI 1 /**< All-multicast receive */
228#define VIRTIONET_CTRL_RX_ALLUNI 2 /**< All-unicast receive */
229#define VIRTIONET_CTRL_RX_NOMULTI 3 /**< No multicast receive */
230#define VIRTIONET_CTRL_RX_NOUNI 4 /**< No unicast receive */
231#define VIRTIONET_CTRL_RX_NOBCAST 5 /**< No broadcast receive */
232/** @} */
233
234typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
235typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
236typedef uint8_t VIRTIONET_CTRL_MAC_ENTRIES[][6];
237
238/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
239 * @{ */
240#define VIRTIONET_CTRL_MAC 1 /**< Control class: MAC address filtering */
241#define VIRTIONET_CTRL_MAC_TABLE_SET 0 /**< Set MAC table */
242#define VIRTIONET_CTRL_MAC_ADDR_SET 1 /**< Set default MAC address */
243/** @} */
244
245/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
246 * @{ */
247#define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */
248#define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */
249#define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */
250/** @} */
251
252/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
253 * @{ */
254#define VIRTIONET_CTRL_ANNOUNCE 3 /**< Control class: Gratuitous Packet Sending */
255#define VIRTIONET_CTRL_ANNOUNCE_ACK 0 /**< Gratuitous Packet Sending ACK */
256/** @} */
257
258struct virtio_net_ctrl_mq {
259 uint16_t uVirtqueuePairs; /**< virtqueue_pairs */
260};
261
262/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
263 * @{ */
264#define VIRTIONET_CTRL_MQ 4 /**< Control class: Receive steering */
265#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET 0 /**< Set number of TX/RX queues */
266#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN 1 /**< Minimum number of TX/RX queues */
267#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 /**< Maximum number of TX/RX queues */
268/** @} */
269
270uint64_t uOffloads; /**< offloads */
271
272/** @name Offload State Configuration Flags (VirtIO 1.0, 5.1.6.5.6.1)
273 * @{ */
274//#define VIRTIONET_F_GUEST_CSUM 1 /**< Guest offloads Chksum */
275//#define VIRTIONET_F_GUEST_TSO4 7 /**< Guest offloads TSO4 */
276//#define VIRTIONET_F_GUEST_TSO6 8 /**< Guest Offloads TSO6 */
277//#define VIRTIONET_F_GUEST_ECN 9 /**< Guest Offloads ECN */
278//#define VIRTIONET_F_GUEST_UFO 10 /**< Guest Offloads UFO */
279/** @} */
280
281/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
282 * @{ */
283#define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 /**< Control class: Offloads state configuration */
284#define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 /** Apply new offloads configuration */
285/** @} */
286
287
288/**
289 * Worker thread context, shared state.
290 */
291typedef struct VIRTIONETWORKER
292{
293 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
294} VIRTIONETWORKER;
295/** Pointer to a VirtIO SCSI worker. */
296typedef VIRTIONETWORKER *PVIRTIONETWORKER;
297
298/**
299 * Worker thread context, ring-3 state.
300 */
301typedef struct VIRTIONETWORKERR3
302{
303 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
304 bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
305 bool volatile fNotified; /**< Flags whether worker thread notified */
306} VIRTIONETWORKERR3;
307/** Pointer to a VirtIO SCSI worker. */
308typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
309
310/**
311 * VirtIO Host NET device state, shared edition.
312 *
313 * @extends VIRTIOCORE
314 */
315typedef struct VIRTIONET
316{
317 /** The core virtio state. */
318 VIRTIOCORE Virtio;
319
320 /** Virtio device-specific configuration */
321 VIRTIONET_CONFIG_T virtioNetConfig;
322
323 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
324 VIRTIONETWORKER aWorkers[VIRTIONET_MAX_QUEUES];
325
326 /** Track which VirtIO queues we've attached to */
327 bool afQueueAttached[VIRTIONET_MAX_QUEUES];
328
329 /** Device-specific spec-based VirtIO VIRTQNAMEs */
330 char aszVirtqNames[VIRTIONET_MAX_QUEUES][VIRTIO_MAX_QUEUE_NAME_SIZE];
331
332 /** Instance name */
333 char szInstanceName[16];
334
335 uint16_t cVirtqPairs;
336
337 uint16_t cVirtQueues;
338
339 uint64_t fNegotiatedFeatures;
340
341#ifdef VIRTIONET_TX_DELAY
342 /** Transmit Delay Timer. */
343 TMTIMERHANDLE hTxTimer;
344 uint32_t ui;
345 uint32_t uAvgDiff;
346 uint32_t uMinDiff;
347 uint32_t uMaxDiff;
348 uint64_t u64NanoTS;
349#else /* !VNET_TX_DELAY */
350 /** The event semaphore TX thread waits on. */
351 SUPSEMEVENT hTxEvent;
352#endif /* !VNET_TX_DELAY */
353
354 /** Indicates transmission in progress -- only one thread is allowed. */
355 uint32_t uIsTransmitting;
356
357 /** MAC address obtained from the configuration. */
358 RTMAC macConfigured;
359
360 /** Default MAC address which rx filtering accepts */
361 RTMAC rxFilterMacDefault;
362
363 /** True if physical cable is attached in configuration. */
364 bool fCableConnected;
365
366 /** virtio-net-1-dot-0 (in milliseconds). */
367 uint32_t cMsLinkUpDelay;
368
369 uint32_t alignment;
370
371 /** Number of packet being sent/received to show in debug log. */
372 uint32_t uPktNo;
373
374 /** N/A: */
375 bool volatile fMaybeOutOfSpace;
376
377 /** Flags whether VirtIO core is in ready state */
378 uint8_t fVirtioReady;
379
380 /** Resetting flag */
381 uint8_t fResetting;
382
383 /** Promiscuous mode -- RX filter accepts all packets. */
384 uint8_t fPromiscuous;
385
386 /** All multicast mode -- RX filter accepts all multicast packets. */
387 uint8_t fAllMulticast;
388
389 /** All unicast mode -- RX filter accepts all unicast packets. */
390 uint8_t fAllUnicast;
391
392 /** No multicast mode - Supresses multicast receive */
393 uint8_t fNoMulticat;
394
395 /** No unicast mode - Suppresses unicast receive */
396 uint8_t fNoUnicast;
397
398 /** No broadcast mode - Supresses broadcast receive */
399 uint8_t fNoBroadcast;
400
401 /** The number of actually used slots in aMacMulticastFilter. */
402 uint32_t cMulticastFilterMacs;
403
404 /** Array of MAC multicast addresses accepted by RX filter. */
405 RTMAC aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
406
407 /** The number of actually used slots in aMacUniicastFilter. */
408 uint32_t cUnicastFilterMacs;
409
410 /** Array of MAC unicast addresses accepted by RX filter. */
411 RTMAC aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
412
413 /** Bit array of VLAN filter, one bit per VLAN ID. */
414 uint8_t aVlanFilter[VIRTIONET_MAX_VID / sizeof(uint8_t)];
415
416 /* Receive-blocking-related fields ***************************************/
417
418 /** EMT: Gets signalled when more RX descriptors become available. */
419 SUPSEMEVENT hEventMoreRxDescAvail;
420
421 /** Handle of the I/O port range. */
422 IOMIOPORTHANDLE hIoPorts;
423
424 /** @name Statistic
425 * @{ */
426 STAMCOUNTER StatReceiveBytes;
427 STAMCOUNTER StatTransmitBytes;
428 STAMCOUNTER StatReceiveGSO;
429 STAMCOUNTER StatTransmitPackets;
430 STAMCOUNTER StatTransmitGSO;
431 STAMCOUNTER StatTransmitChksum;
432#ifdef VBOX_WITH_STATISTICS
433 STAMPROFILE StatReceive;
434 STAMPROFILE StatReceiveStore;
435 STAMPROFILEADV StatTransmit;
436 STAMPROFILE StatTransmitSend;
437 STAMPROFILE StatRxOverflow;
438 STAMCOUNTER StatRxOverflowWakeup;
439 STAMCOUNTER StatTransmitByNetwork;
440 STAMCOUNTER StatTransmitByThread;
441#endif
442} VIRTIONET;
443/** Pointer to the shared state of the VirtIO Host NET device. */
444typedef VIRTIONET *PVIRTIONET;
445
446/**
447 * VirtIO Host NET device state, ring-3 edition.
448 *
449 * @extends VIRTIOCORER3
450 */
451typedef struct VIRTIONETR3
452{
453 /** The core virtio ring-3 state. */
454 VIRTIOCORER3 Virtio;
455
456 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
457 VIRTIONETWORKERR3 aWorkers[VIRTIONET_MAX_QUEUES];
458
459 /** The device instance.
460 * @note This is _only_ for use when dealing with interface callbacks. */
461 PPDMDEVINSR3 pDevIns;
462
463 /** Status LUN: Base interface. */
464 PDMIBASE IBase;
465
466 /** Status LUN: LED port interface. */
467 PDMILEDPORTS ILeds;
468
469 /** Status LUN: LED connector (peer). */
470 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
471
472 /** Status: LED */
473 PDMLED led;
474
475 /** Attached network driver. */
476 R3PTRTYPE(PPDMIBASE) pDrvBase;
477
478 /** Network port interface (down) */
479 PDMINETWORKDOWN INetworkDown;
480
481 /** Network config port interface (main). */
482 PDMINETWORKCONFIG INetworkConfig;
483
484 /** Connector of attached network driver. */
485 R3PTRTYPE(PPDMINETWORKUP) pDrv;
486
487#ifndef VIRTIONET_TX_DELAY
488 R3PTRTYPE(PPDMTHREAD) pTxThread;
489#endif
490
491 /** Link Up(/Restore) Timer. */
492 TMTIMERHANDLE hLinkUpTimer;
493
494 /** Queue to send tasks to R3. - HC ptr */
495 R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
496
497 /** True if in the process of quiescing I/O */
498 uint32_t fQuiescing;
499
500 /** For which purpose we're quiescing. */
501 VIRTIOVMSTATECHANGED enmQuiescingFor;
502
503} VIRTIONETR3;
504/** Pointer to the ring-3 state of the VirtIO Host NET device. */
505typedef VIRTIONETR3 *PVIRTIONETR3;
506
507/**
508 * VirtIO Host NET device state, ring-0 edition.
509 */
510typedef struct VIRTIONETR0
511{
512 /** The core virtio ring-0 state. */
513 VIRTIOCORER0 Virtio;
514} VIRTIONETR0;
515/** Pointer to the ring-0 state of the VirtIO Host NET device. */
516typedef VIRTIONETR0 *PVIRTIONETR0;
517
518
519/**
520 * VirtIO Host NET device state, raw-mode edition.
521 */
522typedef struct VIRTIONETRC
523{
524 /** The core virtio raw-mode state. */
525 VIRTIOCORERC Virtio;
526} VIRTIONETRC;
527/** Pointer to the ring-0 state of the VirtIO Host NET device. */
528typedef VIRTIONETRC *PVIRTIONETRC;
529
530
531/** @typedef VIRTIONETCC
532 * The instance data for the current context. */
533typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
534
535/** @typedef PVIRTIONETCC
536 * Pointer to the instance data for the current context. */
537typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
538
539#ifdef IN_RING3 /* spans most of the file, at the moment. */
540
541
542DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis)
543{
544 for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
545 {
546 RTStrPrintf(pThis->aszVirtqNames[RXQIDX(qPairIdx)], VIRTIO_MAX_QUEUE_NAME_SIZE, "receiveq<%d>", qPairIdx);
547 RTStrPrintf(pThis->aszVirtqNames[TXQIDX(qPairIdx)], VIRTIO_MAX_QUEUE_NAME_SIZE, "transmitq<%d>", qPairIdx);
548 }
549 RTStrCopy(pThis->aszVirtqNames[CTRLQIDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "controlq");
550}
551
552
553DECLINLINE(void) virtioNetPrintFeatures(PVIRTIONET pThis, uint32_t fFeatures, const char *pcszText)
554{
555#ifdef LOG_ENABLED
556 static struct
557 {
558 uint32_t fMask;
559 const char *pcszDesc;
560 } const s_aFeatures[] =
561 {
562 { VIRTIONET_F_CSUM, "Host handles packets with partial checksum." },
563 { VIRTIONET_F_GUEST_CSUM, "Guest handles packets with partial checksum." },
564 { VIRTIONET_F_CTRL_GUEST_OFFLOADS, "Control channel offloads reconfiguration support." },
565 { VIRTIONET_F_MAC, "Host has given MAC address." },
566 { VIRTIONET_F_GUEST_TSO4, "Guest can receive TSOv4." },
567 { VIRTIONET_F_GUEST_TSO6, "Guest can receive TSOv6." },
568 { VIRTIONET_F_GUEST_ECN, "Guest can receive TSO with ECN." },
569 { VIRTIONET_F_GUEST_UFO, "Guest can receive UFO." },
570 { VIRTIONET_F_HOST_TSO4, "Host can receive TSOv4." },
571 { VIRTIONET_F_HOST_TSO6, "Host can receive TSOv6." },
572 { VIRTIONET_F_HOST_ECN, "Host can receive TSO with ECN." },
573 { VIRTIONET_F_HOST_UFO, "Host can receive UFO." },
574 { VIRTIONET_F_MRG_RXBUF, "Guest can merge receive buffers." },
575 { VIRTIONET_F_STATUS, "Configuration status field is available." },
576 { VIRTIONET_F_CTRL_VQ, "Control channel is available." },
577 { VIRTIONET_F_CTRL_RX, "Control channel RX mode support." },
578 { VIRTIONET_F_CTRL_VLAN, "Control channel VLAN filtering." },
579 { VIRTIONET_F_GUEST_ANNOUNCE, "Guest can send gratuitous packets." },
580 { VIRTIONET_F_MQ, "Host supports multiqueue with automatic receive steering." },
581 { VIRTIONET_F_CTRL_MAC_ADDR, "Set MAC address through control channel." }
582 };
583
584 Log3(("%s %s:\n", INSTANCE(pThis), pcszText));
585 for (unsigned i = 0; i < RT_ELEMENTS(s_aFeatures); ++i)
586 {
587 if (s_aFeatures[i].fMask & fFeatures)
588 Log3(("%s --> %s\n", INSTANCE(pThis), s_aFeatures[i].pcszDesc));
589 }
590#else /* !LOG_ENABLED */
591 RT_NOREF3(pThis, fFeatures, pcszText);
592#endif /* !LOG_ENABLED */
593}
594
595/*
596 * Checks whether negotiated features have required flag combinations.
597 * See VirtIO 1.0 specification, Section 5.1.3.1 */
598DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
599{
600 uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
601 || fFeatures & VIRTIONET_F_GUEST_TSO6
602 || fFeatures & VIRTIONET_F_GUEST_UFO;
603
604 uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
605 || fFeatures & VIRTIONET_F_HOST_TSO6
606 || fFeatures & VIRTIONET_F_HOST_UFO;
607
608 uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
609 || fFeatures & VIRTIONET_F_CTRL_VLAN
610 || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
611 || fFeatures & VIRTIONET_F_MQ
612 || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
613
614 if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
615 return false;
616
617 if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
618 return false;
619
620 if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
621 return false;
622
623 if ( fFeatures & VIRTIONET_F_GUEST_ECN
624 && !( fFeatures & VIRTIONET_F_GUEST_TSO4
625 || fFeatures & VIRTIONET_F_GUEST_TSO6))
626 return false;
627
628 if ( fFeatures & VIRTIONET_F_HOST_ECN
629 && !( fFeatures & VIRTIONET_F_HOST_TSO4
630 || fFeatures & VIRTIONET_F_HOST_TSO6))
631 return false;
632 return true;
633}
634
635/**
636 * @callback_method_impl{VIRTIOCORER3,pfnQueueNotified}
637 */
638static DECLCALLBACK(void) virtioNetR3Notified(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint16_t qIdx)
639{
640 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
641 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
642 PPDMDEVINS pDevIns = pThisCC->pDevIns;
643 AssertReturnVoid(qIdx < pThis->cVirtQueues);
644 PVIRTIONETWORKER pWorker = &pThis->aWorkers[qIdx];
645 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[qIdx];
646
647#ifdef LOG_ENABLED
648 RTLogFlush(NULL);
649#endif
650
651 Log6Func(("%s has available data\n", VIRTQNAME(qIdx)));
652 /* Wake queue's worker thread up if sleeping */
653 if (!ASMAtomicXchgBool(&pWorkerR3->fNotified, true))
654 {
655 if (ASMAtomicReadBool(&pWorkerR3->fSleeping))
656 {
657 Log6Func(("waking %s worker.\n", VIRTQNAME(qIdx)));
658 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
659 AssertRC(rc);
660 }
661 }
662}
663
664
665/*********************************************************************************************************************************
666* Virtio Net config. *
667*********************************************************************************************************************************/
668
669/**
670 * Resolves to boolean true if uOffset matches a field offset and size exactly,
671 * (or if 64-bit field, if it accesses either 32-bit part as a 32-bit access)
672 * Assumption is this critereon is mandated by VirtIO 1.0, Section 4.1.3.1)
673 * (Easily re-written to allow unaligned bounded access to a field).
674 *
675 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
676 * @result - true or false
677 */
678#define MATCH_NET_CONFIG(member) \
679 ( ( RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member) == 8 \
680 && ( offConfig == RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) \
681 || offConfig == RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) + sizeof(uint32_t)) \
682 && cb == sizeof(uint32_t)) \
683 || ( offConfig == RT_UOFFSETOF(VIRTIONET_CONFIG_T, member) \
684 && cb == RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member)) )
685
686#ifdef LOG_ENABLED
687# define LOG_NET_CONFIG_ACCESSOR(member) \
688 virtioCoreLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIONET_CONFIG_T, member), \
689 pv, cb, offIntra, fWrite, false, 0);
690#else
691# define LOG_NET_CONFIG_ACCESSOR(member) do { } while (0)
692#endif
693
694#define NET_CONFIG_ACCESSOR(member) \
695 do \
696 { \
697 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIONET_CONFIG_T, member); \
698 if (fWrite) \
699 memcpy((char *)&pThis->virtioNetConfig.member + offIntra, pv, cb); \
700 else \
701 memcpy(pv, (const char *)&pThis->virtioNetConfig.member + offIntra, cb); \
702 LOG_NET_CONFIG_ACCESSOR(member); \
703 } while(0)
704
705#define NET_CONFIG_ACCESSOR_READONLY(member) \
706 do \
707 { \
708 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIONET_CONFIG_T, member); \
709 if (fWrite) \
710 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
711 else \
712 { \
713 memcpy(pv, (const char *)&pThis->virtioNetConfig.member + offIntra, cb); \
714 LOG_NET_CONFIG_ACCESSOR(member); \
715 } \
716 } while(0)
717
718
719#if 0
720
721static int virtioNetR3CfgAccessed(PVIRTIONET pThis, uint32_t offConfig, void *pv, uint32_t cb, bool fWrite)
722{
723 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
724
725 if (MATCH_NET_CONFIG(uMacAddress))
726 NET_CONFIG_ACCESSOR_READONLY(uMacAddress);
727#if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_STATUS
728 else
729 if (MATCH_NET_CONFIG(uStatus))
730 NET_CONFIG_ACCESSOR_READONLY(uStatus);
731#endif
732#if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_MQ
733 else
734 if (MATCH_NET_CONFIG(uMaxVirtqPairs))
735 NET_CONFIG_ACCESSOR_READONLY(uMaxVirtqPairs);
736#endif
737 else
738 {
739 LogFunc(("Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n", offConfig, offConfig, cb));
740 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
741 }
742 return VINF_SUCCESS;
743}
744
745#undef NET_CONFIG_ACCESSOR_READONLY
746#undef NET_CONFIG_ACCESSOR
747#undef LOG_ACCESSOR
748#undef MATCH_NET_CONFIG
749
750/**
751 * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
752 */
753static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
754{
755 return virtioNetR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
756}
757
758/**
759 * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
760 */
761static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
762{
763 return virtioNetR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
764}
765
766#endif
767
768
769/*********************************************************************************************************************************
770* Misc *
771*********************************************************************************************************************************/
772
773/**
774 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
775 */
776static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
777{
778 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
779
780 /* Parse arguments. */
781 RT_NOREF2(pThis, pszArgs); //bool fVerbose = pszArgs && strstr(pszArgs, "verbose") != NULL;
782
783 /* Show basic information. */
784 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
785 pDevIns->pReg->szName,
786 pDevIns->iInstance);
787}
788
789
790/*********************************************************************************************************************************
791* Saved state *
792*********************************************************************************************************************************/
793
794/**
795 * @callback_method_impl{FNSSMDEVLOADEXEC}
796 */
797static DECLCALLBACK(int) virtioNetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
798{
799 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
800 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
801 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
802
803 RT_NOREF(pThisCC);
804 LogFunc(("LOAD EXEC!!\n"));
805
806 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
807 AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVED_STATE_VERSION,
808 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
809
810 virtioNetR3SetVirtqNames(pThis);
811 for (int qIdx = 0; qIdx < pThis->cVirtQueues; qIdx++)
812 pHlp->pfnSSMGetBool(pSSM, &pThis->afQueueAttached[qIdx]);
813
814 /*
815 * Call the virtio core to let it load its state.
816 */
817 int rc = virtioCoreR3LoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
818
819 /*
820 * Nudge queue workers
821 */
822 for (int qIdx = 0; qIdx < pThis->cVirtqPairs; qIdx++)
823 {
824 if (pThis->afQueueAttached[qIdx])
825 {
826 LogFunc(("Waking %s worker.\n", VIRTQNAME(qIdx)));
827 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[qIdx].hEvtProcess);
828 AssertRCReturn(rc, rc);
829 }
830 }
831 return rc;
832}
833
834/**
835 * @callback_method_impl{FNSSMDEVSAVEEXEC}
836 */
837static DECLCALLBACK(int) virtioNetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
838{
839 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
840 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
841 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
842
843 RT_NOREF(pThisCC);
844
845 LogFunc(("SAVE EXEC!!\n"));
846
847 for (int qIdx = 0; qIdx < pThis->cVirtQueues; qIdx++)
848 pHlp->pfnSSMPutBool(pSSM, pThis->afQueueAttached[qIdx]);
849
850 /*
851 * Call the virtio core to let it save its state.
852 */
853 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
854}
855
856
857/*********************************************************************************************************************************
858* Device interface. *
859*********************************************************************************************************************************/
860
861/**
862 * @callback_method_impl{FNPDMDEVASYNCNOTIFY}
863 */
864static DECLCALLBACK(bool) virtioNetR3DeviceQuiesced(PPDMDEVINS pDevIns)
865{
866 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
867 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
868
869// if (ASMAtomicReadu(&pThis->cActiveReqs))
870// return false;
871
872 LogFunc(("Device I/O activity quiesced: %s\n",
873 virtioCoreGetStateChangeText(pThisCC->enmQuiescingFor)));
874
875 virtioCoreR3VmStateChanged(&pThis->Virtio, pThisCC->enmQuiescingFor);
876
877 pThis->fResetting = false;
878 pThisCC->fQuiescing = false;
879
880 return true;
881}
882
883/**
884 * Worker for virtioNetR3Reset() and virtioNetR3SuspendOrPowerOff().
885 */
886static void virtioNetR3QuiesceDevice(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmQuiescingFor)
887{
888 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
889 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
890
891 RT_NOREF(pThis);
892
893 /* Prevent worker threads from removing/processing elements from virtq's */
894 pThisCC->fQuiescing = true;
895 pThisCC->enmQuiescingFor = enmQuiescingFor;
896
897 PDMDevHlpSetAsyncNotification(pDevIns, virtioNetR3DeviceQuiesced);
898
899 /* If already quiesced invoke async callback. */
900// if (!ASMAtomicReadu(&pThis->cActiveReqs))
901// PDMDevHlpAsyncNotificationCompleted(pDevIns);
902}
903
904/**
905 * @interface_method_impl{PDMDEVREGR3,pfnReset}
906 */
907static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
908{
909 LogFunc(("\n"));
910 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
911 pThis->fResetting = true;
912 virtioNetR3QuiesceDevice(pDevIns, kvirtIoVmStateChangedReset);
913}
914
915/**
916 * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
917 */
918static DECLCALLBACK(void) virtioNetR3SuspendOrPowerOff(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmType)
919{
920 LogFunc(("\n"));
921
922 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
923 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
924
925 RT_NOREF2(pThis, pThisCC);
926
927 /* VM is halted, thus no new I/O being dumped into queues by the guest.
928 * Workers have been flagged to stop pulling stuff already queued-up by the guest.
929 * Now tell lower-level to to suspend reqs (for example, DrvVD suspends all reqs
930 * on its wait queue, and we will get a callback as the state changes to
931 * suspended (and later, resumed) for each).
932 */
933
934 virtioNetR3QuiesceDevice(pDevIns, enmType);
935}
936
937/**
938 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
939 */
940static DECLCALLBACK(void) virtioNetR3PowerOff(PPDMDEVINS pDevIns)
941{
942 LogFunc(("\n"));
943 virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedPowerOff);
944}
945
946/**
947 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
948 */
949static DECLCALLBACK(void) virtioNetR3Suspend(PPDMDEVINS pDevIns)
950{
951 LogFunc(("\n"));
952 virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedSuspend);
953}
954
955/**
956 * @interface_method_impl{PDMDEVREGR3,pfnResume}
957 */
958static DECLCALLBACK(void) virtioNetR3Resume(PPDMDEVINS pDevIns)
959{
960 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
961 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
962 LogFunc(("\n"));
963
964 pThisCC->fQuiescing = false;
965
966 /* Wake worker threads flagged to skip pulling queue entries during quiesce
967 * to ensure they re-check their queues. Active request queues may already
968 * be awake due to new reqs coming in.
969 */
970/*
971 for (uint16_t qIdx = 0; qIdx < VIRTIONET_REQ_QUEUE_CNT; qIdx++)
972 {
973 if (ASMAtomicReadBool(&pThisCC->aWorkers[qIdx].fSleeping))
974 {
975 Log6Func(("waking %s worker.\n", VIRTQNAME(qIdx)));
976 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[qIdx].hEvtProcess);
977 AssertRC(rc);
978 }
979 }
980*/
981 /* Ensure guest is working the queues too. */
982 virtioCoreR3VmStateChanged(&pThis->Virtio, kvirtIoVmStateChangedResume);
983}
984
985/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
986#pragma pack(1)
987struct virtio_net_hdr {
988 uint8_t uFlags; /**< flags */
989 uint8_t uGsoType; /**< gso_type */
990 uint16_t uHdrLen; /**< hdr_len */
991 uint16_t uGsoSize; /**< gso_size */
992 uint16_t uChksumStart; /**< csum_start */
993 uint16_t uChksumOffset; /**< csum_offset */
994 uint16_t uNumBuffers; /**< num_buffers */
995};
996#pragma pack()
997typedef virtio_net_hdr VIRTIONET_PKT_HDR_T, *PVIRTIONET_PKT_HDR_T;
998AssertCompileSize(VIRTIONET_PKT_HDR_T, 12);
999
1000/**
1001 * Determines if the packet is to be delivered to upper layer.
1002 *
1003 * @returns true if packet is intended for this node.
1004 * @param pThis Pointer to the state structure.
1005 * @param pvBuf The ethernet packet.
1006 * @param cb Number of bytes available in the packet.
1007 */
1008static bool vnetR3AddressFilter(PVNETSTATE pThis, const void *pvBuf, size_t cb)
1009{
1010 if (pThis->fPromiscuous)
1011 return true;
1012
1013 /* Ignore everything outside of our VLANs */
1014 uint16_t *uPtr = (uint16_t*)pvBuf;
1015 /* Compare TPID with VLAN Ether Type */
1016 if ( uPtr[6] == RT_H2BE_u(0x8100)
1017 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_u(uPtr[7]) & 0xFFF))
1018 {
1019 Log4(("%s vnetR3AddressFilter: not our VLAN, returning false\n", INSTANCE(pThis)));
1020 return false;
1021 }
1022
1023 if (vnetR3IsBroadcast(pvBuf))
1024 return true;
1025
1026 if (pThis->fAllMulti && vnetR3IsMulticast(pvBuf))
1027 return true;
1028
1029 if (!memcmp(pThis->config.mac.au, pvBuf, sizeof(RTMAC)))
1030 return true;
1031 Log4(("%s vnetR3AddressFilter: %RTmac (conf) != %RTmac (dest)\n", INSTANCE(pThis), pThis->config.mac.au, pvBuf));
1032
1033 for (unsigned i = 0; i < pThis->cMacFilterEntries; i++)
1034 if (!memcmp(&pThis->aMacFilter[i], pvBuf, sizeof(RTMAC)))
1035 return true;
1036
1037 Log2(("%s vnetR3AddressFilter: failed all tests, returning false, packet dump follows:\n", INSTANCE(pThis)));
1038 vnetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1039
1040 return false;
1041}
1042
1043/**
1044 * Pad and store received packet.
1045 *
1046 * @remarks Make sure that the packet appears to upper layer as one coming
1047 * from real Ethernet: pad it and insert FCS.
1048 *
1049 * @returns VBox status code.
1050 * @param pDevIns The device instance.
1051 * @param pThis The virtio-net shared instance data.
1052 * @param pvBuf The available data.
1053 * @param cb Number of bytes available in the buffer.
1054 * @thread RX
1055 */
1056static int virtioNetR3HandleRxPacket(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1057 const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso)
1058{
1059 VIRTIONET_PKT_HDR_T rxPktHdr;
1060
1061 if (pGso)
1062 {
1063 Log2Func(("%s gso type=%x cbPktHdrsTotal=%u cbPktHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
1064 pThis->szInstanceName, pGso->uType, pGso->cbHdrsTotal,
1065 pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1066
1067 rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
1068 switch (pGso->uType)
1069 {
1070 case PDMNETWORKGSOTYPE_IPV4_TCP:
1071 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
1072 rxPktHdr.uCSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1073 break;
1074 case PDMNETWORKGSOTYPE_IPV6_TCP:
1075 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
1076 rxPktHdr.uCSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
1077 break;
1078 case PDMNETWORKGSOTYPE_IPV4_UDP:
1079 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
1080 rxPktHdr.uCSumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
1081 break;
1082 default:
1083 return VERR_INVALID_PARAMETER;
1084 }
1085 rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
1086 rxPktHdr.uGSOSize = pGso->cbMaxSeg;
1087 rxPktHdr.uCSumStart = pGso->offHdr2;
1088 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
1089 }
1090 else
1091 {
1092 rxPktHdr.uFlags = 0;
1093 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_NONE;
1094 }
1095
1096 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1097
1098 PRTSGBUF pSegsBuf;
1099 PRTSGSEG paSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 2);
1100 AssertReturn(paSegs, VERR_NO_MEMORY);
1101 RTSgBufInit(pSegsBuf, paSegs, cSegs);
1102
1103 uint16_t *pPhysPktHdrNumBufs, cDescs = 0;
1104
1105 uint8_t fFirstIteration = true;
1106 for (uint32_t uOffset = 0; uOffset < cb; fFirstIteration = false)
1107 {
1108 PVIRTIO_DESC_CHAIN_T pDescChain;
1109 int rc = virtioCoreR3QueueGet(pDevIns, &pThis->Virtio, RXQIDX(0), &pDescChain, true);
1110
1111 AssertRC(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, rc);
1112
1113 /** @todo: Find a better way to deal with this */
1114 AssertMsgReturn(rc == VINF_SUCCESS && pDescChain->cbPhysSend,
1115 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
1116 VERR_INTERNAL_ERROR);
1117
1118 AssertMsgReturn(pDescChain->cbPhysReturn >= sizeof(VIRTIONET_PKT_HDR_T),
1119 ("Desc chain's phys segs have insufficient space for pkt header!\n"),
1120 VERR_INTERNAL_ERROR);
1121
1122 uint32_t cbDescChainLeft = pDescChain->cbPhysSend;
1123
1124 uint16_t cSegs = 0;
1125 if (fFirstIteration)
1126 {
1127 /* Lead with packet header */
1128 paSegs[cSegs].cbSeg = sizeof(VIRTIONET_PKT_HDR_T);
1129 paSegs[cSegs].pvSeg = RTMemAlloc(paSegs[0].cb);
1130 AssertReturn(paSegs[0].pvSeg, VERR_NO_MEMORY);
1131 cbDescChainLeft -= paReqSegs[0].cb;
1132 *pPhysPktHdrNumBufs = ((uint8_t *)paSegs[cSegs].pvSeg)
1133 + RT_UOFFSETOF(VIRTIONET_PKT_HDR_T, uNumBuffers);
1134 cSegs++;
1135 }
1136
1137 /* Append remaining Rx pkt or as much current desc chain has room for */
1138 uint32_t uSize = RT_MIN(cb, cbDescChainLeft);
1139 paSegs[cSegs].cbSeg = uSize;
1140 paSegs[cSegs++].pvSeg = ((uint8_t)pvBuf) + uOffset;
1141 uOffset += uSize;
1142 cDescs++;
1143
1144 virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, RXQIDX(0), pSegsBuf, pDescChain, true);
1145
1146 if (FEATURE_DISABLED(MRG_RXBUF))
1147 break;
1148 }
1149
1150 /* Fix up pkt hdr (already in guest phys. memory) with number of descriptors to send */
1151
1152 int rc = PDMDevHlpPCIPhysWrite(pDevIns, pPhysPktHdrNumBuffers, cDescs, sizeof(cDescs));
1153 AssertMsgRCReturn(rc, "Failure updating descriptor count in pkt hdr in guest physical memory\n");
1154
1155 virtioCoreQueueSync(pDevIns, &pThis->Virtio, RXQIDX(0));
1156
1157 for (int i = 0; i < 2; i++)
1158 RTMemFree(paSegs[i].pvSeg);
1159 RTMemFree(paSegs);
1160 RTMemFree(pSegsBuf);
1161
1162 if (uOffset < cb)
1163 {
1164 Log(("%s vnetR3HandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n", INSTANCE(pThis), cb));
1165 return VERR_TOO_MUCH_DATA;
1166 }
1167
1168 return VINF_SUCCESS;
1169}
1170
1171/**
1172 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
1173 */
1174static DECLCALLBACK(int) virtioNetR3ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf,
1175 size_t cb, PCPDMNETWORKGSO pGso)
1176{
1177 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, PVIRTIONETCC, INetworkDown);
1178 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1179 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1180
1181 if (pGso)
1182 {
1183 uint32_t uFeatures = pThis->VPCI.uGuestFeatures;
1184
1185 switch (pGso->uType)
1186 {
1187 case PDMNETWORKGSOTYPE_IPV4_TCP:
1188 uFeatures &= VIRTIONET_F_GUEST_TSO4;
1189 break;
1190 case PDMNETWORKGSOTYPE_IPV6_TCP:
1191 uFeatures &= VIRTIONET_F_GUEST_TSO6;
1192 break;
1193 case PDMNETWORKGSOTYPE_IPV4_UDP:
1194 case PDMNETWORKGSOTYPE_IPV6_UDP:
1195 uFeatures &= VIRTIONET_F_GUEST_UFO;
1196 break;
1197 default:
1198 uFeatures = 0;
1199 break;
1200 }
1201 if (!uFeatures)
1202 {
1203 Log2Func((GSO type (0x%x) not supported\n", pThis->szInstanceName, pGso->uType));
1204 return VERR_NOT_SUPPORTED;
1205 }
1206 }
1207
1208 Log2Func(("pvBuf=%p cb=%u pGso=%p\n", pThis->szInstanceName, pvBuf, cb, pGso));
1209
1210 int rc = virtioR3CanReceive(pDevIns, pThis, pThisCC);
1211 if (RT_FAILURE(rc))
1212 return rc;
1213
1214 /* Drop packets if VM is not running or cable is disconnected. */
1215 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1216 if (( enmVMState != VMSTATE_RUNNING
1217 && enmVMState != VMSTATE_RUNNING_LS)
1218 || !(pThis->virtioNetConfig.uStatus & VIRTIONET_S_LINK_UP))
1219 return VINF_SUCCESS;
1220
1221 STAM_PROFILE_START(&pThis->StatReceive, a);
1222 virtioNetR3SetReadLed(&pThisCC, true);
1223 if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
1224 {
1225 rc = virtioNetCsRxEnter(pThis, VERR_SEM_BUSY);
1226 if (RT_SUCCESS(rc))
1227 {
1228 rc = virtioNetR3HandleRxPacket(pDevIns, pThis, pThisCC, pvBuf, cb, pGso);
1229 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
1230 virtioNetCsRxLeave(pThis);
1231 }
1232 }
1233 virtioNetR3SetReadLed(&pThisCC, false);
1234 STAM_PROFILE_STOP(&pThis->StatReceive, a);
1235 return rc;
1236}
1237
1238
1239DECLINLINE(uint16_t) virtioNetR3Checkum16(const void *pvBuf, size_t cb)
1240{
1241 uint32_t chksum = 0;
1242 uint16_t *pu = (uint16_t *)pvBuf;
1243
1244 while (cb > 1)
1245 {
1246 chksum += *pu++;
1247 cb -= 2;
1248 }
1249 if (cb)
1250 chksum += *(uint8_t*)pu;
1251 while (chksum >> 16)
1252 chksum = (chksum >> 16) + (chksum & 0xFFFF);
1253 return ~chksum;
1254}
1255
1256DECLINLINE(void) virtioNetR3CompleteChecksum(uint8_t *pBuf, size_t cbSize, uint16_t uStart, uint16_t uOffset)
1257{
1258 AssertReturnVoid(uStart < cbSize);
1259 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cbSize);
1260 *(uint16_t *)(pBuf + uStart + uOffset) = vnetR3Chksum16(pBuf + uStart, cbSize - uStart);
1261}
1262
1263/* Read physical bytes from the out segment(s) of descriptor chain */
1264static void virtioNetR3PullChain(PVIRTIO_DESC_CHAIN_T pDecChain, void *pv, uint16_t cb)
1265{
1266 uint8_t *pb = (uint8_t *)pv;
1267 for (size_t cb = RT_MIN(pDescChain->cbPhysSend, sizeof(VIRTIONET_PKT_HDR_T)); cb; )
1268 {
1269 size_t cbSeg = cb;
1270 RTGCPHYS GCPhys = virtioCoreSgBufGetNextSegment(pDescChain->pSgPhysSend, &cbSeg);
1271 PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pb, cbSeg);
1272 pb += cbSeg;
1273 cb -= cbSeg;
1274 }
1275}
1276static bool virtioNetR3ReadHeader(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONET_PKT_HDR_T pPktHdr, uint32_t cbMax)
1277{
1278 int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pPktHdr, sizeof(*pPktHdr));
1279 if (RT_FAILURE(rc))
1280 return false;
1281
1282 Log4(("virtio-net: header flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x cb=%x\n",
1283 pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen, pPktHdr->uGSOSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbMax));
1284
1285 if (pPktHdr->uGsoType)
1286 {
1287 uint32_t uMinHdrSize;
1288
1289 /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
1290 if ( RT_UNLIKELY(!(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM))
1291 | RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)))
1292 return false;
1293
1294 switch (pPktHdr->uGsoType)
1295 {
1296 case VIRTIONET_HDR_GSO_TCPV4:
1297 case VIRTIONET_HDR_GSO_TCPV6:
1298 uMinHdrSize = sizeof(RTNETTCP);
1299 break;
1300 case VIRTIONET_HDR_GSO_UDP:
1301 uMinHdrSize = 0;
1302 break;
1303 default:
1304 return false;
1305 }
1306 /* Header + MSS must not exceed the packet size. */
1307 if (RT_UNLIKELY(uMinHdrSize + pPktHdr->uChksumStart + pPktHdr->uGSOSize > cbMax))
1308 return false;
1309 }
1310 /* Checksum must fit into the frame (validating both checksum fields). */
1311 if (( pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
1312 && sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset > cbMax)
1313 return false;
1314 Log4func(("returning true\n"));
1315 return true;
1316}
1317
1318static uint8_t virtioNetR3CtrlRx(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1319 PVIRTIONET_PKT_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1320{
1321#define LOG_VIRTIONET_FLAG(field) LogFunc(("%s = %d\n", #field, pThis->field));
1322
1323 LogFunc((""));
1324 switch(pCtrlPktHdr->uCmd)
1325 {
1326 case VIRTIONET_CTRL_RX_PROMISC:
1327 break;
1328 case VIRTIONET_CTRL_RX_ALLMULTI:
1329 break;
1330 case VIRTIONET_CTRL_RX_ALLUNI:
1331 /* fallthrough */
1332 case VIRTIONET_CTRL_RX_NOMULTI:
1333 /* fallthrough */
1334 case VIRTIONET_CTRL_RX_NOUNI:
1335 /* fallthrough */
1336 case VIRTIONET_CTRL_RX_NOBCAST:
1337 AssertMsgReturn(fFeatures & VIRTIONET_F_CTRL_RX_EXTRA,
1338 ("CTRL "extra" cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
1339 VIRTIONET_ERROR);
1340 /* fall out */
1341 }
1342
1343 uint8_t fOn, fPromiscChanged = false;
1344 virtioNetR3PullChain(pDescChain, &fOn, RT_MIN(pDescChain->cbPhysSend, sizeof(fOn)));
1345
1346 switch(pCtrlPktHdr->uCmd)
1347 {
1348 case VIRTIONET_CTRL_RX_PROMISC:
1349 pThis->fPromiscuous = !!fOn;
1350 fPromiscChanged = true;
1351 LOG_VIRTIONET_FLAG(fPromiscuous)
1352 break;
1353 case VIRTIONET_CTRL_RX_ALLMULTI:
1354 pThis->fAllMulticast = !!fOn;
1355 fPromiscChanged = true;
1356 LOG_VIRTIONET_FLAG(fAllMulticast);
1357 break;
1358 case VIRTIONET_CTRL_RX_ALLUNI:
1359 pThis->fAllUnicast = !!fOn;
1360 LOG_VIRTIONET_FLAG(fAllUnicast);
1361 break;
1362 case VIRTIONET_CTRL_RX_NOMULTI:
1363 pThis->fNoMulticast = !!fOn;
1364 LOG_VIRTIONET_FLAG(fNoMulticast);
1365 break;
1366 case VIRTIONET_CTRL_RX_NOUNI:
1367 pThis->fNoUnicast = !!fOn;
1368 LOG_VIRTIONET_FLAG(fNoUnicast);
1369 break;
1370 case VIRTIONET_CTRL_RX_NOBCAST:
1371 pThis->fNoBroadcast = !!fOn;
1372 LOG_VIRTIONET_FLAG(fNoBroadcast);
1373 break;
1374 }
1375
1376 if (pThisCC->pDrv && fPromiscChanged)
1377 {
1378 uint8_t fPromiscuous = pThis->fPromiscuous | pThis->fAllMulticast
1379 LogFunc(("Setting promiscuous state to %d\n", fPromiscuous));
1380 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, fPromiscuous);
1381 }
1382
1383 return VIRTIONET_OK;
1384}
1385
1386static uint8_t virtioNetR3CtrlMac(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1387 PVIRTIONET_PKT_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1388{
1389#define ASSERT_CTRL_ADDR_SET(v) \
1390 AssertMsgReturn((v), ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd"), VIRTIONET_ERROR);
1391
1392#define ASSERT_CTRL_TABLE_SET(v) \
1393 AssertMsgReturn((v), ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd"), VIRTIONET_ERROR);
1394
1395 int cbRemaining = pDescChain.cbPhysSend - sizeof(*pCtrlPktHdr);
1396
1397 switch(pCtrlPktHder->uCmd)
1398 {
1399 case VIRTIONET_CTRL_MAC_ADDR_SET:
1400 {
1401 /* Set default Rx filter MAC */
1402 ASSERT_CTRL_ADDR_SET(cbRemaining >= sizeof(VIRTIONET_CTRL_MAC_TABLE_LEN));
1403 virtioNetR3PullChain(pDescChain, &pThis->rxFilterMacDefault, sizeof(VIRTIONET_CTRL_MAC_TABLE_LEN));
1404 break;
1405 }
1406 case VIRTIONET_CTRL_MAC_TABLE_SET:
1407 {
1408 VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
1409
1410 /* Load unicast MAC filter table */
1411 ASSERT_CTRL_TABLE_SET(cbRemaining >= sizeof(cMacs));
1412 virtioNetR3PullChain(pDescChain, &cMacs, sizeof(cMacs));
1413 cbRemaining -= sizeof(cMacs);
1414 uint32_t cbMacs = cMacs * sizeof(RTMAC);
1415 ASSERT_CTRL_TABLE_SET(cbRemaining >= cbMacs);
1416 virtioNetR3PullChain(pDescChain, &pThis->aMacUnicastFilter, cbMacs);
1417 cbRemaining -= cbMacs;
1418 pThis->cUnicastFilterMacs = cMacs;
1419
1420 /* Load multicast MAC filter table */
1421 ASSERT_CTRL_TABLE_SET(cbRemaining >= sizeof(cMacs));
1422 virtioNetR3PullChain(pDescChain, &cMacs, sizeof(cMacs));
1423 cbRemaining -= sizeof(cMacs);
1424 cbMacs = cMacs * sizeof(RTMAC);
1425 ASSERT_CTRL_TABLE_SET(cbRemaining >= cbMacs);
1426 virtioNetR3PullChain(pDescChain, &pThis->aMacMulticastFilter, cbMacs);
1427 cbRemaining -= cbMacs;
1428 pThis->cMulticastFilterMacs = cMacs;
1429
1430#ifdef LOG_ENABLED
1431 LogFunc(("%s: unicast MACs:\n", pThis->szInstanceName)));
1432 for(unsigned i = 0; i < nMacs; i++)
1433 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
1434
1435 LogFunc(("%s: multicast MACs:\n", pThis->szInstanceName)));
1436 for(unsigned i = 0; i < nMacs; i++)
1437 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
1438#endif
1439
1440 }
1441 }
1442 return VIRTIONET_OK;
1443}
1444
1445static uint8_t virtioNetR3CtrlVlan(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1446 PVIRTIONET_PKT_HDR_T pCtrlPktHdr, PVIRTIO_DESC_CHAIN_T pDescChain)
1447{
1448 uint16_t uVlanId;
1449 int cbRemaining = pDescChain.cbPhysSend - sizeof(*pCtrlPktHdr);
1450 AssertMsgReturn(cbRemaining > sizeof(uVlanId),
1451 ("DESC chain too small for VIRTIO_NET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
1452 virtioNetR3PullChain(pDescChain, &uVlanId, sizeof(uVlanId));
1453 AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
1454 ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInstanceName, uVlanId), VIRTIONET_ERROR);
1455 LogFunc(("%s: uCommand=%u VLAN ID=%u\n", pThis->szInstanceName, pCtrlPktHdr->uCmd, uVlanId));
1456 switch (pCtrlPktHdr->uCmd)
1457 {
1458 case VIRTIONET_CTRL_VLAN_ADD:
1459 ASMBitSet(pThis->aVlanFilter, uVlanId);
1460 break;
1461 case VIRTIONET_CTRL_VLAN_DEL:
1462 ASMBitClear(pThis->aVlanFilter, uVlanId);
1463 break;
1464 default:
1465 return VIRTIONET_ERROR;
1466 }
1467 return VIRTIONET_OK;
1468}
1469
1470static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
1471 PVIRTIO_DESC_CHAIN_T pDescChain)
1472{
1473 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1474 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1475
1476
1477 if (pDescChain->cbPhysSend < 2)
1478 {
1479 LogFunc(("ctrl packet from guest driver incomplete. Skipping ctrl cmd\n"));
1480 return;
1481 }
1482 else if (pDescChain->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
1483 {
1484 LogFunc(("Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n"));
1485 return;
1486 }
1487
1488 /*
1489 * Allocate buffer and read in the control command
1490 */
1491 PVIRTIONET_PKT_HDR_T pCtrlPktHdr = (PVIRTIONET_PKT_HDR_T)RTMemAllocZ(sizeof(VIRTIONET_PKT_HDR_T));
1492 AssertPtrReturn(pCtrlPktHdr, VERR_NO_MEMORY /*ignored*/);
1493
1494 AssertMsgReturnVoid(pDescChain >= sizeof(*pCtrlPktHdr), ("DESC chain too small for CTRL pkt header"));
1495 virtioNetR3PullChain(pDescChain, pCtrlPktHdr, SIZEOF_SEND(pDescChain, VIRTIONET_PKT_HDR_T));
1496
1497 uint8_t uAck;
1498 switch (pCtrlPktHdr->uClass)
1499 {
1500 case VIRTIONET_CTRL_RX:
1501 uAck = virtioNetR3CtrlRx(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
1502 break;
1503 case VIRTIONET_CTRL_MAC:
1504 uAck = virtioNetR3CtrlMac(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
1505 break;
1506 case VIRTIONET_CTRL_VLAN:
1507 uAck = virtioNetR3CtrlVlan(pDevIns, pThis, pThisCC, pCtrlPktHdr, pDescChain);
1508 break;
1509 default:
1510 uAck = VIRTIONET_ERROR;
1511 }
1512
1513 PRTSGSEG paReqSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 2);
1514 AssertReturn(paReqSegs, VERR_NO_MEMORY);
1515
1516 RTSGSEG aSegs[] = { { &uAck, sizeof(uAck) } };
1517 memcpy(paReqSegs, aSegs, sizeof(aSegs));
1518
1519 PRTSGBUF pReqSegBuf = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
1520 AssertReturn(pReqSegBuf, VERR_NO_MEMORY);
1521
1522 /* Copy segment data to malloc'd memory to avoid stack out-of-scope errors sanitizer doesn't detect */
1523 for (int i = 0; i < cSegs; i++)
1524 {
1525 void *pv = paReqSegs[i].pvSeg;
1526 paReqSegs[i].pvSeg = RTMemAlloc(paReqSegs[i].cbSeg);
1527 AssertReturn(paReqSegs[i].pvSeg, VERR_NO_MEMORY);
1528 memcpy(paReqSegs[i].pvSeg, pv, paReqSegs[i].cbSeg);
1529 }
1530
1531 RTSgBufInit(pReqSegBuf, paReqSegs, cSegs);
1532
1533 virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, qIdx, pReqSegBuf, pDescChain, true);
1534 virtioCoreQueueSync(pDevIns, &pThis->Virtio, qIdx);
1535
1536 for (int i = 0; i < cSegs; i++)
1537 RTMemFree(paReqSegs[i].pvSeg);
1538
1539 RTMemFree(paReqSegs);
1540 RTMemFree(pReqSegBuf);
1541
1542 LogFunc(("Processed ctrl message class/cmd/subcmd = %u/%u/%u. Ack=%u.\n",
1543 pCtrlPktHdr.uClass, pCtrlPktHdr.uCmd, pCtrlPktHdr.uCmdSpecific, uAck);
1544
1545}
1546
1547
1548static void virtioNetR3Transmit(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1549{
1550 RT_NOREF5(pDevIns, pThis, pThisCC, qIdx, pDescChain);
1551}
1552 static void virtioNetR3Receive(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1553{
1554 RT_NOREF5(pDevIns, pThis, pThisCC, qIdx, pDescChain);
1555}
1556
1557/**
1558 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
1559 */
1560static DECLCALLBACK(int) virtioNetR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1561{
1562 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1563 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[(uintptr_t)pThread->pvUser].hEvtProcess);
1564}
1565
1566/**
1567 * @callback_method_impl{FNPDMTHREADDEV}
1568 */
1569static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1570{
1571 uint16_t const qIdx = (uint16_t)(uintptr_t)pThread->pvUser;
1572 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1573 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1574 PVIRTIONETWORKER pWorker = &pThis->aWorkers[qIdx];
1575 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[qIdx];
1576
1577 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1578 return VINF_SUCCESS;
1579
1580 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1581 {
1582 if (virtioCoreQueueIsEmpty(pDevIns, &pThis->Virtio, qIdx))
1583 {
1584 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
1585 ASMAtomicWriteBool(&pWorkerR3->fSleeping, true);
1586 bool fNotificationSent = ASMAtomicXchgBool(&pWorkerR3->fNotified, false);
1587 if (!fNotificationSent)
1588 {
1589 Log6Func(("%s worker sleeping...\n", VIRTQNAME(qIdx)));
1590 Assert(ASMAtomicReadBool(&pWorkerR3->fSleeping));
1591 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
1592 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
1593 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1594 return VINF_SUCCESS;
1595 if (rc == VERR_INTERRUPTED)
1596 continue;
1597 Log6Func(("%s worker woken\n", VIRTQNAME(qIdx)));
1598 ASMAtomicWriteBool(&pWorkerR3->fNotified, false);
1599 }
1600 ASMAtomicWriteBool(&pWorkerR3->fSleeping, false);
1601 }
1602
1603 if (!pThis->afQueueAttached[qIdx])
1604 {
1605 LogFunc(("%s queue not attached, worker aborting...\n", VIRTQNAME(qIdx)));
1606 break;
1607 }
1608 if (!pThisCC->fQuiescing)
1609 {
1610 Log6Func(("fetching next descriptor chain from %s\n", VIRTQNAME(qIdx)));
1611 PVIRTIO_DESC_CHAIN_T pDescChain;
1612 int rc = virtioCoreR3QueueGet(pDevIns, &pThis->Virtio, qIdx, &pDescChain, true);
1613 if (rc == VERR_NOT_AVAILABLE)
1614 {
1615 Log6Func(("Nothing found in %s\n", VIRTQNAME(qIdx)));
1616 continue;
1617 }
1618
1619 AssertRC(rc);
1620 if (qIdx == CTRLQIDX)
1621 virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pDescChain);
1622 else if (qIdx & 1)
1623 virtioNetR3Transmit(pDevIns, pThis, pThisCC, qIdx, pDescChain);
1624 else
1625 virtioNetR3Receive(pDevIns, pThis, pThisCC, qIdx, pDescChain);
1626 }
1627 }
1628 return VINF_SUCCESS;
1629}
1630
1631#ifdef IN_RING3
1632
1633/** Returns true if large packets are written into several RX buffers. */
1634DECLINLINE(bool) virtioNetR3MergeableRxBuffers(PVIRTIONET pThis)
1635{
1636 return !!(pThis->fFeatures & VIRTIONET_F_MRG_RXBUF);
1637}
1638
1639DECLINLINE(int) virtioNetR3CsEnter(PPDMDEVINS pDevIns, PVIRTIONET pThis, int rcBusy)
1640{
1641 /* Original DevVirtioNet uses CS in attach/detach/link-up timer/tx timer/transmit */
1642 LogFunc("CS unimplemented. What does the critical section protect in orig driver??"));
1643}
1644
1645DECLINLINE(void) virtioNetR3CsLeave(PPDMDEVINS pDevIns, PVIRTIONET pThis)
1646{
1647 LogFunc("CS unimplemented. What does the critical section protect in orig driver??"));
1648}
1649
1650/**
1651 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
1652 */
1653static DECLCALLBACK(void) virtioNetR3StatusChanged(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
1654{
1655 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
1656 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
1657
1658 LogFunc((""));
1659
1660 pThis->fVirtioReady = fVirtioReady;
1661
1662 if (fVirtioReady)
1663 {
1664 LogFunc(("VirtIO ready\n-----------------------------------------------------------------------------------------\n"));
1665 uint64_t fFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
1666 pThis->fResetting = false;
1667 pThisCC->fQuiescing = false;
1668
1669 for (unsigned i = 0; i < VIRTIONET_MAX_QUEUES; i++)
1670 pThis->afQueueAttached[i] = true;
1671 }
1672 else
1673 {
1674 LogFunc(("VirtIO is resetting\n"));
1675
1676 pThis->virtioNetConfig.status = pThis->fCableConnected ? VIRTIONET_S_LINK_UP : 0;
1677 LogFunc(("%s Link is %s\n", pThis->szInstanceName, pThis->fCableConnected ? "up" : "down"));
1678
1679 pThis->fPromiscuous = true;
1680 pThis->fAllMulticast = false;
1681 pThis->fAllUnicast = false;
1682 pThis->fNoMulticat = false;
1683 pThis->fNoUnicast = false;
1684 pThis->fNoBroadcast = false;
1685 pThis->uIsTransmitting = 0;
1686 pThis->cUnicastFilterMacs = 0;
1687 pThis->cMulticastFilterMacs = 0;
1688
1689 memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
1690 memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
1691 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
1692
1693 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
1694
1695 for (unsigned i = 0; i < VIRTIONET_MAX_QUEUES; i++)
1696 pThis->afQueueAttached[i] = false;
1697 }
1698}
1699#endif /* IN_RING3 */
1700
1701/**
1702 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
1703 *
1704 * One harddisk at one port has been unplugged.
1705 * The VM is suspended at this point.
1706 */
1707static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1708{
1709 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1710 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1711
1712 LogFunc((""));
1713 AssertLogRelReturnVoid(iLUN == 0);
1714
1715 int rc = virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
1716 AssertMsgRCReturn(rc, ("Failed to enter critical section"), rc);
1717
1718 /*
1719 * Zero important members.
1720 */
1721 pThisCC->pDrvBase = NULL;
1722 pThisCC->pDrv = NULL;
1723
1724 virtioNetR3CsLeave(pDevIns, pThis);
1725}
1726
1727/**
1728 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
1729 *
1730 * This is called when we change block driver.
1731 */
1732static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1733{
1734 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1735 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1736
1737 RT_NOREF(fFlags);
1738 LogFunc(("%s", INSTANCE(pThis)));
1739
1740 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1741
1742 int rc = virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
1743 AssertMsgRCReturn(rc, ("Failed to enter critical section"), rc);
1744
1745 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pDevIns->IBase, &pThisCC->pDrvBase, "Network Port");
1746 if (RT_SUCCESS(rc))
1747 {
1748 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
1749 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1750 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
1751 }
1752 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
1753 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
1754 Log(("%s No attached driver!\n", INSTANCE(pThis)));
1755
1756 virtioNetR3CsLeave(pDevIns, pThis);
1757 return rc;
1758
1759 AssertRelease(!pThisCC->pDrvBase);
1760 return rc;
1761}
1762
1763/**
1764 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
1765 */
1766static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1767{
1768 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
1769 if (iLUN)
1770 return VERR_PDM_LUN_NOT_FOUND;
1771 *ppLed = &pThisR3->led;
1772 return VINF_SUCCESS;
1773}
1774
1775/**
1776 * Turns on/off the write status LED.
1777 *
1778 * @returns VBox status code.
1779 * @param pThis Pointer to the device state structure.
1780 * @param fOn New LED state.
1781 */
1782void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
1783{
1784 Log6Func(("%s\n", fOn ? "on" : "off"));
1785 if (fOn)
1786 pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
1787 else
1788 pThisR3->led.Actual.s.fWriting = fOn;
1789}
1790
1791/**
1792 * Turns on/off the read status LED.
1793 *
1794 * @returns VBox status code.
1795 * @param pThis Pointer to the device state structure.
1796 * @param fOn New LED state.
1797 */
1798void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
1799{
1800 Log6Func(("%s\n", fOn ? "on" : "off"));
1801 if (fOn)
1802 pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
1803 else
1804 pThisR3->led.Actual.s.fReading = fOn;
1805}
1806/**
1807 * @interface_method_impl{PDMIBASE,pfnQueryInterface,
1808 */
1809static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
1810{
1811 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, IBase);
1812
1813 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisR3->INetworkDown);
1814 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisR3->INetworkConfig);
1815 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisR3->IBase);
1816 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisR3->ILeds);
1817 return NULL;
1818}
1819
1820/**
1821 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
1822 */
1823static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
1824{
1825 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1826 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1827 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1828
1829
1830 for (unsigned qIdx = 0; qIdx < pThis->cVirtQueues; qIdx++)
1831 {
1832 PVIRTIONETWORKER pWorker = &pThis->aWorkers[qIdx];
1833 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
1834 {
1835 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
1836 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
1837 }
1838 }
1839
1840 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
1841 return VINF_SUCCESS;
1842}
1843
1844/**
1845 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
1846 */
1847static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1848{
1849 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1850 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1851 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1852 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1853
1854 /*
1855 * Quick initialization of the state data, making sure that the destructor always works.
1856 */
1857 LogFunc(("PDM device instance: %d\n", iInstance));
1858 RTStrPrintf(pThis->szInstanceName, sizeof(pThis->szInstanceName), "VIRTIONET%d", iInstance);
1859 pThisCC->pDevIns = pDevIns;
1860
1861 pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
1862 pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
1863 pThisCC->led.uMagic = PDMLED_MAGIC;
1864
1865 /*
1866 * Validate configuration.
1867 */
1868 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo", "");
1869
1870 /* Get config params */
1871 int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au, sizeof(pThis->macConfigured));
1872 if (RT_FAILURE(rc))
1873 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
1874
1875 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
1876 if (RT_FAILURE(rc))
1877 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
1878
1879 uint32_t uStatNo = iInstance;
1880 rc = pHlp->pfnCFGMQueryuDef(pCfg, "StatNo", &uStatNo, iInstance);
1881 if (RT_FAILURE(rc))
1882 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
1883
1884 rc = pHlp->pfnCFGMQueryuDef(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
1885 if (RT_FAILURE(rc))
1886 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
1887
1888 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
1889
1890 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
1891 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
1892 pThis->szInstanceName, pThis->cMsLinkUpDelay / 1000));
1893
1894 Log(("%s Link up delay is set to %u seconds\n", pThis->szInstanceName, pThis->cMsLinkUpDelay / 1000));
1895
1896 /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
1897 memcpy(pThis->virtioNetConfig.uMacAddress, pThis->macConfigured.au, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
1898
1899 /*
1900 * Do core virtio initialization.
1901 */
1902
1903#if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_STATUS
1904 pThis->virtioNetConfig.uStatus = 0;
1905#endif
1906
1907#if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_MQ
1908 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
1909#endif
1910
1911 /* Initialize the generic Virtio core: */
1912 pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChanged;
1913 pThisCC->Virtio.pfnQueueNotified = virtioNetR3Notified;
1914
1915 VIRTIOPCIPARAMS VirtioPciParams;
1916 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
1917 VirtioPciParams.uClassBase = PCI_CLASS_BASE_NETWORK_CONTROLLER;
1918 VirtioPciParams.uClassSub = PCI_CLASS_SUB_NET_ETHERNET_CONTROLLER;
1919 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
1920 VirtioPciParams.uSubsystemId = PCI_DEVICE_ID_VIRTIONET_HOST; /* VirtIO 1.0 spec allows PCI Device ID here */
1921 VirtioPciParams.uInterruptLine = 0x00;
1922 VirtioPciParams.uInterruptPin = 0x01;
1923
1924 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInstanceName,
1925 VIRTIONET_HOST_FEATURES_OFFERED,
1926 &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
1927 if (RT_FAILURE(rc))
1928 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
1929
1930 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
1931 if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
1932 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
1933
1934 pThis->cVirtqPairs = pThis->fNegotiatedFeatures & VIRTIONET_F_MQ
1935 ? pThis->virtioNetConfig.uMaxVirtqPairs : 1;
1936 pThis->cVirtQueues = pThis->cVirtqPairs + 1;
1937
1938 /*
1939 * Initialize queues.
1940 */
1941 virtioNetR3SetVirtqNames(pThis);
1942
1943 /* Attach the queues and create worker threads for them: */
1944 for (uint16_t qIdx = 0; qIdx < pThis->cVirtQueues; qIdx++)
1945 {
1946 rc = virtioCoreR3QueueAttach(&pThis->Virtio, qIdx, VIRTQNAME(qIdx));
1947 if (RT_FAILURE(rc))
1948 continue;
1949 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->aWorkers[qIdx].pThread,
1950 (void *)(uintptr_t)qIdx, virtioNetR3WorkerThread,
1951 virtioNetR3WorkerWakeUp, 0, RTTHREADTYPE_IO, VIRTQNAME(qIdx));
1952 if (rc != VINF_SUCCESS)
1953 {
1954 LogRel(("Error creating thread for Virtual Queue %s: %Rrc\n", VIRTQNAME(qIdx), rc));
1955 return rc;
1956 }
1957
1958 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->aWorkers[qIdx].hEvtProcess);
1959 if (RT_FAILURE(rc))
1960 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1961 N_("DevVirtioNET: Failed to create SUP event semaphore"));
1962 pThis->afQueueAttached[qIdx] = true;
1963 }
1964
1965 /*
1966 * Status driver (optional).
1967 */
1968 PPDMIBASE pUpBase;
1969 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
1970 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
1971 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
1972 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
1973
1974 /*
1975 * Register saved state.
1976 */
1977 rc = PDMDevHlpSSMRegister(pDevIns, VIRTIONET_SAVED_STATE_VERSION, sizeof(*pThis),
1978 virtioNetR3SaveExec, virtioNetR3LoadExec);
1979 AssertRCReturn(rc, rc);
1980
1981 /*
1982 * Register the debugger info callback (ignore errors).
1983 */
1984 char szTmp[128];
1985 RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
1986 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-net info", virtioNetR3Info);
1987 return rc;
1988}
1989
1990#else /* !IN_RING3 */
1991
1992/**
1993 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1994 */
1995static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
1996{
1997 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1998 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1999 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2000
2001 return virtioCoreRZInit(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
2002}
2003
2004#endif /* !IN_RING3 */
2005
2006
2007/**
2008 * The device registration structure.
2009 */
2010const PDMDEVREG g_DeviceVirtioNet_1_0 =
2011{
2012 /* .uVersion = */ PDM_DEVREG_VERSION,
2013 /* .uReserved0 = */ 0,
2014 /* .szName = */ "virtio-net-1-dot-0",
2015 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE //| PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
2016 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2017 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2018 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
2019 /* .cMaxInstances = */ ~0U,
2020 /* .uSharedVersion = */ 42,
2021 /* .cbInstanceShared = */ sizeof(VIRTIONET),
2022 /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
2023 /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
2024 /* .cMaxPciDevices = */ 1,
2025 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
2026 /* .pszDescription = */ "Virtio Host NET.\n",
2027#if defined(IN_RING3)
2028 /* .pszRCMod = */ "VBoxDDRC.rc",
2029 /* .pszR0Mod = */ "VBoxDDR0.r0",
2030 /* .pfnConstruct = */ virtioNetR3Construct,
2031 /* .pfnDestruct = */ virtioNetR3Destruct,
2032 /* .pfnRelocate = */ NULL,
2033 /* .pfnMemSetup = */ NULL,
2034 /* .pfnPowerOn = */ NULL,
2035 /* .pfnReset = */ virtioNetR3Reset,
2036 /* .pfnSuspend = */ virtioNetR3Suspend,
2037 /* .pfnResume = */ virtioNetR3Resume,
2038 /* .pfnAttach = */ virtioNetR3Attach,
2039 /* .pfnDetach = */ virtioNetR3Detach,
2040 /* .pfnQueryInterface = */ NULL,
2041 /* .pfnInitComplete = */ NULL,
2042 /* .pfnPowerOff = */ virtioNetR3PowerOff,
2043 /* .pfnSoftReset = */ NULL,
2044 /* .pfnReserved0 = */ NULL,
2045 /* .pfnReserved1 = */ NULL,
2046 /* .pfnReserved2 = */ NULL,
2047 /* .pfnReserved3 = */ NULL,
2048 /* .pfnReserved4 = */ NULL,
2049 /* .pfnReserved5 = */ NULL,
2050 /* .pfnReserved6 = */ NULL,
2051 /* .pfnReserved7 = */ NULL,
2052#elif defined(IN_RING0)
2053 /* .pfnEarlyConstruct = */ NULL,
2054 /* .pfnConstruct = */ virtioNetRZConstruct,
2055 /* .pfnDestruct = */ NULL,
2056 /* .pfnFinalDestruct = */ NULL,
2057 /* .pfnRequest = */ NULL,
2058 /* .pfnReserved0 = */ NULL,
2059 /* .pfnReserved1 = */ NULL,
2060 /* .pfnReserved2 = */ NULL,
2061 /* .pfnReserved3 = */ NULL,
2062 /* .pfnReserved4 = */ NULL,
2063 /* .pfnReserved5 = */ NULL,
2064 /* .pfnReserved6 = */ NULL,
2065 /* .pfnReserved7 = */ NULL,
2066#elif defined(IN_RC)
2067 /* .pfnConstruct = */ virtioNetRZConstruct,
2068 /* .pfnReserved0 = */ NULL,
2069 /* .pfnReserved1 = */ NULL,
2070 /* .pfnReserved2 = */ NULL,
2071 /* .pfnReserved3 = */ NULL,
2072 /* .pfnReserved4 = */ NULL,
2073 /* .pfnReserved5 = */ NULL,
2074 /* .pfnReserved6 = */ NULL,
2075 /* .pfnReserved7 = */ NULL,
2076#else
2077# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2078#endif
2079 /* .uVersionEnd = */ PDM_DEVREG_VERSION
2080};
2081
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