VirtualBox

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

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

Network/DevVirtioNet_1_0.cpp: Round trip traffic happening continuously with network activity, but pings are not resolving yet. Need to dig into higher level packet traces, and validate thing like checksumming, etc...

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