VirtualBox

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

Last change on this file since 82781 was 82780, checked in by vboxsync, 4 years ago

Fix burn

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

© 2023 Oracle
ContactPrivacy policyTerms of Use