VirtualBox

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

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

Network/DevVirtioNet.cpp: Some clean up. Have verified that received packets are formatted properly, conveyed to and read by guest properly. Still no Tx packets from guest yet. Continuing to investigate

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

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