VirtualBox

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

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

Network/DevVirtioNet_1_0.cpp: Fixed but in Virtio_1_0.cpp virtioWriteUsedFlags. Can now read guest MAC filter table properly.

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