VirtualBox

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

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

Network/DevVirtioNet_1_0.cpp: Device appears and is doing transactions over VirtIO but snags when client setups MAC filter. See BugRef(#8651) Comment #53

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

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