VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevVirtioNet.cpp

Last change on this file was 109188, checked in by vboxsync, 11 days ago

VirtioNet: Temporary code from 7.0 branch for debugging descriptor loop issue ​bugref:10572

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 166.7 KB
Line 
1/* $Id: DevVirtioNet.cpp 109188 2025-05-07 07:42:49Z vboxsync $ $Revision: 109188 $ $Date: 2025-05-07 07:42:49 +0000 (Wed, 07 May 2025) $ $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-2024 Oracle and/or its affiliates.
16 *
17 * This file is part of VirtualBox base platform packages, as
18 * available from https://www.215389.xyz.
19 *
20 * This program is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU General Public License
22 * as published by the Free Software Foundation, in version 3 of the
23 * License.
24 *
25 * This program is distributed in the hope that it will be useful, but
26 * WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, see <https://www.gnu.org/licenses>.
32 *
33 * SPDX-License-Identifier: GPL-3.0-only
34 */
35
36/*******************************************************************************************************************************
37* Header Files *
38***************************************************************************************************************************** **/
39#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
40#define VIRTIONET_WITH_GSO
41
42#include <iprt/types.h>
43#include <iprt/errcore.h>
44#include <iprt/assert.h>
45#include <iprt/string.h>
46
47#include <VBox/sup.h>
48#include <VBox/vmm/pdmdev.h>
49#include <VBox/vmm/stam.h>
50#include <VBox/vmm/pdmcritsect.h>
51#include <VBox/vmm/pdmnetifs.h>
52#include <VBox/msi.h>
53#include <VBox/version.h>
54#include <VBox/log.h>
55#include <VBox/pci.h>
56
57
58#ifdef IN_RING3
59# include <VBox/VBoxPktDmp.h>
60# include <iprt/alloc.h>
61# include <iprt/memcache.h>
62# include <iprt/semaphore.h>
63# include <iprt/sg.h>
64# include <iprt/param.h>
65# include <iprt/uuid.h>
66#endif
67#include "../VirtIO/VirtioCore.h"
68
69#include "VBoxDD.h"
70
71#define VIRTIONET_TRANSITIONAL_ENABLE_FLAG 1 /** < If set behave as VirtIO "transitional" device */
72
73/** The current saved state version for the virtio core. */
74#define VIRTIONET_SAVEDSTATE_VERSION UINT32_C(1)
75#define VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY UINT32_C(1) /**< Grandfathered in from DevVirtioNet.cpp */
76#define VIRTIONET_SAVEDSTATE_VERSION_LEGACY UINT32_C(2) /**< Grandfathered in from DevVirtioNet.cpp */
77#define VIRTIONET_VERSION_MARKER_MAC_ADDR { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /** SSM handling */
78
79/*
80 * Glossary of networking acronyms used in feature names below:
81 *
82 * GSO = Generic Segmentation Offload
83 * TSO = TCP Segmentation Offload
84 * UFO = UDP Fragmentation Offload
85 * ECN = Explicit Congestion Notification
86 */
87
88/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
89 * @{ */
90#define VIRTIONET_F_CSUM RT_BIT_64(0) /**< Handle packets with partial checksum */
91#define VIRTIONET_F_GUEST_CSUM RT_BIT_64(1) /**< Handles packets with partial checksum */
92#define VIRTIONET_F_CTRL_GUEST_OFFLOADS RT_BIT_64(2) /**< Control channel offloads reconfig support */
93#define VIRTIONET_F_MAC RT_BIT_64(5) /**< Device has given MAC address */
94#define VIRTIONET_F_GUEST_TSO4 RT_BIT_64(7) /**< Driver can receive TSOv4 */
95#define VIRTIONET_F_GUEST_TSO6 RT_BIT_64(8) /**< Driver can receive TSOv6 */
96#define VIRTIONET_F_GUEST_ECN RT_BIT_64(9) /**< Driver can receive TSO with ECN */
97#define VIRTIONET_F_GUEST_UFO RT_BIT_64(10) /**< Driver can receive UFO */
98#define VIRTIONET_F_HOST_TSO4 RT_BIT_64(11) /**< Device can receive TSOv4 */
99#define VIRTIONET_F_HOST_TSO6 RT_BIT_64(12) /**< Device can receive TSOv6 */
100#define VIRTIONET_F_HOST_ECN RT_BIT_64(13) /**< Device can receive TSO with ECN */
101#define VIRTIONET_F_HOST_UFO RT_BIT_64(14) /**< Device can receive UFO */
102#define VIRTIONET_F_MRG_RXBUF RT_BIT_64(15) /**< Driver can merge receive buffers */
103#define VIRTIONET_F_STATUS RT_BIT_64(16) /**< Config status field is available */
104#define VIRTIONET_F_CTRL_VQ RT_BIT_64(17) /**< Control channel is available */
105#define VIRTIONET_F_CTRL_RX RT_BIT_64(18) /**< Control channel RX mode + MAC addr filtering */
106#define VIRTIONET_F_CTRL_VLAN RT_BIT_64(19) /**< Control channel VLAN filtering */
107#define VIRTIONET_F_CTRL_RX_EXTRA RT_BIT_64(20) /**< Control channel RX mode extra functions */
108#define VIRTIONET_F_GUEST_ANNOUNCE RT_BIT_64(21) /**< Driver can send gratuitous packets */
109#define VIRTIONET_F_MQ RT_BIT_64(22) /**< Support ultiqueue with auto receive steering */
110#define VIRTIONET_F_CTRL_MAC_ADDR RT_BIT_64(23) /**< Set MAC address through control channel */
111/** @} */
112
113#ifdef IN_RING3
114static const VIRTIO_FEATURES_LIST s_aDevSpecificFeatures[] =
115{
116 { VIRTIONET_F_STATUS, " STATUS Configuration status field is available.\n" },
117 { VIRTIONET_F_MAC, " MAC Host has given MAC address.\n" },
118 { VIRTIONET_F_CTRL_VQ, " CTRL_VQ Control channel is available.\n" },
119 { VIRTIONET_F_CTRL_MAC_ADDR, " CTRL_MAC_ADDR Set MAC address through control channel.\n" },
120 { VIRTIONET_F_CTRL_RX, " CTRL_RX Control channel RX mode support.\n" },
121 { VIRTIONET_F_CTRL_VLAN, " CTRL_VLAN Control channel VLAN filtering.\n" },
122 { VIRTIONET_F_CTRL_GUEST_OFFLOADS, " CTRL_GUEST_OFFLOADS Control channel offloads reconfiguration support.\n" },
123 { VIRTIONET_F_GUEST_CSUM, " GUEST_CSUM Guest handles packets with partial checksum.\n" },
124 { VIRTIONET_F_GUEST_ANNOUNCE, " GUEST_ANNOUNCE Guest can send gratuitous packets.\n" },
125 { VIRTIONET_F_GUEST_TSO4, " GUEST_TSO4 Guest can receive TSOv4.\n" },
126 { VIRTIONET_F_GUEST_TSO6, " GUEST_TSO6 Guest can receive TSOv6.\n" },
127 { VIRTIONET_F_GUEST_ECN, " GUEST_ECN Guest can receive TSO with ECN.\n" },
128 { VIRTIONET_F_GUEST_UFO, " GUEST_UFO Guest can receive UFO.\n" },
129 { VIRTIONET_F_HOST_TSO4, " HOST_TSO4 Host can receive TSOv4.\n" },
130 { VIRTIONET_F_HOST_TSO6, " HOST_TSO6 Host can receive TSOv6.\n" },
131 { VIRTIONET_F_HOST_ECN, " HOST_ECN Host can receive TSO with ECN.\n" },
132 { VIRTIONET_F_HOST_UFO, " HOST_UFO Host can receive UFO.\n" },
133 { VIRTIONET_F_MQ, " MQ Host supports multiqueue with automatic receive steering.\n" },
134 { VIRTIONET_F_CSUM, " CSUM Host handles packets with partial checksum.\n" },
135 { VIRTIONET_F_MRG_RXBUF, " MRG_RXBUF Guest can merge receive buffers.\n" },
136};
137#endif
138
139#ifdef VIRTIONET_WITH_GSO
140# define VIRTIONET_HOST_FEATURES_GSO \
141 ( VIRTIONET_F_CSUM \
142 | VIRTIONET_F_HOST_TSO4 \
143 | VIRTIONET_F_HOST_TSO6 \
144 | VIRTIONET_F_HOST_UFO \
145 | VIRTIONET_F_GUEST_TSO4 \
146 | VIRTIONET_F_GUEST_TSO6 \
147 | VIRTIONET_F_GUEST_UFO \
148 | VIRTIONET_F_GUEST_CSUM) /* @bugref(4796) Guest must handle partial chksums */
149#else
150# define VIRTIONET_HOST_FEATURES_GSO
151#endif
152
153#define VIRTIONET_HOST_FEATURES_OFFERED \
154 VIRTIONET_F_STATUS \
155 | VIRTIONET_F_GUEST_ANNOUNCE \
156 | VIRTIONET_F_MAC \
157 | VIRTIONET_F_CTRL_VQ \
158 | VIRTIONET_F_CTRL_RX \
159 | VIRTIONET_F_CTRL_VLAN \
160 | VIRTIONET_HOST_FEATURES_GSO \
161 | VIRTIONET_F_MRG_RXBUF
162
163#define FEATURE_ENABLED(feature) RT_BOOL(!!(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature))
164#define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
165#define FEATURE_OFFERED(feature) VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
166
167#if FEATURE_OFFERED(MQ)
168/* Instance data doesn't allow an array large enough to contain VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX entries */
169# define VIRTIONET_MAX_QPAIRS 1 /* This should be increased at some point and made to work */
170#else
171# define VIRTIONET_MAX_QPAIRS VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN /* default, VirtIO 1.0, 5.1.6.5.5 */
172#endif
173
174#define VIRTIONET_CTRL_MQ_VQ_PAIRS 64
175#define VIRTIONET_MAX_WORKERS VIRTIONET_MAX_QPAIRS + 1
176#define VIRTIONET_MAX_VIRTQS (VIRTIONET_MAX_QPAIRS * 2 + 1)
177#define VIRTIONET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP pkt size + Eth. header w/VLAN tag */
178#define VIRTIONET_MAC_FILTER_LEN 64
179#define VIRTIONET_MAX_VLAN_ID 4096
180#define VIRTIONET_RX_SEG_COUNT 32
181
182#define VIRTQNAME(uVirtqNbr) (pThis->aVirtqs[uVirtqNbr]->szName)
183#define CBVIRTQNAME(uVirtqNbr) RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
184
185#define IS_TX_VIRTQ(n) ((n) != CTRLQIDX && ((n) & 1))
186#define IS_RX_VIRTQ(n) ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
187#define IS_CTRL_VIRTQ(n) ((n) == CTRLQIDX)
188
189/*
190 * Macros to calculate queue type-pecific index number regardless of scale. VirtIO 1.0, 5.1.2
191 */
192#define RXQIDX(qPairIdx) (qPairIdx * 2)
193#define TXQIDX(qPairIdx) (RXQIDX(qPairIdx) + 1)
194#define CTRLQIDX (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
195
196#define IS_LINK_UP(pState) !!(pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
197#define IS_LINK_DOWN(pState) !IS_LINK_UP(pState)
198
199#define SET_LINK_UP(pState) \
200 LogFunc(("SET_LINK_UP\n")); \
201 pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
202 virtioCoreNotifyConfigChanged(&pThis->Virtio)
203
204#define SET_LINK_DOWN(pState) \
205 LogFunc(("SET_LINK_DOWN\n")); \
206 pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
207 virtioCoreNotifyConfigChanged(&pThis->Virtio)
208
209#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
210 (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
211
212#define PCI_DEVICE_ID_VIRTIONET_HOST 0x1000 /**< VirtIO transitional device ID for network card */
213#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
214#define VIRTIONET_PCI_CLASS 0x01 /**< Base class Mass Storage? */
215
216/**
217 * VirtIO Network (virtio-net) device-specific configuration subregion (VirtIO 1.0, 5.1.4)
218 * Guest MMIO is processed through callback to VirtIO core which forwards references to network configuration
219 * fields to this device-specific code through a callback.
220 */
221#pragma pack(1)
222
223 typedef struct virtio_net_config
224 {
225 RTMAC uMacAddress; /**< mac */
226
227#if FEATURE_OFFERED(STATUS)
228 uint16_t uStatus; /**< status */
229#endif
230
231#if FEATURE_OFFERED(MQ)
232 uint16_t uMaxVirtqPairs; /**< max_virtq_pairs */
233#endif
234
235 } VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
236
237#pragma pack()
238
239#define VIRTIONET_F_LINK_UP 1 /**< config status: Link is up */
240#define VIRTIONET_F_ANNOUNCE 2 /**< config status: Announce */
241
242/** @name VirtIO 1.0 NET Host Device device specific control types
243 * @{ */
244#define VIRTIONET_HDR_F_NEEDS_CSUM 1 /**< flags: Packet needs checksum */
245#define VIRTIONET_HDR_GSO_NONE 0 /**< gso_type: No Global Segmentation Offset */
246#define VIRTIONET_HDR_GSO_TCPV4 1 /**< gso_type: Global Segment Offset for TCPV4 */
247#define VIRTIONET_HDR_GSO_UDP 3 /**< gso_type: Global Segment Offset for UDP */
248#define VIRTIONET_HDR_GSO_TCPV6 4 /**< gso_type: Global Segment Offset for TCPV6 */
249#define VIRTIONET_HDR_GSO_ECN 0x80 /**< gso_type: Explicit Congestion Notification */
250/** @} */
251
252/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
253#pragma pack(1)
254struct virtio_net_pkt_hdr {
255 uint8_t uFlags; /**< flags */
256 uint8_t uGsoType; /**< gso_type */
257 uint16_t uHdrLen; /**< hdr_len */
258 uint16_t uGsoSize; /**< gso_size */
259 uint16_t uChksumStart; /**< Chksum_start */
260 uint16_t uChksumOffset; /**< Chksum_offset */
261 uint16_t uNumBuffers; /**< num_buffers */
262};
263#pragma pack()
264typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
265AssertCompileSize(VIRTIONETPKTHDR, 12);
266
267/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
268#pragma pack(1)
269struct virtio_net_ctrl_hdr {
270 uint8_t uClass; /**< class */
271 uint8_t uCmd; /**< command */
272};
273#pragma pack()
274typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
275
276typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
277
278/* Command entry fAck values */
279#define VIRTIONET_OK 0 /**< Internal success status */
280#define VIRTIONET_ERROR 1 /**< Internal failure status */
281
282/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
283 * @{ */
284#define VIRTIONET_CTRL_RX 0 /**< Control class: Receive filtering */
285#define VIRTIONET_CTRL_RX_PROMISC 0 /**< Promiscuous mode */
286#define VIRTIONET_CTRL_RX_ALLMULTI 1 /**< All-multicast receive */
287#define VIRTIONET_CTRL_RX_ALLUNI 2 /**< All-unicast receive */
288#define VIRTIONET_CTRL_RX_NOMULTI 3 /**< No multicast receive */
289#define VIRTIONET_CTRL_RX_NOUNI 4 /**< No unicast receive */
290#define VIRTIONET_CTRL_RX_NOBCAST 5 /**< No broadcast receive */
291/** @} */
292
293typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
294typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
295typedef uint8_t VIRTIONET_CTRL_MAC_ENTRIES[][6];
296
297/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
298 * @{ */
299#define VIRTIONET_CTRL_MAC 1 /**< Control class: MAC address filtering */
300#define VIRTIONET_CTRL_MAC_TABLE_SET 0 /**< Set MAC table */
301#define VIRTIONET_CTRL_MAC_ADDR_SET 1 /**< Set default MAC address */
302/** @} */
303
304/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
305 * @{ */
306#define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */
307#define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */
308#define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */
309/** @} */
310
311/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
312 * @{ */
313#define VIRTIONET_CTRL_ANNOUNCE 3 /**< Control class: Gratuitous Packet Sending */
314#define VIRTIONET_CTRL_ANNOUNCE_ACK 0 /**< Gratuitous Packet Sending ACK */
315/** @} */
316
317struct virtio_net_ctrl_mq {
318 uint16_t uVirtqueuePairs; /**< virtqueue_pairs */
319};
320
321/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
322 * @{ */
323#define VIRTIONET_CTRL_MQ 4 /**< Control class: Receive steering */
324#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET 0 /**< Set number of TX/RX queues */
325#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN 1 /**< Minimum number of TX/RX queues */
326#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 /**< Maximum number of TX/RX queues */
327/** @} */
328
329uint64_t uOffloads; /**< offloads */
330
331/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
332 * @{ */
333#define VIRTIONET_CTRL_GUEST_OFFLOADS 5 /**< Control class: Offloads state configuration */
334#define VIRTIONET_CTRL_GUEST_OFFLOADS_SET 0 /**< Apply new offloads configuration */
335/** @} */
336
337typedef enum VIRTIONETPKTHDRTYPE
338{
339 kVirtioNetUninitializedPktHdrType = 0, /**< Uninitialized (default) packet header type */
340 kVirtioNetModernPktHdrWithoutMrgRx = 1, /**< Packets should not be merged (modern driver) */
341 kVirtioNetModernPktHdrWithMrgRx = 2, /**< Packets should be merged (modern driver) */
342 kVirtioNetLegacyPktHdrWithoutMrgRx = 3, /**< Packets should not be merged (legacy driver) */
343 kVirtioNetLegacyPktHdrWithMrgRx = 4, /**< Packets should be merged (legacy driver) */
344 kVirtioNetFor32BitHack = 0x7fffffff
345} VIRTIONETPKTHDRTYPE;
346
347/**
348 * device-specific queue info
349 */
350struct VIRTIONETWORKER;
351struct VIRTIONETWORKERR3;
352
353typedef struct VIRTIONETVIRTQ
354{
355 uint16_t uIdx; /**< Index of this queue */
356 uint16_t align;
357 bool fCtlVirtq; /**< If set this queue is the control queue */
358 bool fHasWorker; /**< If set this queue has an associated worker */
359 bool fAttachedToVirtioCore; /**< Set if queue attached to virtio core */
360 char szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name */
361} VIRTIONETVIRTQ, *PVIRTIONETVIRTQ;
362
363/**
364 * Worker thread context, shared state.
365 */
366typedef struct VIRTIONETWORKER
367{
368 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
369 uint16_t uIdx; /**< Index of this worker */
370 bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
371 bool volatile fNotified; /**< Flags whether worker thread notified */
372 bool fAssigned; /**< Flags whether worker thread has been set up */
373 uint8_t pad;
374} VIRTIONETWORKER;
375/** Pointer to a virtio net worker. */
376typedef VIRTIONETWORKER *PVIRTIONETWORKER;
377
378/**
379 * Worker thread context, ring-3 state.
380 */
381typedef struct VIRTIONETWORKERR3
382{
383 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
384 uint16_t uIdx; /**< Index of this worker */
385 uint16_t pad;
386} VIRTIONETWORKERR3;
387/** Pointer to a virtio net worker. */
388typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
389
390/**
391 * VirtIO Host NET device state, shared edition.
392 *
393 * @extends VIRTIOCORE
394 */
395typedef struct VIRTIONET
396{
397 /** The core virtio state. */
398 VIRTIOCORE Virtio;
399
400 /** Virtio device-specific configuration */
401 VIRTIONET_CONFIG_T virtioNetConfig;
402
403 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
404 VIRTIONETWORKER aWorkers[VIRTIONET_MAX_VIRTQS];
405
406 /** Track which VirtIO queues we've attached to */
407 VIRTIONETVIRTQ aVirtqs[VIRTIONET_MAX_VIRTQS];
408
409 /** PDM device Instance name */
410 char szInst[16];
411
412 /** VirtIO features negotiated with the guest, including generic core and device specific */
413 uint64_t fNegotiatedFeatures;
414
415 /** Number of Rx/Tx queue pairs (only one if MQ feature not negotiated */
416 uint16_t cVirtqPairs;
417
418 /** Number of Rx/Tx queue pairs that have already been initialized */
419 uint16_t cInitializedVirtqPairs;
420
421 /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
422 uint16_t cVirtqs;
423
424 /** Number of worker threads (one for the control queue and one for each Tx queue) */
425 uint16_t cWorkers;
426
427 /** Alignment */
428 uint16_t alignment;
429
430 /** Indicates transmission in progress -- only one thread is allowed. */
431 uint32_t uIsTransmitting;
432
433 /** Link up delay (in milliseconds). */
434 uint32_t cMsLinkUpDelay;
435
436 /** The number of actually used slots in aMacMulticastFilter. */
437 uint32_t cMulticastFilterMacs;
438
439 /** The number of actually used slots in aMacUniicastFilter. */
440 uint32_t cUnicastFilterMacs;
441
442 /** Semaphore leaf device's thread waits on until guest driver sends empty Rx bufs */
443 SUPSEMEVENT hEventRxDescAvail;
444
445 /** Array of MAC multicast addresses accepted by RX filter. */
446 RTMAC aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
447
448 /** Array of MAC unicast addresses accepted by RX filter. */
449 RTMAC aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
450
451 /** Default MAC address which rx filtering accepts */
452 RTMAC rxFilterMacDefault;
453
454 /** MAC address obtained from the configuration. */
455 RTMAC macConfigured;
456
457 /** Bit array of VLAN filter, one bit per VLAN ID. */
458 uint8_t aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
459
460 /** Set if PDM leaf device at the network interface is starved for Rx buffers */
461 bool volatile fLeafWantsEmptyRxBufs;
462
463 /** Number of packet being sent/received to show in debug log. */
464 uint32_t uPktNo;
465
466 /** Flags whether VirtIO core is in ready state */
467 uint8_t fVirtioReady;
468
469 /** Resetting flag */
470 uint8_t fResetting;
471
472 /** Promiscuous mode -- RX filter accepts all packets. */
473 uint8_t fPromiscuous;
474
475 /** All multicast mode -- RX filter accepts all multicast packets. */
476 uint8_t fAllMulticast;
477
478 /** All unicast mode -- RX filter accepts all unicast packets. */
479 uint8_t fAllUnicast;
480
481 /** No multicast mode - Suppresses multicast receive */
482 uint8_t fNoMulticast;
483
484 /** No unicast mode - Suppresses unicast receive */
485 uint8_t fNoUnicast;
486
487 /** No broadcast mode - Suppresses broadcast receive */
488 uint8_t fNoBroadcast;
489
490 /** Type of network pkt header based on guest driver version/features */
491 VIRTIONETPKTHDRTYPE ePktHdrType;
492
493 /** Size of network pkt header based on guest driver version/features */
494 uint16_t cbPktHdr;
495
496 /** True if physical cable is attached in configuration. */
497 bool fCableConnected;
498
499 /** True if this device should offer legacy virtio support to the guest */
500 bool fOfferLegacy;
501
502 /** @name Statistic
503 * @{ */
504 STAMCOUNTER StatReceiveBytes;
505 STAMCOUNTER StatTransmitBytes;
506 STAMCOUNTER StatReceiveGSO;
507 STAMCOUNTER StatTransmitPackets;
508 STAMCOUNTER StatTransmitGSO;
509 STAMCOUNTER StatTransmitCSum;
510#ifdef VBOX_WITH_STATISTICS
511 STAMPROFILE StatReceive;
512 STAMPROFILE StatReceiveStore;
513 STAMPROFILEADV StatTransmit;
514 STAMPROFILE StatTransmitSend;
515 STAMPROFILE StatRxOverflow;
516 STAMCOUNTER StatRxOverflowWakeup;
517 STAMCOUNTER StatTransmitByNetwork;
518 STAMCOUNTER StatTransmitByThread;
519 /** @} */
520#endif
521} VIRTIONET;
522/** Pointer to the shared state of the VirtIO Host NET device. */
523typedef VIRTIONET *PVIRTIONET;
524
525/**
526 * VirtIO Host NET device state, ring-3 edition.
527 *
528 * @extends VIRTIOCORER3
529 */
530typedef struct VIRTIONETR3
531{
532 /** The core virtio ring-3 state. */
533 VIRTIOCORER3 Virtio;
534
535 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
536 VIRTIONETWORKERR3 aWorkers[VIRTIONET_MAX_VIRTQS];
537
538 /** The device instance.
539 * @note This is _only_ for use whxen dealing with interface callbacks. */
540 PPDMDEVINSR3 pDevIns;
541
542 /** Status LUN: Base interface. */
543 PDMIBASE IBase;
544
545 /** Status LUN: LED port interface. */
546 PDMILEDPORTS ILeds;
547
548 /** Status LUN: LED connector (peer). */
549 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
550
551 /** Status: LED */
552 PDMLED led;
553
554 /** Attached network driver. */
555 R3PTRTYPE(PPDMIBASE) pDrvBase;
556
557 /** Network port interface (down) */
558 PDMINETWORKDOWN INetworkDown;
559
560 /** Network config port interface (main). */
561 PDMINETWORKCONFIG INetworkConfig;
562
563 /** Connector of attached network driver. */
564 R3PTRTYPE(PPDMINETWORKUP) pDrv;
565
566 /** Link Up(/Restore) Timer. */
567 TMTIMERHANDLE hLinkUpTimer;
568
569} VIRTIONETR3;
570
571/** Pointer to the ring-3 state of the VirtIO Host NET device. */
572typedef VIRTIONETR3 *PVIRTIONETR3;
573
574/**
575 * VirtIO Host NET device state, ring-0 edition.
576 */
577typedef struct VIRTIONETR0
578{
579 /** The core virtio ring-0 state. */
580 VIRTIOCORER0 Virtio;
581} VIRTIONETR0;
582/** Pointer to the ring-0 state of the VirtIO Host NET device. */
583typedef VIRTIONETR0 *PVIRTIONETR0;
584
585/**
586 * VirtIO Host NET device state, raw-mode edition.
587 */
588typedef struct VIRTIONETRC
589{
590 /** The core virtio raw-mode state. */
591 VIRTIOCORERC Virtio;
592} VIRTIONETRC;
593/** Pointer to the ring-0 state of the VirtIO Host NET device. */
594typedef VIRTIONETRC *PVIRTIONETRC;
595
596/** @typedef VIRTIONETCC
597 * The instance data for the current context. */
598typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
599
600/** @typedef PVIRTIONETCC
601 * Pointer to the instance data for the current context. */
602typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
603
604#ifdef IN_RING3
605static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
606static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
607
608/**
609 * Helper function used when logging state of a VM thread.
610 *
611 * @param Thread
612 *
613 * @return Associated name of thread as a pointer to a zero-terminated string.
614 */
615DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
616{
617 if (!pThread)
618 return "<null>";
619
620 switch(pThread->enmState)
621 {
622 case PDMTHREADSTATE_INVALID:
623 return "invalid state";
624 case PDMTHREADSTATE_INITIALIZING:
625 return "initializing";
626 case PDMTHREADSTATE_SUSPENDING:
627 return "suspending";
628 case PDMTHREADSTATE_SUSPENDED:
629 return "suspended";
630 case PDMTHREADSTATE_RESUMING:
631 return "resuming";
632 case PDMTHREADSTATE_RUNNING:
633 return "running";
634 case PDMTHREADSTATE_TERMINATING:
635 return "terminating";
636 case PDMTHREADSTATE_TERMINATED:
637 return "terminated";
638 default:
639 return "unknown state";
640 }
641}
642#endif
643
644/**
645 * Wakeup PDM managed downstream (e.g. hierarchically inferior device's) RX thread
646 */
647static DECLCALLBACK(void) virtioNetWakeupRxBufWaiter(PPDMDEVINS pDevIns)
648{
649 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
650
651 AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
652
653 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
654 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
655 {
656 Log10Func(("[%s] Waking downstream device's Rx buf waiter thread\n", pThis->szInst));
657 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
658 AssertRC(rc);
659 }
660}
661
662/**
663 * Guest notifying us of its activity with a queue. Figure out which queue and respond accordingly.
664 *
665 * @callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
666 */
667static DECLCALLBACK(void) virtioNetVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
668{
669 RT_NOREF(pVirtio);
670 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
671
672 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
673 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
674
675#ifdef VIRTIO_REL_INFO_DUMP
676 if (ASMAtomicReadBool(&pVirtio->fRecovering))
677 LogRel(("[%s] Received notification from the guest on queue %u\n", pThis->szInst, uVirtqNbr));
678#endif /* VIRTIO_REL_INFO_DUMP */
679#if defined (IN_RING3) && defined (LOG_ENABLED)
680 RTLogFlush(NULL);
681#endif
682 if (IS_RX_VIRTQ(uVirtqNbr))
683 {
684 uint16_t cBufsAvailable = virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr);
685
686 if (cBufsAvailable)
687 {
688 Log10Func(("%s %u empty bufs added to %s by guest (notifying leaf device)\n",
689 pThis->szInst, cBufsAvailable, pVirtq->szName));
690 virtioNetWakeupRxBufWaiter(pDevIns);
691 }
692 else
693 Log10Func(("%s \n\n***WARNING: %s notified but no empty bufs added by guest! (skip leaf dev. notification)\n\n",
694 pThis->szInst, pVirtq->szName));
695 }
696 else if (IS_TX_VIRTQ(uVirtqNbr) || IS_CTRL_VIRTQ(uVirtqNbr))
697 {
698 /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
699 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
700 {
701 if (ASMAtomicReadBool(&pWorker->fSleeping))
702 {
703 Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
704
705 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
706 AssertRC(rc);
707 }
708 else
709 Log10Func(("[%s] %s has available buffers - worker already awake\n", pThis->szInst, pVirtq->szName));
710 }
711 else
712 Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
713 }
714 else
715 LogRelFunc(("[%s] unrecognized queue %s (idx=%d) notified\n", pThis->szInst, pVirtq->szName, uVirtqNbr));
716}
717
718#ifdef IN_RING3 /* spans most of the file, at the moment. */
719
720/**
721 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
722 */
723static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
724{
725 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
726 PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
727
728 Log10Func(("[%s]\n", pThis->szInst));
729 RT_NOREF(pThis);
730 return PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
731}
732
733/**
734 * Set queue names, distinguishing between modern or legacy mode.
735 *
736 * @note This makes it obvious during logging which mode this transitional device is
737 * operating in, legacy or modern.
738 *
739 * @param pThis Device specific device state
740 * @param fLegacy (input) true if running in legacy mode
741 * false if running in modern mode
742 */
743DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis, uint32_t fLegacy)
744{
745 RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, fLegacy ? "legacy-ctrlq" : " modern-ctrlq");
746 for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
747 {
748 RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-recvq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
749 RTStrPrintf(pThis->aVirtqs[TXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-xmitq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
750 }
751}
752
753/**
754 * Dump a packet to debug log.
755 *
756 * @param pThis The virtio-net shared instance data.
757 * @param pbPacket The packet.
758 * @param cb The size of the packet.
759 * @param pszText A string denoting direction of packet transfer.
760 */
761DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
762{
763#ifdef LOG_ENABLED
764 if (!LogIs12Enabled())
765 return;
766#endif
767 vboxEthPacketDump(pThis->szInst, pszText, pbPacket, (uint32_t)cb);
768}
769
770#ifdef LOG_ENABLED
771void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONETPKTHDR pRxPktHdr,
772 uint16_t cVirtqBufs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS GCPhysRxBuf, uint8_t cbRxBuf)
773{
774 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
775 pRxPktHdr->uNumBuffers = cVirtqBufs;
776 if (pRxPktHdr)
777 {
778 LogFunc(("%*c\nrxPktHdr\n"
779 " uFlags ......... %2.2x\n uGsoType ....... %2.2x\n uHdrLen ........ %4.4x\n"
780 " uGsoSize ....... %4.4x\n uChksumStart ... %4.4x\n uChksumOffset .. %4.4x\n",
781 60, ' ', pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
782 pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset));
783 if (!virtioCoreIsLegacyMode(&pThis->Virtio) || FEATURE_ENABLED(MRG_RXBUF))
784 LogFunc((" uNumBuffers .... %4.4x\n", pRxPktHdr->uNumBuffers));
785 virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONETPKTHDR), 0, "Dump of virtual rPktHdr");
786 }
787 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
788 LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
789 virtioCoreGCPhysHexDump(pDevIns, GCPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
790 LogFunc(("%*c", 60, '-'));
791}
792
793#endif /* LOG_ENABLED */
794
795/**
796 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
797 */
798#ifdef VIRTIO_REL_INFO_DUMP
799DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
800#else /* !VIRTIO_REL_INFO_DUMP */
801static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
802#endif /* !VIRTIO_REL_INFO_DUMP */
803{
804 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
805 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
806
807 bool fNone = pszArgs && *pszArgs == '\0';
808 bool fAll = pszArgs && (*pszArgs == 'a' || *pszArgs == 'A'); /* "all" */
809 bool fNetwork = pszArgs && (*pszArgs == 'n' || *pszArgs == 'N'); /* "network" */
810 bool fFeatures = pszArgs && (*pszArgs == 'f' || *pszArgs == 'F'); /* "features" */
811 bool fState = pszArgs && (*pszArgs == 's' || *pszArgs == 'S'); /* "state" */
812 bool fPointers = pszArgs && (*pszArgs == 'p' || *pszArgs == 'P'); /* "pointers" */
813 bool fVirtqs = pszArgs && (*pszArgs == 'q' || *pszArgs == 'Q'); /* "queues */
814#ifdef VIRTIO_REL_INFO_DUMP
815 if (pszArgs && (*pszArgs == 't' || *pszArgs == 'T'))
816 {
817 ASMAtomicWriteBool(&pThis->Virtio.fTestRecovery, true);
818 pHlp->pfnPrintf(pHlp, "Triggering recovery for %s\n", pThis->szInst);
819 return;
820 }
821#endif /* VIRTIO_REL_INFO_DUMP */
822
823 /* Show basic information. */
824 pHlp->pfnPrintf(pHlp,
825 "\n"
826 "---------------------------------------------------------------------------\n"
827 "Debug Info: %s\n"
828 " (options: [a]ll, [n]et, [f]eatures, [s]tate, [p]ointers, [q]ueues)\n"
829 "---------------------------------------------------------------------------\n\n",
830 pThis->szInst);
831
832 if (fNone)
833 return;
834
835 /* Show offered/unoffered, accepted/rejected features */
836 if (fAll || fFeatures)
837 {
838 virtioCorePrintDeviceFeatures(&pThis->Virtio, pHlp, s_aDevSpecificFeatures,
839 RT_ELEMENTS(s_aDevSpecificFeatures));
840 pHlp->pfnPrintf(pHlp, "\n");
841 }
842
843 /* Show queues (and associate worker info if applicable) */
844 if (fAll || fVirtqs)
845 {
846 pHlp->pfnPrintf(pHlp, "Virtq information:\n\n");
847 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
848 {
849 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
850
851 if (pVirtq->fHasWorker)
852 {
853 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
854 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
855
856 Assert((pWorker->uIdx == pVirtq->uIdx));
857 Assert((pWorkerR3->uIdx == pVirtq->uIdx));
858
859 if (pWorker->fAssigned)
860 {
861 pHlp->pfnPrintf(pHlp, " %-15s (pThread: %p %s) ",
862 pVirtq->szName,
863 pWorkerR3->pThread,
864 virtioNetThreadStateName(pWorkerR3->pThread));
865 if (pVirtq->fAttachedToVirtioCore)
866 {
867 pHlp->pfnPrintf(pHlp, "worker: ");
868 pHlp->pfnPrintf(pHlp, "%s", pWorker->fSleeping ? "blocking" : "unblocked");
869 pHlp->pfnPrintf(pHlp, "%s", pWorker->fNotified ? ", notified" : "");
870 }
871 else
872 if (pWorker->fNotified)
873 pHlp->pfnPrintf(pHlp, "not attached to virtio core");
874 }
875 }
876 else
877 {
878 pHlp->pfnPrintf(pHlp, " %-15s (INetworkDown's thread) %s", pVirtq->szName,
879 pVirtq->fAttachedToVirtioCore ? "" : "not attached to virtio core");
880 }
881 pHlp->pfnPrintf(pHlp, "\n");
882 virtioCoreR3VirtqInfo(pDevIns, pHlp, &pThis->Virtio, pszArgs, uVirtqNbr);
883 pHlp->pfnPrintf(pHlp, " ---------------------------------------------------------------------\n");
884 pHlp->pfnPrintf(pHlp, "\n");
885 }
886 pHlp->pfnPrintf(pHlp, "\n");
887 }
888
889 /* Show various pointers */
890 if (fAll || fPointers)
891 {
892 pHlp->pfnPrintf(pHlp, "Internal Pointers (for instance \"%s\"):\n\n", pThis->szInst);
893 pHlp->pfnPrintf(pHlp, " pDevIns ................... %p\n", pDevIns);
894 pHlp->pfnPrintf(pHlp, " PVIRTIOCORE ............... %p\n", &pThis->Virtio);
895 pHlp->pfnPrintf(pHlp, " PVIRTIONET ................ %p\n", pThis);
896 pHlp->pfnPrintf(pHlp, " PVIRTIONETCC .............. %p\n", pThisCC);
897 pHlp->pfnPrintf(pHlp, " VIRTIONETVIRTQ[] .......... %p\n", pThis->aVirtqs);
898 pHlp->pfnPrintf(pHlp, " pDrvBase .................. %p\n", pThisCC->pDrvBase);
899 pHlp->pfnPrintf(pHlp, " pDrv ...................... %p\n", pThisCC->pDrv);
900 pHlp->pfnPrintf(pHlp, "\n");
901 }
902
903 /* Show device state info */
904 if (fAll || fState)
905 {
906 pHlp->pfnPrintf(pHlp, "Device state:\n\n");
907 uint32_t fTransmitting = ASMAtomicReadU32(&pThis->uIsTransmitting);
908
909 pHlp->pfnPrintf(pHlp, " Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
910 pHlp->pfnPrintf(pHlp, "\n");
911 pHlp->pfnPrintf(pHlp, "Misc state\n");
912 pHlp->pfnPrintf(pHlp, "\n");
913 pHlp->pfnPrintf(pHlp, " fOfferLegacy .............. %d\n", pThis->fOfferLegacy);
914 pHlp->pfnPrintf(pHlp, " fVirtioReady .............. %d\n", pThis->fVirtioReady);
915 pHlp->pfnPrintf(pHlp, " fResetting ................ %d\n", pThis->fResetting);
916 pHlp->pfnPrintf(pHlp, " fGenUpdatePending ......... %d\n", pThis->Virtio.fGenUpdatePending);
917 pHlp->pfnPrintf(pHlp, " fMsiSupport ............... %d\n", pThis->Virtio.fMsiSupport);
918 pHlp->pfnPrintf(pHlp, " uConfigGeneration ......... %d\n", pThis->Virtio.uConfigGeneration);
919 pHlp->pfnPrintf(pHlp, " uDeviceStatus ............. 0x%x\n", pThis->Virtio.fDeviceStatus);
920 pHlp->pfnPrintf(pHlp, " cVirtqPairs .,............. %d\n", pThis->cVirtqPairs);
921 pHlp->pfnPrintf(pHlp, " cVirtqs .,................. %d\n", pThis->cVirtqs);
922 pHlp->pfnPrintf(pHlp, " cWorkers .................. %d\n", pThis->cWorkers);
923 pHlp->pfnPrintf(pHlp, " MMIO mapping name ......... %s\n", pThisCC->Virtio.szMmioName);
924 pHlp->pfnPrintf(pHlp, "\n");
925 }
926
927 /* Show network related information */
928 if (fAll || fNetwork)
929 {
930 pHlp->pfnPrintf(pHlp, "Network configuration:\n\n");
931 pHlp->pfnPrintf(pHlp, " MAC: ...................... %RTmac\n", &pThis->macConfigured);
932 pHlp->pfnPrintf(pHlp, "\n");
933 pHlp->pfnPrintf(pHlp, " Cable: .................... %s\n", pThis->fCableConnected ? "connected" : "disconnected");
934 pHlp->pfnPrintf(pHlp, " Link-up delay: ............ %d ms\n", pThis->cMsLinkUpDelay);
935 pHlp->pfnPrintf(pHlp, "\n");
936 pHlp->pfnPrintf(pHlp, " Accept all multicast: ..... %s\n", pThis->fAllMulticast ? "true" : "false");
937 pHlp->pfnPrintf(pHlp, " Suppress broadcast: ....... %s\n", pThis->fNoBroadcast ? "true" : "false");
938 pHlp->pfnPrintf(pHlp, " Suppress unicast: ......... %s\n", pThis->fNoUnicast ? "true" : "false");
939 pHlp->pfnPrintf(pHlp, " Suppress multicast: ....... %s\n", pThis->fNoMulticast ? "true" : "false");
940 pHlp->pfnPrintf(pHlp, " Promiscuous: .............. %s\n", pThis->fPromiscuous ? "true" : "false");
941 pHlp->pfnPrintf(pHlp, "\n");
942 pHlp->pfnPrintf(pHlp, " Default Rx MAC filter: .... %RTmac\n", &pThis->rxFilterMacDefault);
943 pHlp->pfnPrintf(pHlp, "\n");
944
945 pHlp->pfnPrintf(pHlp, " Unicast filter MACs:\n");
946
947 if (!pThis->cUnicastFilterMacs)
948 pHlp->pfnPrintf(pHlp, " <none>\n");
949
950 for (uint32_t i = 0; i < pThis->cUnicastFilterMacs; i++)
951 pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacUnicastFilter[i]);
952
953 pHlp->pfnPrintf(pHlp, "\n Multicast filter MACs:\n");
954
955 if (!pThis->cMulticastFilterMacs)
956 pHlp->pfnPrintf(pHlp, " <none>\n");
957
958 for (uint32_t i = 0; i < pThis->cMulticastFilterMacs; i++)
959 pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacMulticastFilter[i]);
960
961 pHlp->pfnPrintf(pHlp, "\n\n");
962 pHlp->pfnPrintf(pHlp, " Leaf starved: ............. %s\n", pThis->fLeafWantsEmptyRxBufs ? "true" : "false");
963 pHlp->pfnPrintf(pHlp, "\n");
964 }
965 /** @todo implement this
966 * pHlp->pfnPrintf(pHlp, "\n");
967 * virtioCoreR3Info(pDevIns, pHlp, pszArgs);
968 */
969 pHlp->pfnPrintf(pHlp, "\n");
970}
971
972/**
973 * Checks whether certain mutually dependent negotiated features are clustered in required combinations.
974 *
975 * @note See VirtIO 1.0 spec, Section 5.1.3.1
976 *
977 * @param fFeatures Bitmask of negotiated features to evaluate
978 *
979 * @returns true if valid feature combination(s) found.
980 * false if non-valid feature set.
981 */
982DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
983{
984 uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
985 || fFeatures & VIRTIONET_F_GUEST_TSO6
986 || fFeatures & VIRTIONET_F_GUEST_UFO;
987
988 uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
989 || fFeatures & VIRTIONET_F_HOST_TSO6
990 || fFeatures & VIRTIONET_F_HOST_UFO;
991
992 uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
993 || fFeatures & VIRTIONET_F_CTRL_VLAN
994 || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
995 || fFeatures & VIRTIONET_F_MQ
996 || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
997
998 if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
999 return false;
1000
1001 if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
1002 return false;
1003
1004 if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
1005 return false;
1006
1007 if ( fFeatures & VIRTIONET_F_GUEST_ECN
1008 && !( fFeatures & VIRTIONET_F_GUEST_TSO4
1009 || fFeatures & VIRTIONET_F_GUEST_TSO6))
1010 return false;
1011
1012 if ( fFeatures & VIRTIONET_F_HOST_ECN
1013 && !( fFeatures & VIRTIONET_F_HOST_TSO4
1014 || fFeatures & VIRTIONET_F_HOST_TSO6))
1015 return false;
1016 return true;
1017}
1018
1019/**
1020 * Read or write device-specific configuration parameters.
1021 * This is called by VirtIO core code a guest-initiated MMIO access is made to access device-specific
1022 * configuration
1023 *
1024 * @note See VirtIO 1.0 spec, 2.3 Device Configuration Space
1025 *
1026 * @param pThis Pointer to device-specific state
1027 * @param uOffsetOfAccess Offset (within VIRTIONET_CONFIG_T)
1028 * @param pv Pointer to data to read or write
1029 * @param cb Number of bytes to read or write
1030 * @param fWrite True if writing, false if reading
1031 *
1032 * @returns VINF_SUCCESS if successful, or VINF_IOM_MMIO_UNUSED if fails (bad offset or size)
1033 */
1034static int virtioNetR3DevCfgAccess(PVIRTIONET pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
1035{
1036 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
1037
1038 if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess))
1039 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
1040#if FEATURE_OFFERED(STATUS)
1041 else
1042 if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess))
1043 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
1044#endif
1045#if FEATURE_OFFERED(MQ)
1046 else
1047 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess))
1048 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
1049#endif
1050 else
1051 {
1052 LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n",
1053 pThis->szInst, uOffsetOfAccess, uOffsetOfAccess, cb));
1054 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
1055 }
1056#ifdef VIRTIO_REL_INFO_DUMP
1057 if (ASMAtomicReadBool(&pThis->Virtio.fRecovering))
1058 LogRel(("[%s] %s %u bytes @ dev cfg + %u: %.*Rhxs\n", pThis->szInst, fWrite ? "wrote" : "read", cb, uOffsetOfAccess, cb, pv));
1059#endif /* VIRTIO_REL_INFO_DUMP */
1060 return VINF_SUCCESS;
1061}
1062
1063/**
1064 * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
1065 */
1066static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
1067{
1068 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1069
1070 RT_NOREF(pThis);
1071 return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
1072}
1073
1074/**
1075 * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
1076 */
1077static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1078{
1079 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1080
1081 Log10Func(("[%s] uOffset: %u, cb: %u: %.*Rhxs\n", pThis->szInst, uOffset, cb, cb, pv));
1082 RT_NOREF(pThis);
1083 return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
1084}
1085
1086static int virtioNetR3VirtqDestroy(PVIRTIOCORE pVirtio, PVIRTIONETVIRTQ pVirtq)
1087{
1088 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
1089 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pVirtio->pDevInsR3, PVIRTIONETCC);
1090 PVIRTIONETWORKER pWorker = &pThis->aWorkers[pVirtq->uIdx];
1091 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[pVirtq->uIdx];
1092
1093 int rc = VINF_SUCCESS, rcThread;
1094 Log10Func(("[%s] Destroying \"%s\"", pThis->szInst, pVirtq->szName));
1095 if (pVirtq->fHasWorker)
1096 {
1097 Log10((" and its worker"));
1098 rc = PDMDevHlpThreadDestroy(pVirtio->pDevInsR3, pWorkerR3->pThread, &rcThread);
1099 AssertRCReturn(rc, rc);
1100 pWorkerR3->pThread = 0;
1101
1102 rc = PDMDevHlpSUPSemEventClose(pVirtio->pDevInsR3, pWorker->hEvtProcess);
1103 AssertRCReturn(rc, rc);
1104 pWorker->hEvtProcess = 0;
1105
1106 pVirtq->fHasWorker = false;
1107 }
1108 pWorker->fAssigned = false;
1109 pVirtq->fCtlVirtq = false;
1110 Log10(("\n"));
1111 return rc;
1112}
1113
1114/**
1115 * Takes down the link temporarily if its current status is up.
1116 *
1117 * This is used during restore and when replumbing the network link.
1118 *
1119 * The temporary link outage is supposed to indicate to the OS that all network
1120 * connections have been lost and that it for instance is appropriate to
1121 * renegotiate any DHCP lease.
1122 *
1123 * @param pDevIns The device instance.
1124 * @param pThis The virtio-net shared instance data.
1125 * @param pThisCC The virtio-net ring-3 instance data.
1126 */
1127static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
1128{
1129 if (IS_LINK_UP(pThis))
1130 {
1131 SET_LINK_DOWN(pThis);
1132
1133 /* Re-establish link in 5 seconds. */
1134 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
1135 AssertRC(rc);
1136
1137 LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
1138 }
1139}
1140
1141
1142static void virtioNetConfigurePktHdr(PVIRTIONET pThis, uint32_t fLegacy)
1143{
1144 /* Calculate network packet header type and size based on what we know now */
1145 pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
1146 if (!fLegacy)
1147 /* Modern (e.g. >= VirtIO 1.0) device specification's pkt size rules */
1148 if (FEATURE_ENABLED(MRG_RXBUF))
1149 pThis->ePktHdrType = kVirtioNetModernPktHdrWithMrgRx;
1150 else /* Modern guest driver with MRG_RX feature disabled */
1151 pThis->ePktHdrType = kVirtioNetModernPktHdrWithoutMrgRx;
1152 else
1153 {
1154 /* Legacy (e.g. < VirtIO 1.0) device specification's pkt size rules */
1155 if (FEATURE_ENABLED(MRG_RXBUF))
1156 pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithMrgRx;
1157 else /* Legacy guest with MRG_RX feature disabled */
1158 {
1159 pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
1160 pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
1161 }
1162 }
1163}
1164
1165
1166/*********************************************************************************************************************************
1167* Saved state *
1168*********************************************************************************************************************************/
1169
1170/**
1171 * @callback_method_impl{FNSSMDEVLOADEXEC}
1172 *
1173 * @note: This is included to accept and migrate VMs that had used the original VirtualBox legacy-only virtio-net (network card)
1174 * controller device emulator ("DevVirtioNet.cpp") to work with this superset of VirtIO compatibility known
1175 * as a transitional device (see PDM-invoked device constructor comments for more information)
1176 */
1177static DECLCALLBACK(int) virtioNetR3LegacyDeviceLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass,
1178 RTMAC uMacLoaded)
1179{
1180 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1181 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1182 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1183 int rc;
1184
1185 Log7Func(("[%s] LOAD EXEC (LEGACY)!!\n", pThis->szInst));
1186
1187 if ( memcmp(&uMacLoaded.au8, &pThis->macConfigured.au8, sizeof(uMacLoaded))
1188 && ( uPass == 0
1189 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1190 LogRelFunc(("[%s]: The mac address differs: config=%RTmac saved=%RTmac\n",
1191 pThis->szInst, &pThis->macConfigured, &uMacLoaded));
1192
1193 if (uPass == SSM_PASS_FINAL)
1194 {
1195 /* Call the virtio core to have it load legacy device state */
1196 rc = virtioCoreR3LegacyDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion, VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY);
1197 AssertRCReturn(rc, rc);
1198 /*
1199 * Scan constructor-determined virtqs to determine if they are all valid-as-restored.
1200 * If so, nudge them with a signal, otherwise destroy the unusable queue(s)
1201 * to avoid tripping up the other queue processing logic.
1202 */
1203 int cVirtqsToRemove = 0;
1204 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1205 {
1206 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
1207 if (pVirtq->fHasWorker)
1208 {
1209 if (!virtioCoreR3VirtqIsEnabled(&pThis->Virtio, uVirtqNbr))
1210 {
1211 virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
1212 ++cVirtqsToRemove;
1213 }
1214 else
1215 {
1216 if (virtioCoreR3VirtqIsAttached(&pThis->Virtio, uVirtqNbr))
1217 {
1218 Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
1219 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[pVirtq->uIdx].hEvtProcess);
1220 AssertRCReturn(rc, rc);
1221 }
1222 }
1223 }
1224 }
1225 AssertMsg(cVirtqsToRemove < 2, ("Multiple unusable queues in saved state unexpected\n"));
1226 pThis->cVirtqs -= cVirtqsToRemove;
1227
1228 pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus;
1229 pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
1230
1231 rc = pHlp->pfnSSMGetMem(pSSM, pThis->virtioNetConfig.uMacAddress.au8, sizeof(pThis->virtioNetConfig.uMacAddress));
1232 AssertRCReturn(rc, rc);
1233
1234 if (uVersion > VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY)
1235 {
1236 /* Zero-out the the Unicast/Multicast filter table */
1237 memset(&pThis->aMacUnicastFilter[0], 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1238
1239 rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
1240 AssertRCReturn(rc, rc);
1241 rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
1242 AssertRCReturn(rc, rc);
1243 /*
1244 * The 0.95 legacy virtio spec defines a control queue command VIRTIO_NET_CTRL_MAC_TABLE_SET,
1245 * wherein guest driver configures two variable length mac filter tables: A unicast filter,
1246 * and a multicast filter. However original VBox virtio-net saved both sets of filter entries
1247 * in a single table, abandoning the distinction between unicast and multicast filters. It preserved
1248 * only *one* filter's table length, leaving no way to separate table back out into respective unicast
1249 * and multicast tables this device implementation preserves. Deduced from legacy code, the original
1250 * assumption was that the both MAC filters are whitelists that can be processed identically
1251 * (from the standpoint of a *single* host receiver), such that the distinction between unicast and
1252 * multicast doesn't matter in any one VM's context. Little choice here but to save the undifferentiated
1253 * unicast & multicast MACs to the unicast filter table and leave multicast table empty/unused.
1254 */
1255 uint32_t cCombinedUnicastMulticastEntries;
1256 rc = pHlp->pfnSSMGetU32(pSSM, &cCombinedUnicastMulticastEntries);
1257 AssertRCReturn(rc, rc);
1258 AssertReturn(cCombinedUnicastMulticastEntries <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1259 pThis->cUnicastFilterMacs = cCombinedUnicastMulticastEntries;
1260 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aMacUnicastFilter, cCombinedUnicastMulticastEntries * sizeof(RTMAC));
1261 AssertRCReturn(rc, rc);
1262 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1263 AssertRCReturn(rc, rc);
1264 }
1265 else
1266 {
1267 pThis->fAllMulticast = false;
1268 pThis->cUnicastFilterMacs = 0;
1269 memset(&pThis->aMacUnicastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1270
1271 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
1272
1273 pThis->fPromiscuous = true;
1274 if (pThisCC->pDrv)
1275 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
1276 }
1277
1278 /*
1279 * Log the restored VirtIO feature selection.
1280 */
1281 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
1282 /** @todo shouldn't we update the virtio header size here? it depends on the negotiated features. */
1283 virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
1284
1285 /*
1286 * Configure remaining transitional device parameters presumably or deductively
1287 * as these weren't part of the legacy device code thus it didn't save them to SSM
1288 */
1289 pThis->fCableConnected = 1;
1290 pThis->fAllUnicast = 0;
1291 pThis->fNoMulticast = 0;
1292 pThis->fNoUnicast = 0;
1293 pThis->fNoBroadcast = 0;
1294
1295 /* Zero out the multicast table and count, all MAC filters, if any, are in the unicast filter table */
1296 pThis->cMulticastFilterMacs = 0;
1297 memset(&pThis->aMacMulticastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
1298 }
1299 return VINF_SUCCESS;
1300}
1301
1302/**
1303 * @callback_method_impl{FNSSMDEVLOADEXEC}
1304 *
1305 * @note: This loads state saved by a Modern (VirtIO 1.0+) device, of which this transitional device is one,
1306 * and thus supports both legacy and modern guest virtio drivers.
1307 */
1308static DECLCALLBACK(int) virtioNetR3ModernLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1309{
1310 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1311 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1312 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1313 int rc;
1314
1315 RT_NOREF(pThisCC);
1316
1317 RTMAC uMacLoaded, uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
1318 rc = pHlp->pfnSSMGetMem(pSSM, &uMacLoaded.au8, sizeof(uMacLoaded.au8));
1319 AssertRCReturn(rc, rc);
1320 if (memcmp(&uMacLoaded.au8, uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8)))
1321 {
1322 rc = virtioNetR3LegacyDeviceLoadExec(pDevIns, pSSM, uVersion, uPass, uMacLoaded);
1323 return rc;
1324 }
1325
1326 Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
1327
1328 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
1329 AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVEDSTATE_VERSION,
1330 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1331
1332 virtioNetR3SetVirtqNames(pThis, false /* fLegacy */);
1333
1334 pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
1335
1336 pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtqs);
1337 AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2) + 1, VERR_OUT_OF_RANGE);
1338 pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
1339 AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS , VERR_OUT_OF_RANGE);
1340
1341 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1342 pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1343
1344 /* Config checks */
1345 RTMAC macConfigured;
1346 rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
1347 AssertRCReturn(rc, rc);
1348 if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
1349 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1350 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
1351 pThis->szInst, &pThis->macConfigured, &macConfigured));
1352 memcpy(pThis->virtioNetConfig.uMacAddress.au8, macConfigured.au8, sizeof(macConfigured.au8));
1353
1354#if FEATURE_OFFERED(STATUS)
1355 uint16_t fChkStatus;
1356 pHlp->pfnSSMGetU16( pSSM, &fChkStatus);
1357 if (fChkStatus == 0xffff)
1358 {
1359 /* Dummy value in saved state because status feature wasn't enabled at the time */
1360 pThis->virtioNetConfig.uStatus = 0; /* VIRTIO_NET_S_ANNOUNCE disabled */
1361 pThis->virtioNetConfig.uStatus = !!IS_LINK_UP(pThis); /* VIRTIO_NET_IS_LINK_UP (bit 0) */
1362 }
1363 else
1364 pThis->virtioNetConfig.uStatus = fChkStatus;
1365#else
1366 uint16_t fDiscard;
1367 pHlp->pfnSSMGetU16( pSSM, &fDiscard);
1368#endif
1369
1370#if FEATURE_OFFERED(MQ)
1371 uint16_t uCheckMaxVirtqPairs;
1372 pHlp->pfnSSMGetU16( pSSM, &uCheckMaxVirtqPairs);
1373 if (uCheckMaxVirtqPairs)
1374 pThis->virtioNetConfig.uMaxVirtqPairs = uCheckMaxVirtqPairs;
1375 else
1376 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_CTRL_MQ_VQ_PAIRS;
1377#else
1378 uint16_t fDiscard;
1379 pHlp->pfnSSMGetU16( pSSM, &fDiscard);
1380#endif
1381
1382 /* Save device-specific part */
1383 pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
1384 pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
1385 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
1386 pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
1387 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
1388 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
1389 pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
1390
1391 pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
1392 AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1393 pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1394
1395 if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1396 memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
1397 (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
1398
1399 pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
1400 AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
1401 pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1402
1403 if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
1404 memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
1405 (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
1406
1407 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1408 AssertRCReturn(rc, rc);
1409 /*
1410 * Call the virtio core to let it load its state.
1411 */
1412 rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion,
1413 VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
1414 AssertRCReturn(rc, rc);
1415 /*
1416 * Since the control queue is created proactively in the constructor to accomodate worst-case
1417 * legacy guests, even though the queue may have been deducted from queue count while saving state,
1418 * we must explicitly remove queue and associated worker thread and context at this point,
1419 * or presence of bogus control queue will confuse operations.
1420 */
1421 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[CTRLQIDX];
1422 if (FEATURE_DISABLED(CTRL_VQ) || !virtioCoreIsVirtqEnabled(&pThis->Virtio, CTRLQIDX))
1423 {
1424 virtioCoreR3VirtqDetach(&pThis->Virtio, CTRLQIDX);
1425 virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
1426 pVirtq->fAttachedToVirtioCore = false;
1427 --pThis->cWorkers;
1428 }
1429 /*
1430 * Nudge queue workers
1431 */
1432 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1433 {
1434 pVirtq = &pThis->aVirtqs[uVirtqNbr];
1435 if (pVirtq->fAttachedToVirtioCore)
1436 {
1437 if (pVirtq->fHasWorker)
1438 {
1439 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
1440 Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
1441 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
1442 AssertRCReturn(rc, rc);
1443 }
1444 }
1445 }
1446 pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus; /* reflects state to guest driver */
1447 pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
1448 virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
1449 return rc;
1450}
1451
1452/**
1453 * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
1454 * loading.}
1455 */
1456static DECLCALLBACK(int) virtioNetR3ModernLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1457{
1458 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1459 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1460 RT_NOREF(pSSM);
1461
1462 if (pThisCC->pDrv)
1463 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulticast));
1464
1465 /*
1466 * Indicate link down to the guest OS that all network connections have
1467 * been lost, unless we've been teleported here.
1468 */
1469 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1470 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
1471
1472 return VINF_SUCCESS;
1473}
1474
1475/**
1476 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1477 */
1478static DECLCALLBACK(int) virtioNetR3ModernSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1479{
1480 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1481 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1482 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1483
1484 RT_NOREF(pThisCC);
1485 Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
1486
1487 /* Store a dummy MAC address that would never be actually assigned to a NIC
1488 * so that when load exec handler is called it can be easily determined
1489 * whether saved state is modern or legacy. This works because original
1490 * legacy code stored assigned NIC address as the first item of SSM state
1491 */
1492 RTMAC uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
1493 pHlp->pfnSSMPutMem(pSSM, &uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8));
1494
1495 pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
1496
1497 pHlp->pfnSSMPutU16( pSSM, pThis->cVirtqs);
1498 pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
1499
1500 for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
1501 pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
1502 /*
1503
1504 * Save device config area (accessed via MMIO)
1505 */
1506 pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
1507 sizeof(pThis->virtioNetConfig.uMacAddress.au8));
1508#if FEATURE_OFFERED(STATUS)
1509 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uStatus);
1510#else
1511 /*
1512 * Relevant values are lower bits. Forcing this to 0xffff let's loadExec know this
1513 * feature was not enabled in saved state. VirtIO 1.0, 5.1.4
1514 */
1515 pHlp->pfnSSMPutU16( pSSM, 0xffff);
1516
1517#endif
1518#if FEATURE_OFFERED(MQ)
1519 pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
1520#else
1521 /*
1522 * Legal values for max_virtqueue_pairs are 0x1 -> 0x8000 *. Forcing zero let's loadExec know this
1523 * feature was not enabled in saved state. VirtIO 1.0, 5.1.4.1
1524 */
1525 pHlp->pfnSSMPutU16( pSSM, 0);
1526#endif
1527
1528 /* Save device-specific part */
1529 pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
1530 pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
1531 pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
1532 pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
1533 pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
1534 pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
1535 pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
1536
1537 pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
1538 pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
1539
1540 pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
1541 pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
1542
1543 int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1544 AssertRCReturn(rc, rc);
1545
1546 /*
1547 * Call the virtio core to let it save its state.
1548 */
1549 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
1550}
1551
1552
1553/*********************************************************************************************************************************
1554* Device interface. *
1555*********************************************************************************************************************************/
1556
1557#ifdef IN_RING3
1558
1559#ifdef VIRTIO_REL_INFO_DUMP
1560/**
1561 * Attempt to recover from the state when a descriptor loop is discovered in adapter queue.
1562 * The idea is to force device reset via setting the DEVICE_NEEDS_RESET bit in the device status field.
1563 * To prevent further accesses to the queue in invalid state we need to bring down the link temporarily.
1564 * Unfortunately it does not seem to be handled by Linux virtio at all, so the faulty queue will remain
1565 * disabled, preventing recovery.
1566 *
1567 * @param pDevIns PDM instance
1568 *
1569 */
1570DECLHIDDEN(int) virtioNetAttemptToRecover(PPDMDEVINS pDevIns)
1571{
1572 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1573 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
1574
1575 if (IS_LINK_UP(pThis))
1576 {
1577 SET_LINK_DOWN(pThis);
1578
1579 /* Re-establish link in 10 seconds. */
1580 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, 10000);
1581 AssertRC(rc);
1582
1583 LogRel(("[%s] Virtio disabled until device reset\n", pThis->szInst));
1584 pThis->fVirtioReady = false;
1585 }
1586 virtioCoreResetAll(&pThis->Virtio);
1587
1588 if (ASMAtomicXchgBool(&pThis->Virtio.fRecovering, true))
1589 LogRel(("[%s] Attempting recovery while in recovery!\n", pThis->szInst));
1590
1591 return VINF_SUCCESS;
1592}
1593#endif /* VIRTIO_REL_INFO_DUMP */
1594
1595/**
1596 * Perform 16-bit 1's compliment checksum on provided packet in accordance with VirtIO specification,
1597 * pertinent to VIRTIO_NET_F_CSUM feature, which 'offloads' the Checksum feature from the driver
1598 * to save processor cycles, which is ironic in our case, where the controller device ('network card')
1599 * is emulated on the virtualization host.
1600 *
1601 * @note See VirtIO 1.0 spec, 5.1.6.2 Packet Transmission
1602 *
1603 * @param pBuf Pointer to r/w buffer with any portion to calculate checksum for
1604 * @param cbSize Number of bytes to checksum
1605 * @param uStart Where to start the checksum within the buffer
1606 * @param uOffset Offset past uStart point in the buffer to store checksum result
1607 *
1608 */
1609DECLINLINE(void) virtioNetR3Calc16BitChecksum(uint8_t *pBuf, size_t cb, uint16_t uStart, uint16_t uOffset)
1610{
1611 AssertReturnVoid(uStart < cb);
1612 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cb);
1613
1614 uint32_t chksum = 0;
1615 uint16_t *pu = (uint16_t *)(pBuf + uStart);
1616
1617 cb -= uStart;
1618 while (cb > 1)
1619 {
1620 chksum += *pu++;
1621 cb -= 2;
1622 }
1623 if (cb)
1624 chksum += *(uint8_t *)pu;
1625 while (chksum >> 16)
1626 chksum = (chksum >> 16) + (chksum & 0xFFFF);
1627
1628 /* Store 1's compliment of calculated sum */
1629 *(uint16_t *)(pBuf + uStart + uOffset) = ~chksum;
1630}
1631
1632/**
1633 * Turns on/off the read status LED.
1634 *
1635 * @returns VBox status code.
1636 * @param pThis Pointer to the device state structure.
1637 * @param fOn New LED state.
1638 */
1639static void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
1640{
1641 if (fOn)
1642 pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
1643 else
1644 pThisR3->led.Actual.s.fReading = fOn;
1645}
1646
1647/**
1648 * Turns on/off the write status LED.
1649 *
1650 * @returns VBox status code.
1651 * @param pThis Pointer to the device state structure.
1652 * @param fOn New LED state.
1653 */
1654static void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
1655{
1656 if (fOn)
1657 pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
1658 else
1659 pThisR3->led.Actual.s.fWriting = fOn;
1660}
1661
1662/**
1663 * Check that the core is setup and ready and co-configured with guest virtio driver,
1664 * and verifies that the VM is running.
1665 *
1666 * @returns true if VirtIO core and device are in a running and operational state
1667 */
1668DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
1669{
1670 if (RT_LIKELY(pThis->fVirtioReady))
1671 {
1672 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1673 if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
1674 return true;
1675 }
1676 return false;
1677}
1678
1679/**
1680 * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
1681 * available. This must be called before the pfnRecieve() method is called.
1682 *
1683 * @remarks As a side effect this function enables queue notification
1684 * if it cannot receive because the queue is empty.
1685 * It disables notification if it can receive.
1686 *
1687 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1688 * @thread RX
1689 */
1690static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
1691{
1692 int rc = VERR_INVALID_STATE;
1693 Log8Func(("[%s] ", pThis->szInst));
1694 if (!virtioNetIsOperational(pThis, pDevIns))
1695 Log8(("No Rx bufs available. (VirtIO core not ready)\n"));
1696
1697 else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
1698 Log8(("[No Rx bufs available. (%s not enabled)\n", pRxVirtq->szName));
1699
1700 else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->uIdx))
1701 Log8(("No Rx bufs available. (%s empty)\n", pRxVirtq->szName));
1702
1703 else
1704 {
1705 Log8(("%s has %d empty guest bufs in avail ring\n", pRxVirtq->szName,
1706 virtioCoreVirtqAvailBufCount(pDevIns, &pThis->Virtio, pRxVirtq->uIdx)));
1707 rc = VINF_SUCCESS;
1708 }
1709#ifdef VIRTIO_REL_INFO_DUMP
1710 if (ASMAtomicReadBool(&pThis->Virtio.fRecovering))
1711 LogRel(("[%s] %sRX buffers available on %s\n", pThis->szInst, rc == VERR_INVALID_STATE ? "Enabling notify as no " : "", pRxVirtq->szName));
1712#endif /* VIRTIO_REL_INFO_DUMP */
1713 virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
1714 return rc;
1715}
1716
1717/**
1718 * Find an Rx queue that has Rx packets in it, if *any* do.
1719 *
1720 * @todo When multiqueue (MQ) mode is fully supported and tested, some kind of round-robin
1721 * or randomization scheme should probably be incorporated here.
1722 *
1723 * @returns true if Rx pkts avail on queue and sets pRxVirtq to point to queue w/pkts found
1724 * @thread RX
1725 *
1726 */
1727static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
1728{
1729 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
1730 {
1731 PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
1732 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
1733 {
1734 if (pRxVirtq)
1735 *pRxVirtq = pThisRxVirtq;
1736 return true;
1737 }
1738 }
1739 return false;
1740}
1741
1742/**
1743 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
1744 */
1745static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
1746{
1747 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
1748 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1749 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
1750
1751 if (!virtioNetIsOperational(pThis, pDevIns))
1752 return VERR_INTERRUPTED;
1753
1754 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1755 {
1756 Log10Func(("[%s] Rx bufs available, releasing waiter...\n", pThis->szInst));
1757 return VINF_SUCCESS;
1758 }
1759 if (!timeoutMs)
1760 return VERR_NET_NO_BUFFER_SPACE;
1761
1762 LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
1763
1764 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
1765 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
1766
1767 do {
1768 if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
1769 {
1770 Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
1771 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1772 return VINF_SUCCESS;
1773 }
1774 Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
1775
1776 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
1777
1778 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
1779 {
1780 LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
1781
1782 if (!virtioNetIsOperational(pThis, pDevIns))
1783 break;
1784
1785 continue;
1786 }
1787 if (RT_FAILURE(rc)) {
1788 LogFunc(("Waken due to failure %Rrc\n", rc));
1789 RTThreadSleep(1);
1790 }
1791 } while (virtioNetIsOperational(pThis, pDevIns));
1792
1793 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
1794 ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
1795
1796 Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
1797 return VERR_INTERRUPTED;
1798}
1799
1800/**
1801 * Sets up the GSO context according to the Virtio header.
1802 *
1803 * @param pGso The GSO context to setup.
1804 * @param pCtx The context descriptor.
1805 */
1806DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
1807{
1808 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1809
1810 if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
1811 {
1812 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
1813 return NULL;
1814 }
1815 switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
1816 {
1817 case VIRTIONET_HDR_GSO_TCPV4:
1818 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
1819 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1820 break;
1821 case VIRTIONET_HDR_GSO_TCPV6:
1822 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
1823 pGso->cbHdrsSeg = pPktHdr->uHdrLen;
1824 break;
1825 case VIRTIONET_HDR_GSO_UDP:
1826 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
1827 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
1828 break;
1829 default:
1830 return NULL;
1831 }
1832 if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
1833 pGso->offHdr2 = pPktHdr->uChksumStart;
1834 else
1835 {
1836 AssertMsgFailed(("GSO without checksum offloading!\n"));
1837 return NULL;
1838 }
1839 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1840 pGso->cbHdrsTotal = pPktHdr->uHdrLen;
1841 pGso->cbMaxSeg = pPktHdr->uGsoSize;
1842 /* Mark GSO frames with zero MSS as PDMNETWORKGSOTYPE_INVALID, so they will be ignored by send. */
1843 if (pPktHdr->uGsoType != VIRTIONET_HDR_GSO_NONE && pPktHdr->uGsoSize == 0)
1844 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1845 return pGso;
1846}
1847
1848/**
1849 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
1850 */
1851static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1852{
1853 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
1854 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
1855 memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
1856 return VINF_SUCCESS;
1857}
1858
1859/**
1860 * Returns true if it is a broadcast packet.
1861 *
1862 * @returns true if destination address indicates broadcast.
1863 * @param pvBuf The ethernet packet.
1864 */
1865DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
1866{
1867 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1868 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
1869}
1870
1871/**
1872 * Returns true if it is a multicast packet.
1873 *
1874 * @remarks returns true for broadcast packets as well.
1875 * @returns true if destination address indicates multicast.
1876 * @param pvBuf The ethernet packet.
1877 */
1878DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
1879{
1880 return (*(char*)pvBuf) & 1;
1881}
1882
1883/**
1884 * Determines if the packet is to be delivered to upper layer.
1885 *
1886 * @returns true if packet is intended for this node.
1887 * @param pThis Pointer to the state structure.
1888 * @param pvBuf The ethernet packet.
1889 * @param cb Number of bytes available in the packet.
1890 */
1891static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
1892{
1893
1894RT_NOREF(cb);
1895
1896#ifdef LOG_ENABLED
1897 if (LogIs11Enabled())
1898 {
1899 char *pszType;
1900 if (virtioNetR3IsMulticast(pvBuf))
1901 pszType = (char *)"mcast";
1902 else if (virtioNetR3IsBroadcast(pvBuf))
1903 pszType = (char *)"bcast";
1904 else
1905 pszType = (char *)"ucast";
1906
1907 LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
1908 pThis->virtioNetConfig.uMacAddress.au8,
1909 pThis->fPromiscuous ? " promisc" : "",
1910 pThis->fAllMulticast ? " all-mcast" : "",
1911 pvBuf, pszType));
1912 }
1913#endif
1914
1915 if (pThis->fPromiscuous) {
1916 Log11(("\n"));
1917 return true;
1918 }
1919
1920 /* Ignore everything outside of our VLANs */
1921 uint16_t *uPtr = (uint16_t *)pvBuf;
1922
1923 /* Compare TPID with VLAN Ether Type */
1924 if ( uPtr[6] == RT_H2BE_U16(0x8100)
1925 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
1926 {
1927 Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
1928 return false;
1929 }
1930
1931 if (virtioNetR3IsBroadcast(pvBuf))
1932 {
1933 Log11(("acpt (bcast)\n"));
1934#ifdef LOG_ENABLED
1935 if (LogIs12Enabled())
1936 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1937#endif
1938 return true;
1939 }
1940 if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
1941 {
1942 Log11(("acpt (all-mcast)\n"));
1943#ifdef LOG_ENABLED
1944 if (LogIs12Enabled())
1945 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1946#endif
1947 return true;
1948 }
1949
1950 if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
1951 {
1952 Log11(("acpt (to-node)\n"));
1953#ifdef LOG_ENABLED
1954 if (LogIs12Enabled())
1955 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1956#endif
1957 return true;
1958 }
1959
1960 for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
1961 {
1962 if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
1963 {
1964 Log11(("acpt (mcast whitelist)\n"));
1965#ifdef LOG_ENABLED
1966 if (LogIs12Enabled())
1967 virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
1968#endif
1969 return true;
1970 }
1971 }
1972
1973 for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
1974 if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
1975 {
1976 Log11(("acpt (ucast whitelist)\n"));
1977 return true;
1978 }
1979#ifdef LOG_ENABLED
1980 if (LogIs11Enabled())
1981 Log(("... reject\n"));
1982#endif
1983
1984 return false;
1985}
1986
1987
1988/**
1989 * This handles the case where Rx packet must be transfered to guest driver via multiple buffers using
1990 * copy tactics slower than preferred method using a single virtq buf. Yet this is an available option
1991 * for guests. Although cited in the spec it's to accomodate guest that perhaps have memory constraints
1992 * wherein guest may benefit from smaller buffers (see MRG_RXBUF feature), in practice it is seen
1993 * that without MRG_RXBUF the linux guest enqueues 'huge' multi-segment buffers so that the largest
1994 * conceivable Rx packet can be contained in a single buffer, where for most transactions most of that
1995 * memory will be unfilled, so it is typically both wasteful and *slower* to avoid MRG_RXBUF.
1996 *
1997 * As an optimization, this multi-buffer copy is only used when:
1998 *
1999 * A. Guest has negotiated MRG_RXBUF
2000 * B. Next packet in the Rx avail queue isn't big enough to contain Rx pkt hdr+data.
2001 *
2002 * Architecture is defined in VirtIO 1.1 5.1.6 (Device Operations), which has improved
2003 * wording over the VirtIO 1.0 specification, but, as an implementation note, there is one
2004 * ambiguity that needs clarification:
2005 *
2006 * The VirtIO 1.1, 5.1.6.4 explains something in a potentially misleading way. And note,
2007 * the VirtIO spec makes a document-wide assertion that the distinction between
2008 * "SHOULD" and "MUST" is to be taken quite literally.
2009 *
2010 * The confusion is that VirtIO 1.1, 5.1.6.3.1 essentially says guest driver "SHOULD" populate
2011 * Rx queue with buffers large enough to accomodate full pkt hdr + data. That's a grammatical
2012 * error (dangling participle).
2013 *
2014 * In practice we MUST assume "SHOULD" strictly applies to the word *populate*, -not- to buffer
2015 * size, because ultimately buffer minimum size is predicated on configuration parameters,
2016 * specifically, when MRG_RXBUF feature is disabled, the driver *MUST* provide Rx bufs
2017 * (if and when it can provide them), that are *large enough* to hold pkt hdr + payload.
2018 *
2019 * Therefore, proper interpretation of 5.1.6.3.1 is, the guest *should* (ideally) keep Rx virtq
2020 * populated with appropriately sized buffers to *prevent starvation* (i.e. starvation may be
2021 * unavoidable thus can't be prohibited). As it would be a ludicrous to presume 5.1.6.3.1 is
2022 * giving guests leeway to violate MRG_RXBUF feature buf size constraints.
2023 *
2024 * @param pDevIns PDM instance
2025 * @param pThis Device instance
2026 * @param pvBuf Pointer to incoming GSO Rx data from downstream device
2027 * @param cb Amount of data given
2028 * @param rxPktHdr Rx pkt Header that's been massaged into VirtIO semantics
2029 * @param pRxVirtq Pointer to Rx virtq
2030 * @param pVirtqBuf Initial virtq buffer to start copying Rx hdr/pkt to guest into
2031 *
2032 */
2033static int virtioNetR3RxPktMultibufXfer(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint8_t *pvPktBuf, size_t cb,
2034 PVIRTIONETPKTHDR pRxPktHdr, PVIRTIONETVIRTQ pRxVirtq, PVIRTQBUF pVirtqBuf)
2035{
2036
2037 size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
2038 size_t cbPktHdr = pThis->cbPktHdr;
2039
2040 AssertMsgReturn(cbBufRemaining >= pThis->cbPktHdr,
2041 ("guest-provided Rx buf not large enough to store pkt hdr"), VERR_INTERNAL_ERROR);
2042
2043 Log7Func((" Sending packet header to guest...\n"));
2044
2045 /* Copy packet header to rx buf provided by caller. */
2046 size_t cbHdrEnqueued = pVirtqBuf->cbPhysReturn == cbPktHdr ? cbPktHdr : 0;
2047 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, cbHdrEnqueued);
2048
2049 /* Cache address of uNumBuffers field of pkthdr to update ex post facto */
2050 RTGCPHYS GCPhysNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
2051 uint16_t cVirtqBufsUsed = 0;
2052 cbBufRemaining -= cbPktHdr;
2053 /*
2054 * Copy packet to guest using as many buffers as necessary, tracking and handling whether
2055 * the buf containing the packet header was already written to the Rx queue's used buffer ring.
2056 */
2057 uint64_t uPktOffset = 0;
2058 while(uPktOffset < cb)
2059 {
2060 Log7Func((" Sending packet data (in buffer #%d) to guest...\n", cVirtqBufsUsed));
2061 size_t cbBounded = RT_MIN(cbBufRemaining, cb - uPktOffset);
2062 (void) virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbBounded,
2063 pvPktBuf + uPktOffset, pVirtqBuf, cbBounded + (cbPktHdr - cbHdrEnqueued) /* cbEnqueue */);
2064 ++cVirtqBufsUsed;
2065 cbBufRemaining -= cbBounded;
2066 uPktOffset += cbBounded;
2067 if (uPktOffset < cb)
2068 {
2069 cbHdrEnqueued = cbPktHdr;
2070 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
2071#ifdef VIRTIO_REL_INFO_DUMP
2072 if (rc == VERR_INVALID_STATE)
2073 return virtioNetAttemptToRecover(pDevIns);
2074 if (rc == VERR_NOT_AVAILABLE)
2075 return VINF_SUCCESS;
2076#endif /* VIRTIO_REL_INFO_DUMP */
2077 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
2078 AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
2079 ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
2080 VERR_INTERNAL_ERROR);
2081 cbBufRemaining = pVirtqBuf->cbPhysReturn;
2082 }
2083 }
2084
2085 /* Fix-up pkthdr (in guest phys. memory) with number of buffers (descriptors) that were processed */
2086 int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysNumBuffers, &cVirtqBufsUsed, sizeof(cVirtqBufsUsed));
2087 AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
2088
2089 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
2090 Log7(("\n"));
2091 return rc;
2092}
2093
2094/**
2095 * Pad and store received packet.
2096 *
2097 * @remarks Make sure that the packet appears to upper layer as one coming
2098 * from real Ethernet: pad it and insert FCS.
2099 *
2100 * @returns VBox status code.
2101 * @param pDevIns The device instance.
2102 * @param pThis The virtio-net shared instance data.
2103 * @param pvBuf The available data.
2104 * @param cb Number of bytes available in the buffer.
2105 * @param pGso Pointer to Global Segmentation Offload structure
2106 * @param pRxVirtq Pointer to Rx virtqueue
2107 * @thread RX
2108 */
2109
2110static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, const void *pvBuf, size_t cb,
2111 PVIRTIONETPKTHDR pRxPktHdr, uint8_t cbPktHdr, PVIRTIONETVIRTQ pRxVirtq)
2112{
2113 RT_NOREF(pThisCC);
2114 VIRTQBUF_T VirtqBuf;
2115
2116 VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
2117 VirtqBuf.cRefs = 1;
2118
2119 PVIRTQBUF pVirtqBuf = &VirtqBuf;
2120 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
2121#ifdef VIRTIO_REL_INFO_DUMP
2122 if (rc == VERR_INVALID_STATE)
2123 return virtioNetAttemptToRecover(pDevIns);
2124 if (rc == VERR_NOT_AVAILABLE)
2125 return VINF_SUCCESS;
2126#endif /* VIRTIO_REL_INFO_DUMP */
2127 AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
2128 AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
2129 ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
2130 VERR_INTERNAL_ERROR);
2131
2132 /*
2133 * Try to do fast (e.g. single-buffer) copy to guest, even if MRG_RXBUF feature is enabled
2134 */
2135 STAM_PROFILE_START(&pThis->StatReceiveStore, a);
2136 if (RT_LIKELY(FEATURE_DISABLED(MRG_RXBUF))
2137 || RT_LIKELY(pVirtqBuf->cbPhysReturn > cb + cbPktHdr))
2138 {
2139 Log7Func(("Send Rx packet header and data to guest (single-buffer copy)...\n"));
2140 pRxPktHdr->uNumBuffers = 1;
2141 rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, 0 /* cbEnqueue */);
2142 if (rc == VINF_SUCCESS)
2143 rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cb, pvBuf, pVirtqBuf, cbPktHdr + cb /* cbEnqueue */);
2144 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
2145 AssertMsgReturn(rc == VINF_SUCCESS, ("%Rrc\n", rc), rc);
2146 }
2147 else
2148 {
2149 Log7Func(("Send Rx pkt to guest (merged-buffer copy [MRG_RXBUF feature])...\n"));
2150 rc = virtioNetR3RxPktMultibufXfer(pDevIns, pThis, (uint8_t *)pvBuf, cb, pRxPktHdr, pRxVirtq, pVirtqBuf);
2151 return rc;
2152 }
2153 STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
2154#ifdef VIRTIO_REL_INFO_DUMP
2155 if (ASMAtomicXchgBool(&pThis->Virtio.fRecovering, false))
2156 LogRel(("[%s] Recovery complete on successfully receiving a packet\n", pThis->szInst));
2157#endif /* VIRTIO_REL_INFO_DUMP */
2158 return VINF_SUCCESS;
2159}
2160
2161/**
2162 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
2163 */
2164static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb,
2165 PCPDMNETWORKGSO pGso)
2166{
2167 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2168 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2169 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2170 VIRTIONETPKTHDR rxPktHdr = { 0, VIRTIONET_HDR_GSO_NONE, 0, 0, 0, 0, 0 };
2171
2172 if (!pThis->fVirtioReady)
2173 {
2174 LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
2175 return VERR_INTERRUPTED;
2176 }
2177 /*
2178 * If GSO (Global Segment Offloading) was received from downstream PDM network device, massage the
2179 * PDM-provided GSO parameters into VirtIO semantics, which get passed to guest virtio-net via
2180 * Rx pkt header. See VirtIO 1.1, 5.1.6 Device Operation for more information.
2181 */
2182 if (pGso)
2183 {
2184 LogFunc(("[%s] (%RTmac) \n", pThis->szInst, pvBuf));
2185
2186 rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
2187 rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
2188 rxPktHdr.uGsoSize = pGso->cbMaxSeg;
2189 rxPktHdr.uChksumStart = pGso->offHdr2;
2190
2191 switch (pGso->u8Type)
2192 {
2193 case PDMNETWORKGSOTYPE_IPV4_TCP:
2194 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
2195 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
2196 break;
2197 case PDMNETWORKGSOTYPE_IPV6_TCP:
2198 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
2199 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
2200 break;
2201 case PDMNETWORKGSOTYPE_IPV4_UDP:
2202 rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
2203 rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
2204 break;
2205 default:
2206 LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
2207 return VERR_NOT_SUPPORTED;
2208 }
2209 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
2210 Log2Func(("[%s] gso type=%#x, cbHdrsTotal=%u cbHdrsSeg=%u mss=%u offHdr1=%#x offHdr2=%#x\n",
2211 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2212 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2213 }
2214
2215 /*
2216 * Find a virtq with Rx bufs on avail ring, if any, and copy the packet to the guest's Rx buffer.
2217 * @todo pk: PROBABLY NOT A SOPHISTICATED ENOUGH QUEUE SELECTION ALGORTITH FOR OPTIMAL MQ (FEATURE) SUPPORT
2218 */
2219 for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
2220 {
2221 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
2222 if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
2223 {
2224 int rc = VINF_SUCCESS;
2225 STAM_PROFILE_START(&pThis->StatReceive, a);
2226 virtioNetR3SetReadLed(pThisCC, true);
2227 if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
2228 {
2229 /* rxPktHdr is local stack variable that should not go out of scope in this use */
2230 rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pThisCC, pvBuf, cb, &rxPktHdr, pThis->cbPktHdr, pRxVirtq);
2231 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
2232 }
2233 virtioNetR3SetReadLed(pThisCC, false);
2234 STAM_PROFILE_STOP(&pThis->StatReceive, a);
2235 return rc;
2236 }
2237 }
2238 return VERR_INTERRUPTED;
2239}
2240
2241/**
2242 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
2243 */
2244static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
2245{
2246
2247#ifdef LOG_ENABLED
2248 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2249 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2250 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2251 LogFunc(("[%s] (%RTmac)\n", pThis->szInst, pvBuf));
2252#endif
2253
2254 return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
2255}
2256
2257/*
2258 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's Rx packet receive filtering.
2259 * See VirtIO 1.0, 5.1.6.5.1
2260 *
2261 * @param pThis virtio-net instance
2262 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2263 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2264 */
2265static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2266 PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2267{
2268
2269#define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
2270
2271 LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
2272 switch(pCtrlPktHdr->uCmd)
2273 {
2274 case VIRTIONET_CTRL_RX_PROMISC:
2275 break;
2276 case VIRTIONET_CTRL_RX_ALLMULTI:
2277 break;
2278 case VIRTIONET_CTRL_RX_ALLUNI:
2279 /* fallthrough */
2280 case VIRTIONET_CTRL_RX_NOMULTI:
2281 /* fallthrough */
2282 case VIRTIONET_CTRL_RX_NOUNI:
2283 /* fallthrough */
2284 case VIRTIONET_CTRL_RX_NOBCAST:
2285 AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
2286 ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
2287 VIRTIONET_ERROR);
2288 /* fall out */
2289 }
2290
2291 uint8_t fOn, fPromiscChanged = false;
2292 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
2293
2294 switch(pCtrlPktHdr->uCmd)
2295 {
2296 case VIRTIONET_CTRL_RX_PROMISC:
2297 pThis->fPromiscuous = RT_BOOL(fOn);
2298 fPromiscChanged = true;
2299 LOG_VIRTIONET_FLAG(fPromiscuous);
2300 break;
2301 case VIRTIONET_CTRL_RX_ALLMULTI:
2302 pThis->fAllMulticast = RT_BOOL(fOn);
2303 fPromiscChanged = true;
2304 LOG_VIRTIONET_FLAG(fAllMulticast);
2305 break;
2306 case VIRTIONET_CTRL_RX_ALLUNI:
2307 pThis->fAllUnicast = RT_BOOL(fOn);
2308 LOG_VIRTIONET_FLAG(fAllUnicast);
2309 break;
2310 case VIRTIONET_CTRL_RX_NOMULTI:
2311 pThis->fNoMulticast = RT_BOOL(fOn);
2312 LOG_VIRTIONET_FLAG(fNoMulticast);
2313 break;
2314 case VIRTIONET_CTRL_RX_NOUNI:
2315 pThis->fNoUnicast = RT_BOOL(fOn);
2316 LOG_VIRTIONET_FLAG(fNoUnicast);
2317 break;
2318 case VIRTIONET_CTRL_RX_NOBCAST:
2319 pThis->fNoBroadcast = RT_BOOL(fOn);
2320 LOG_VIRTIONET_FLAG(fNoBroadcast);
2321 break;
2322 }
2323
2324 if (pThisCC->pDrv && fPromiscChanged)
2325 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
2326
2327 return VIRTIONET_OK;
2328}
2329
2330/*
2331 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MAC filter tables
2332 * See VirtIO 1.0, 5.1.6.5.2
2333 *
2334 * @param pThis virtio-net instance
2335 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2336 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2337 */
2338static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2339{
2340 LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
2341
2342
2343 AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
2344 ("insufficient descriptor space for ctrl pkt hdr"),
2345 VIRTIONET_ERROR);
2346
2347 size_t cbRemaining = pVirtqBuf->cbPhysSend;
2348 switch(pCtrlPktHdr->uCmd)
2349 {
2350 case VIRTIONET_CTRL_MAC_ADDR_SET:
2351 {
2352 /* Set default Rx filter MAC */
2353 AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
2354 ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
2355
2356 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(RTMAC));
2357 break;
2358 }
2359 case VIRTIONET_CTRL_MAC_TABLE_SET:
2360 {
2361 VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
2362
2363 /* Load unicast MAC filter table */
2364 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
2365 ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2366
2367 /* Fetch count of unicast filter MACs from guest buffer */
2368 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
2369 cbRemaining -= sizeof(cMacs);
2370
2371 Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
2372
2373 AssertMsgReturn(cMacs <= RT_ELEMENTS(pThis->aMacUnicastFilter),
2374 ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
2375
2376 if (cMacs)
2377 {
2378 uint32_t cbMacs = cMacs * sizeof(RTMAC);
2379
2380 AssertMsgReturn(cbRemaining >= cbMacs,
2381 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2382
2383
2384 /* Fetch unicast table contents from guest buffer */
2385 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
2386 cbRemaining -= cbMacs;
2387 }
2388 pThis->cUnicastFilterMacs = cMacs;
2389
2390 /* Load multicast MAC filter table */
2391 AssertMsgReturn(cbRemaining >= sizeof(cMacs),
2392 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2393
2394 /* Fetch count of multicast filter MACs from guest buffer */
2395 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
2396 cbRemaining -= sizeof(cMacs);
2397
2398 Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
2399
2400 AssertMsgReturn(cMacs <= RT_ELEMENTS(pThis->aMacMulticastFilter),
2401 ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
2402
2403 if (cMacs)
2404 {
2405 uint32_t cbMacs = cMacs * sizeof(RTMAC);
2406
2407 AssertMsgReturn(cbRemaining >= cbMacs,
2408 ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
2409
2410 /* Fetch multicast table contents from guest buffer */
2411 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
2412 cbRemaining -= cbMacs;
2413 }
2414 pThis->cMulticastFilterMacs = cMacs;
2415
2416#ifdef LOG_ENABLED
2417 LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
2418 for(unsigned i = 0; i < pThis->cUnicastFilterMacs; i++)
2419 LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
2420
2421 LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
2422 for(unsigned i = 0; i < pThis->cMulticastFilterMacs; i++)
2423 LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
2424#endif
2425 break;
2426 }
2427 default:
2428 LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
2429 return VIRTIONET_ERROR;
2430 }
2431 return VIRTIONET_OK;
2432}
2433
2434/*
2435 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MQ (multiqueue) operations.
2436 * See VirtIO 1.0, 5.1.6.5.5
2437 *
2438 * @param pThis virtio-net instance
2439 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2440 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2441 */
2442static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2443{
2444 LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
2445
2446 uint16_t cVirtqPairs;
2447 switch(pCtrlPktHdr->uCmd)
2448 {
2449 case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
2450 {
2451 size_t cbRemaining = pVirtqBuf->cbPhysSend;
2452
2453 AssertMsgReturn(cbRemaining >= sizeof(cVirtqPairs),
2454 ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
2455
2456 /* Fetch number of virtq pairs from guest buffer */
2457 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
2458
2459 AssertMsgReturn(cVirtqPairs <= VIRTIONET_MAX_QPAIRS,
2460 ("[%s] Guest CTRL MQ virtq pair count out of range [%d])\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
2461
2462 LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
2463 pThis->cVirtqPairs = cVirtqPairs;
2464 break;
2465 }
2466 default:
2467 LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
2468 return VIRTIONET_ERROR;
2469 }
2470
2471 /*
2472 * The MQ control function is invoked by the guest in an RPC like manner to change
2473 * the Rx/Tx queue pair count. If the new value exceeds the number of queues
2474 * (and associated workers) already initialized initialize only the new queues and
2475 * respective workers.
2476 */
2477 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
2478 {
2479 virtioNetR3SetVirtqNames(pThis, virtioCoreIsLegacyMode(&pThis->Virtio));
2480 int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
2481 if (RT_FAILURE(rc))
2482 {
2483 LogRelFunc(("Failed to create worker threads\n"));
2484 return VIRTIONET_ERROR;
2485 }
2486 }
2487 return VIRTIONET_OK;
2488}
2489
2490/*
2491 * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's VLAN filtering.
2492 * See VirtIO 1.0, 5.1.6.5.3
2493 *
2494 * @param pThis virtio-net instance
2495 * @param pCtrlPktHdr Control packet header (which includes command parameters)
2496 * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
2497 */
2498static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
2499{
2500 LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
2501
2502 uint16_t uVlanId;
2503 size_t cbRemaining = pVirtqBuf->cbPhysSend;
2504
2505 AssertMsgReturn(cbRemaining >= sizeof(uVlanId),
2506 ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
2507
2508 /* Fetch VLAN ID from guest buffer */
2509 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
2510
2511 AssertMsgReturn(uVlanId < VIRTIONET_MAX_VLAN_ID,
2512 ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
2513
2514 LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
2515
2516 switch (pCtrlPktHdr->uCmd)
2517 {
2518 case VIRTIONET_CTRL_VLAN_ADD:
2519 ASMBitSet(pThis->aVlanFilter, uVlanId);
2520 break;
2521 case VIRTIONET_CTRL_VLAN_DEL:
2522 ASMBitClear(pThis->aVlanFilter, uVlanId);
2523 break;
2524 default:
2525 LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
2526 return VIRTIONET_ERROR;
2527 }
2528 return VIRTIONET_OK;
2529}
2530
2531/**
2532 * Processes control command from guest.
2533 * See VirtIO 1.0 spec, 5.1.6 "Device Operation" and 5.1.6.5 "Control Virtqueue".
2534 *
2535 * The control command is contained in a virtio buffer pulled from the virtio-net defined control queue (ctrlq).
2536 * Command type is parsed is dispatched to a command-specific device-configuration handler function (e.g. RX, MAC, VLAN, MQ
2537 * and ANNOUNCE).
2538 *
2539 * This function handles all parts of the host-side of the ctrlq round-trip buffer processing.
2540 *
2541 * Invoked by worker for virtio-net control queue to process a queued control command buffer.
2542 *
2543 * @param pDevIns PDM device instance
2544 * @param pThis virtio-net device instance
2545 * @param pThisCC virtio-net device instance
2546 * @param pVirtqBuf pointer to buffer pulled from virtq (input to this function)
2547 */
2548static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2549 PVIRTQBUF pVirtqBuf)
2550{
2551 if (!(pThis->fNegotiatedFeatures & VIRTIONET_F_CTRL_VQ))
2552 LogFunc(("[%s] WARNING: Guest using CTRL queue w/o negotiating VIRTIONET_F_CTRL_VQ feature\n", pThis->szInst));
2553
2554 LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
2555
2556 if (pVirtqBuf->cbPhysSend < 2)
2557 {
2558 LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
2559 return;
2560 }
2561 else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
2562 {
2563 LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
2564 return;
2565 }
2566
2567 /*
2568 * Allocate buffer and read in the control command
2569 */
2570 VIRTIONET_CTRL_HDR_T CtrlPktHdr; RT_ZERO(CtrlPktHdr);
2571 AssertLogRelMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(CtrlPktHdr),
2572 ("DESC chain too small for CTRL pkt header"));
2573 virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &CtrlPktHdr, sizeof(CtrlPktHdr));
2574
2575 Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, CtrlPktHdr.uClass, CtrlPktHdr.uCmd));
2576
2577 uint8_t uAck;
2578 switch (CtrlPktHdr.uClass)
2579 {
2580 case VIRTIONET_CTRL_RX:
2581 uAck = virtioNetR3CtrlRx(pThis, pThisCC, &CtrlPktHdr, pVirtqBuf);
2582 break;
2583 case VIRTIONET_CTRL_MAC:
2584 uAck = virtioNetR3CtrlMac(pThis, &CtrlPktHdr, pVirtqBuf);
2585 break;
2586 case VIRTIONET_CTRL_VLAN:
2587 uAck = virtioNetR3CtrlVlan(pThis, &CtrlPktHdr, pVirtqBuf);
2588 break;
2589 case VIRTIONET_CTRL_MQ:
2590 uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, &CtrlPktHdr, pVirtqBuf);
2591 break;
2592 case VIRTIONET_CTRL_ANNOUNCE:
2593 uAck = VIRTIONET_OK;
2594 if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
2595 {
2596 LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
2597 "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
2598 break;
2599 }
2600 if (CtrlPktHdr.uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
2601 {
2602 LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
2603 break;
2604 }
2605#if FEATURE_OFFERED(STATUS)
2606 pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
2607#endif
2608 Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
2609 break;
2610 default:
2611 LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", CtrlPktHdr.uClass));
2612 uAck = VIRTIONET_ERROR;
2613 }
2614
2615 /* Return CTRL packet Ack byte (result code) to guest driver */
2616 RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
2617 RTSGBUF SgBuf;
2618
2619 RTSgBufInit(&SgBuf, aStaticSegs, RT_ELEMENTS(aStaticSegs));
2620 virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, &SgBuf, pVirtqBuf, true /* fFence */);
2621 virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
2622
2623 LogFunc(("%s Finished processing CTRL command with status %s\n",
2624 pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
2625}
2626
2627/**
2628 * Reads virtio-net pkt header from provided Phy. addr of virtio descriptor chain
2629 * (e.g. S/G segment from guest-driver provided buffer pulled from Tx virtq)
2630 * Verifies state and supported modes, sets TCP header size.
2631 *
2632 * @param pVirtio VirtIO core instance data
2633 * @param pThis virtio-net instance
2634 * @param pDevIns PDM device instance
2635 * @param GCPhys Phys. Address from where to read virtio-net pkt header
2636 * @param pPktHdr Where to store read Tx pkt hdr (virtio pkt hdr size is determined from instance configuration)
2637 * @param cbFrame Total pkt frame size to inform bounds check
2638 */
2639static int virtioNetR3ReadVirtioTxPktHdr(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
2640{
2641 int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
2642 if (RT_FAILURE(rc))
2643 return rc;
2644
2645 LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
2646 pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
2647 pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
2648
2649 if (pPktHdr->uGsoType)
2650 {
2651 /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
2652 AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2653 && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
2654 ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
2655
2656 uint32_t uTcpHdrSize;
2657 switch (pPktHdr->uGsoType)
2658 {
2659 case VIRTIONET_HDR_GSO_TCPV4:
2660 case VIRTIONET_HDR_GSO_TCPV6:
2661 uTcpHdrSize = sizeof(RTNETTCP);
2662 break;
2663 case VIRTIONET_HDR_GSO_UDP:
2664 uTcpHdrSize = 0;
2665 break;
2666 default:
2667 LogFunc(("Bad GSO type in packet header\n"));
2668 return VERR_INVALID_PARAMETER;
2669 }
2670 /* Header + MSS must not exceed the packet size. */
2671 AssertMsgReturn(RT_LIKELY(uTcpHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
2672 ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
2673 }
2674
2675 AssertMsgReturn( !(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2676 || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
2677 ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
2678 sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
2679 VERR_BUFFER_OVERFLOW);
2680
2681 return VINF_SUCCESS;
2682}
2683
2684/**
2685 * Transmits single GSO frame via PDM framework to downstream PDM device, to emit from virtual NIC.
2686 *
2687 * This does final prep of GSO parameters including checksum calculation if configured
2688 * (e.g. if VIRTIONET_HDR_F_NEEDS_CSUM flag is set).
2689 *
2690 * @param pThis virtio-net instance
2691 * @param pThisCC virtio-net instance
2692 * @param pSgBuf PDM S/G buffer containing pkt and hdr to transmit
2693 * @param pGso GSO parameters used for the packet
2694 * @param pPktHdr virtio-net pkt header to adapt to PDM semantics
2695 */
2696static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
2697 PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
2698{
2699
2700 virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
2701 if (pGso)
2702 {
2703 /* Some guests (RHEL) may report HdrLen excluding transport layer header!
2704 * Thus cannot use cdHdrs provided by the guest because of different ways
2705 * it gets filled out by different versions of kernels. */
2706 Log4Func(("%s HdrLen before adjustment %d.\n", pThis->szInst, pGso->cbHdrsTotal));
2707 switch (pGso->u8Type)
2708 {
2709 case PDMNETWORKGSOTYPE_IPV4_TCP:
2710 case PDMNETWORKGSOTYPE_IPV6_TCP:
2711 pGso->cbHdrsTotal = pPktHdr->uChksumStart +
2712 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
2713 AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
2714 ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
2715 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
2716 break;
2717 case PDMNETWORKGSOTYPE_IPV4_UDP:
2718 pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
2719 pGso->cbHdrsSeg = pPktHdr->uChksumStart;
2720 break;
2721 case PDMNETWORKGSOTYPE_INVALID:
2722 LogFunc(("%s ignoring invalid GSO frame\n", pThis->szInst));
2723 return VERR_INVALID_PARAMETER;
2724 }
2725 /* Update GSO structure embedded into the frame */
2726 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
2727 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
2728 Log4Func(("%s adjusted HdrLen to %d.\n",
2729 pThis->szInst, pGso->cbHdrsTotal));
2730 Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
2731 pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
2732 pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
2733 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
2734 }
2735 else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
2736 {
2737 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
2738 /*
2739 * This is not GSO frame but checksum offloading is requested.
2740 */
2741 virtioNetR3Calc16BitChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
2742 pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
2743 }
2744
2745 return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
2746}
2747
2748/**
2749 * Non-reentrant function transmits all available packets from specified Tx virtq to downstream
2750 * PDM device (if cable is connected). For each Tx pkt, virtio-net pkt header is converted
2751 * to required GSO information (VBox host network stack semantics)
2752 *
2753 * @param pDevIns PDM device instance
2754 * @param pThis virtio-net device instance
2755 * @param pThisCC virtio-net device instance
2756 * @param pTxVirtq Address of transmit virtq
2757 * @param fOnWorkerThread Flag to PDM whether to use caller's or or PDM transmit worker's thread.
2758 */
2759static int virtioNetR3TransmitPkts(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
2760 PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
2761{
2762 PVIRTIOCORE pVirtio = &pThis->Virtio;
2763
2764
2765 if (!pThis->fVirtioReady)
2766 {
2767 LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
2768 pThis->szInst, pThis->virtioNetConfig.uStatus));
2769 return VERR_IGNORED;
2770 }
2771
2772 if (!pThis->fCableConnected)
2773 {
2774 Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
2775 return VERR_IGNORED;
2776 }
2777
2778 /*
2779 * Only one thread is allowed to transmit at a time, others should skip transmission as the packets
2780 * will be picked up by the transmitting thread.
2781 */
2782 if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
2783 return VERR_IGNORED;
2784
2785 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2786 if (pDrv)
2787 {
2788 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
2789 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
2790 if (rc == VERR_TRY_AGAIN)
2791 {
2792 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2793 return VERR_TRY_AGAIN;
2794 }
2795 }
2796 int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2797 if (!cPkts)
2798 {
2799#ifdef VIRTIO_REL_INFO_DUMP
2800 if (ASMAtomicReadBool(&pThis->Virtio.fRecovering))
2801 LogRel(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
2802#endif /* VIRTIO_REL_INFO_DUMP */
2803 LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
2804
2805 if (pDrv)
2806 pDrv->pfnEndXmit(pDrv);
2807
2808 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2809 return VERR_MISSING;
2810 }
2811#ifdef VIRTIO_REL_INFO_DUMP
2812 if (ASMAtomicReadBool(&pThis->Virtio.fRecovering))
2813 LogRel(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
2814#endif /* VIRTIO_REL_INFO_DUMP */
2815 LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
2816
2817 virtioNetR3SetWriteLed(pThisCC, true);
2818
2819 /* Disable notifications until all available descriptors have been processed */
2820 if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX))
2821 virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, false /* fEnable */);
2822
2823 int rc;
2824 VIRTQBUF_T VirtqBuf;
2825
2826 VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
2827 VirtqBuf.cRefs = 1;
2828
2829 PVIRTQBUF pVirtqBuf = &VirtqBuf;
2830 while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, pVirtqBuf)) == VINF_SUCCESS)
2831 {
2832 Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
2833
2834 PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
2835 PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
2836 uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
2837 size_t uFrameSize = 0;
2838
2839 AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
2840 ("Desc chain's first seg has insufficient space for pkt header!\n"),
2841 VERR_INTERNAL_ERROR);
2842
2843 VIRTIONETPKTHDR PktHdr;
2844 PVIRTIONETPKTHDR pPktHdr = &PktHdr;
2845
2846 /* Compute total frame size from guest (including virtio-net pkt hdr) */
2847 for (unsigned i = 0; i < cSegsFromGuest && uFrameSize < VIRTIONET_MAX_FRAME_SIZE; i++)
2848 uFrameSize += paSegsFromGuest[i].cbSeg;
2849
2850 Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uFrameSize));
2851 Assert(uFrameSize <= VIRTIONET_MAX_FRAME_SIZE);
2852
2853 /* Truncate oversized frames. */
2854 if (uFrameSize > VIRTIONET_MAX_FRAME_SIZE)
2855 uFrameSize = VIRTIONET_MAX_FRAME_SIZE;
2856
2857 if (pThisCC->pDrv)
2858 {
2859 uFrameSize -= pThis->cbPktHdr;
2860 /*
2861 * Peel off pkt header and convert to PDM/GSO semantics.
2862 */
2863 rc = virtioNetR3ReadVirtioTxPktHdr(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uFrameSize /* cbFrame */);
2864 if (RT_FAILURE(rc))
2865 return rc;
2866 virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
2867
2868 PDMNETWORKGSO Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
2869
2870 /* Allocate PDM transmit buffer to send guest provided network frame from to VBox network leaf device */
2871 PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
2872 rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uFrameSize, pGso, &pSgBufToPdmLeafDevice);
2873
2874 /*
2875 * Copy virtio-net guest S/G buffer to PDM leaf driver S/G buffer
2876 * converting from GCphys to virt memory at the same time
2877 */
2878 if (RT_SUCCESS(rc))
2879 {
2880 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
2881 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
2882
2883 size_t cbCopied = 0;
2884 size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uFrameSize;
2885 uint64_t uOffset = 0;
2886 while (cbRemain)
2887 {
2888 PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
2889 uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
2890 uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
2891 uint64_t srcSgCur = (uint64_t)pSgPhysSend->GCPhysCur;
2892 cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
2893 /*
2894 * Guest sent a bogus S/G chain, there doesn't seem to be a way to report an error but
2895 * as this shouldn't happen anyway we just stop proccessing this chain.
2896 */
2897 if (RT_UNLIKELY(!cbCopied))
2898 break;
2899 virtioCoreGCPhysRead(pVirtio, pDevIns,
2900 (RTGCPHYS)pSgPhysSend->GCPhysCur,
2901 ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
2902 virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
2903 cbRemain -= cbCopied;
2904 uOffset += cbCopied;
2905 }
2906
2907 LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
2908 uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
2909
2910 rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
2911 if (RT_FAILURE(rc))
2912 {
2913 LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
2914 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2915 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
2916 pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
2917 }
2918 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
2919 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
2920 }
2921 else
2922 {
2923 Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
2924 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
2925 break;
2926 }
2927
2928 virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
2929
2930 /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
2931 virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
2932 virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
2933 }
2934
2935 /* Before we break the loop we need to check if the queue is empty,
2936 * re-enable notifications, and then re-check again to avoid missing
2937 * a notification for the descriptor that is added to the queue
2938 * after we have checked it on being empty, but before we re-enabled
2939 * notifications.
2940 */
2941 if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
2942 && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
2943 virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
2944 }
2945#ifdef VIRTIO_REL_INFO_DUMP
2946 if (rc == VERR_INVALID_STATE)
2947 {
2948 /*
2949 * Our best bet for recovering from a bad queue state is to do a device reset, but
2950 * it is not enough because NetKVM driver does not complete NBLs on reset. We need to lower
2951 * the link in order to prevent adding new descriptors to the TX queue first. Then we
2952 * need to simulate processing of all outstanding descriptor chains by simply copying
2953 * all head indices from avail to used ring. Note that we have only 'peeked' into the avail
2954 * ring, so the avail index should not have moved yet. This is not the case for other queues.
2955 */
2956 virtioNetAttemptToRecover(pDevIns);
2957 virtioCorePutAllAvailBufsToUsedRing(pDevIns, &pThis->Virtio, pTxVirtq->uIdx);
2958 }
2959 else if (ASMAtomicXchgBool(&pThis->Virtio.fRecovering, false))
2960 LogRel(("[%s] Recovery complete on successfully transmitting a packet\n", pThis->szInst));
2961#endif /* VIRTIO_REL_INFO_DUMP */
2962 virtioNetR3SetWriteLed(pThisCC, false);
2963
2964 if (pDrv)
2965 pDrv->pfnEndXmit(pDrv);
2966
2967 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
2968 return VINF_SUCCESS;
2969}
2970
2971/**
2972 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2973 */
2974static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
2975{
2976 LogFunc(("\n"));
2977 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
2978 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2979 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
2980 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(0)];
2981 STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
2982
2983 (void)virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
2984}
2985
2986/**
2987 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
2988 */
2989static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
2990{
2991 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
2992 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
2993
2994#ifdef VIRTIO_REL_INFO_DUMP
2995 if (RT_UNLIKELY(!pThis->fVirtioReady))
2996 {
2997 if (ASMAtomicXchgBool(&pThis->Virtio.fRecovering, false))
2998 {
2999 LogRel(("[%s] Recovery failed, VM reset is needed!\n", pThis->szInst));
3000 return;
3001 }
3002 }
3003#endif /* VIRTIO_REL_INFO_DUMP */
3004
3005 SET_LINK_UP(pThis);
3006#ifdef VIRTIO_REL_INFO_DUMP
3007 if (ASMAtomicReadBool(&pThis->Virtio.fRecovering))
3008 LogRel(("[%s] Link is up\n", pThis->szInst));
3009#endif /* VIRTIO_REL_INFO_DUMP */
3010 virtioNetWakeupRxBufWaiter(pDevIns);
3011
3012 if (pThisCC->pDrv)
3013 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
3014
3015 LogFunc(("[%s] Link is up\n", pThis->szInst));
3016 RT_NOREF(hTimer, pvUser);
3017}
3018
3019/**
3020 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
3021 */
3022static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
3023{
3024 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
3025 PPDMDEVINS pDevIns = pThisCC->pDevIns;
3026 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3027
3028 bool fRequestedLinkStateIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
3029
3030#ifdef LOG_ENABLED
3031 if (LogIs7Enabled())
3032 {
3033 LogFunc(("[%s]", pThis->szInst));
3034 switch(enmState)
3035 {
3036 case PDMNETWORKLINKSTATE_UP:
3037 Log(("UP\n"));
3038 break;
3039 case PDMNETWORKLINKSTATE_DOWN:
3040 Log(("DOWN\n"));
3041 break;
3042 case PDMNETWORKLINKSTATE_DOWN_RESUME:
3043 Log(("DOWN (RESUME)\n"));
3044 break;
3045 default:
3046 Log(("UNKNOWN)\n"));
3047 }
3048 }
3049#endif
3050
3051 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
3052 {
3053 if (IS_LINK_UP(pThis))
3054 {
3055 /*
3056 * We bother to bring the link down only if it was up previously. The UP link state
3057 * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
3058 */
3059 virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
3060 if (pThisCC->pDrv)
3061 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
3062 }
3063 }
3064 else if (fRequestedLinkStateIsUp != IS_LINK_UP(pThis))
3065 {
3066 if (fRequestedLinkStateIsUp)
3067 {
3068 Log(("[%s] Link is up\n", pThis->szInst));
3069 pThis->fCableConnected = true;
3070 SET_LINK_UP(pThis);
3071 }
3072 else /* Link requested to be brought down */
3073 {
3074 /* The link was brought down explicitly, make sure it won't come up by timer. */
3075 PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
3076 Log(("[%s] Link is down\n", pThis->szInst));
3077 pThis->fCableConnected = false;
3078 SET_LINK_DOWN(pThis);
3079 }
3080 if (pThisCC->pDrv)
3081 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
3082 }
3083 return VINF_SUCCESS;
3084}
3085/**
3086 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
3087 */
3088static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
3089{
3090 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
3091 PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
3092
3093 return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
3094}
3095
3096static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
3097{
3098 Log10Func(("[%s]\n", pThis->szInst));
3099 int rc = VINF_SUCCESS;
3100 for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
3101 {
3102 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uIdxWorker];
3103 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
3104
3105 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
3106 {
3107 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
3108 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
3109 }
3110 if (pWorkerR3->pThread)
3111 {
3112 int rcThread;
3113 rc = PDMDevHlpThreadDestroy(pDevIns, pWorkerR3->pThread, &rcThread);
3114 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
3115 AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
3116 pWorkerR3->pThread = NULL;
3117 }
3118 }
3119 return rc;
3120}
3121
3122/**
3123 * Creates a worker for specified queue, along with semaphore to throttle the worker.
3124 *
3125 * @param pDevIns - PDM device instance
3126 * @param pThis - virtio-net instance
3127 * @param pWorker - Pointer to worker state
3128 * @param pWorkerR3 - Pointer to worker state
3129 * @param pVirtq - Pointer to virtq
3130 */
3131static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis,
3132 PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
3133 PVIRTIONETVIRTQ pVirtq)
3134{
3135 Log10Func(("[%s]\n", pThis->szInst));
3136 RT_NOREF(pThis);
3137
3138 int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess);
3139
3140 if (RT_FAILURE(rc))
3141 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
3142 N_("DevVirtioNET: Failed to create SUP event semaphore"));
3143
3144 LogFunc(("creating thread for queue %s\n", pVirtq->szName));
3145
3146 rc = PDMDevHlpThreadCreate(pDevIns, &pWorkerR3->pThread,
3147 (void *)pWorker, virtioNetR3WorkerThread,
3148 virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, pVirtq->szName);
3149 if (RT_FAILURE(rc))
3150 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
3151 N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
3152
3153 pWorker->fAssigned = true; /* Because worker's state in fixed-size array initialized w/empty slots */
3154
3155 LogFunc(("%s pThread: %p\n", pVirtq->szName, pWorkerR3->pThread));
3156
3157 return rc;
3158}
3159
3160static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
3161{
3162 Log10Func(("[%s]\n", pThis->szInst));
3163 int rc;
3164
3165 /* Create the Control Queue worker anyway whether or not it is feature-negotiated or utilized by the guest.
3166 * See related comment for queue construction in the device constructor function for more context.
3167 */
3168
3169 PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
3170 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis,
3171 &pThis->aWorkers[CTRLQIDX], &pThisCC->aWorkers[CTRLQIDX], pCtlVirtq);
3172 AssertRCReturn(rc, rc);
3173
3174 pCtlVirtq->fHasWorker = true;
3175
3176 for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
3177 {
3178 PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
3179 PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
3180
3181 rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, &pThis->aWorkers[TXQIDX(uVirtqPair)],
3182 &pThisCC->aWorkers[TXQIDX(uVirtqPair)], pTxVirtq);
3183 AssertRCReturn(rc, rc);
3184
3185 pTxVirtq->fHasWorker = true;
3186 pRxVirtq->fHasWorker = false;
3187 }
3188
3189 if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
3190 pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
3191
3192 pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
3193
3194 return rc;
3195}
3196
3197
3198/**
3199 * @callback_method_impl{FNPDMTHREADDEV}
3200 */
3201static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3202{
3203 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3204 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3205 PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
3206 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[pWorker->uIdx];
3207 uint16_t uIdx = pWorker->uIdx;
3208
3209 ASMAtomicWriteBool(&pWorker->fSleeping, false);
3210
3211 Assert(pWorker->uIdx == pVirtq->uIdx);
3212
3213 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
3214 return VINF_SUCCESS;
3215
3216 LogFunc(("[%s] worker thread idx=%d started for %s (virtq idx=%d)\n", pThis->szInst, pWorker->uIdx, pVirtq->szName, pVirtq->uIdx));
3217
3218 /** @todo Race w/guest enabling/disabling guest notifications cyclically.
3219 See BugRef #8651, Comment #82 */
3220 virtioCoreVirtqEnableNotify(&pThis->Virtio, uIdx, true /* fEnable */);
3221
3222 while ( pThread->enmState != PDMTHREADSTATE_TERMINATING
3223 && pThread->enmState != PDMTHREADSTATE_TERMINATED)
3224 {
3225 if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pVirtq->uIdx))
3226 {
3227 /* Precisely coordinated atomic interlocks avoid a race condition that results in hung thread
3228 * wherein a sloppily coordinated wake-up notification during a transition into or out
3229 * of sleep leaves notifier and target mutually confused about actual & intended state.
3230 */
3231 ASMAtomicWriteBool(&pWorker->fSleeping, true);
3232 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
3233 if (!fNotificationSent)
3234 {
3235 Log10Func(("[%s] %s worker sleeping...\n\n", pThis->szInst, pVirtq->szName));
3236 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
3237
3238 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
3239 STAM_COUNTER_INC(&pThis->StatTransmitByThread);
3240 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
3241 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
3242 return VINF_SUCCESS;
3243 if (rc == VERR_INTERRUPTED)
3244 continue;
3245 ASMAtomicWriteBool(&pWorker->fNotified, false);
3246 }
3247 ASMAtomicWriteBool(&pWorker->fSleeping, false);
3248 }
3249 /*
3250 * Dispatch to the handler for the queue this worker is set up to drive
3251 */
3252 if (pVirtq->fCtlVirtq)
3253 {
3254 Log10Func(("[%s] %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
3255 VIRTQBUF_T VirtqBuf;
3256 PVIRTQBUF pVirtqBuf = &VirtqBuf;
3257 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, pVirtqBuf, true);
3258#ifdef VIRTIO_REL_INFO_DUMP
3259 if (rc == VERR_INVALID_STATE)
3260 {
3261 virtioNetAttemptToRecover(pDevIns);
3262 continue;
3263 }
3264#endif /* VIRTIO_REL_INFO_DUMP */
3265 if (rc == VERR_NOT_AVAILABLE)
3266 {
3267 Log10Func(("[%s] %s worker woken. Nothing found in queue\n", pThis->szInst, pVirtq->szName));
3268 continue;
3269 }
3270 virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
3271 }
3272 else /* Must be Tx queue */
3273 {
3274 Log10Func(("[%s] %s worker woken. Virtq has data to transmit\n", pThis->szInst, pVirtq->szName));
3275 virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
3276 }
3277 /* Note: Surprise! Rx queues aren't handled by local worker threads. Instead, the PDM network leaf driver
3278 * invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback, which waits until woken by virtioNetVirtqNotified()
3279 * indicating that guest IN buffers have been added to Rx virt queue.
3280 */
3281 }
3282 Log10(("[%s] %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
3283 return VINF_SUCCESS;
3284}
3285
3286/**
3287 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
3288 *
3289 * Called back by the core code when VirtIO's ready state has changed.
3290 */
3291static DECLCALLBACK(void) virtioNetR3StatusChg(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
3292{
3293 PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
3294 PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
3295
3296 pThis->fVirtioReady = fVirtioReady;
3297
3298 if (fVirtioReady)
3299 {
3300#ifdef LOG_ENABLED
3301 Log(("\n%-23s: %s *** VirtIO Ready ***\n\n", __FUNCTION__, pThis->szInst));
3302 virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
3303#endif
3304 pThis->fResetting = false;
3305 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(pVirtio);
3306 /* Now we can properly figure out the size of virtio header! */
3307 virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
3308 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
3309
3310 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3311 {
3312 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
3313 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
3314
3315 Assert(pWorker->uIdx == uVirtqNbr);
3316 RT_NOREF(pWorker);
3317
3318 Assert(pVirtq->uIdx == pWorker->uIdx);
3319
3320 (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
3321 pVirtq->fAttachedToVirtioCore = true;
3322 if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
3323 virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
3324 }
3325
3326 virtioNetWakeupRxBufWaiter(pThisCC->pDevIns);
3327 }
3328 else
3329 {
3330#ifdef VIRTIO_REL_INFO_DUMP
3331 if (ASMAtomicReadBool(&pThis->Virtio.fRecovering))
3332 LogRel(("[%s] Resetting...\n", pThis->szInst));
3333#endif /* VIRTIO_REL_INFO_DUMP */
3334 Log(("\n%-23s: %s VirtIO is resetting ***\n", __FUNCTION__, pThis->szInst));
3335
3336 pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
3337 Log7(("%-23s: %s Link is %s\n", __FUNCTION__, pThis->szInst, pThis->fCableConnected ? "up" : "down"));
3338
3339 pThis->fPromiscuous = true;
3340 pThis->fAllMulticast = false;
3341 pThis->fAllUnicast = false;
3342 pThis->fNoMulticast = false;
3343 pThis->fNoUnicast = false;
3344 pThis->fNoBroadcast = false;
3345 pThis->uIsTransmitting = 0;
3346 pThis->cUnicastFilterMacs = 0;
3347 pThis->cMulticastFilterMacs = 0;
3348
3349 memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
3350 memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
3351 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
3352
3353 if (pThisCC->pDrv)
3354 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
3355
3356 for (uint16_t uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3357 {
3358 virtioCoreR3VirtqDetach(&pThis->Virtio, uVirtqNbr);
3359 pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore = false;
3360 }
3361 }
3362
3363#ifdef VIRTIO_REL_INFO_DUMP
3364 if (pThisCC->pDrv)
3365 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, pThis->virtioNetConfig.uStatus ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN);
3366#endif /* VIRTIO_REL_INFO_DUMP */
3367}
3368
3369/**
3370 * @callback_method_impl{VIRTIOCORER3,pfnFeatureNegotiationComplete}
3371 */
3372static DECLCALLBACK(void) pfnFeatureNegotiationComplete(PVIRTIOCORE pVirtio, uint64_t fDriverFeatures, uint32_t fLegacy)
3373{
3374 PVIRTIONET pThis = PDMDEVINS_2_DATA(pVirtio->pDevInsR3, PVIRTIONET);
3375
3376 LogFunc(("[Feature Negotiation Complete] Guest Driver version is: %s\n", fLegacy ? "legacy" : "modern"));
3377#ifdef VIRTIO_REL_INFO_DUMP
3378 if (ASMAtomicReadBool(&pThis->Virtio.fRecovering))
3379 LogRel(("[%s] Feature Negotiation Complete for %s driver\n", pThis->szInst, fLegacy ? "legacy" : "modern"));
3380#endif /* VIRTIO_REL_INFO_DUMP */
3381 virtioNetConfigurePktHdr(pThis, fLegacy);
3382 virtioNetR3SetVirtqNames(pThis, fLegacy);
3383
3384 /** @todo r=aeichner We can't just destroy the control queue here because the UEFI firmware and the guest OS might have different
3385 * opinions on how to use the device and if the UEFI firmware causes the control queue to be destroyed Linux guests
3386 * will have a hard time using it. */
3387#if 0
3388 /* Senseless for modern guest to use control queue in this case. (See Note 1 in PDM-invoked device constructor) */
3389 if (!fLegacy && !(fDriverFeatures & VIRTIONET_F_CTRL_VQ))
3390 virtioNetR3VirtqDestroy(pVirtio, &pThis->aVirtqs[CTRLQIDX]);
3391#else
3392 RT_NOREF(fDriverFeatures);
3393#endif
3394}
3395
3396#endif /* IN_RING3 */
3397
3398/**
3399 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
3400 *
3401 * The VM is suspended at this point.
3402 */
3403static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3404{
3405 RT_NOREF(fFlags);
3406
3407 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3408 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3409
3410 Log7Func(("[%s]\n", pThis->szInst));
3411 RT_NOREF(pThis);
3412
3413 AssertLogRelReturnVoid(iLUN == 0);
3414
3415 pThisCC->pDrvBase = NULL;
3416 pThisCC->pDrv = NULL;
3417}
3418
3419/**
3420 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
3421 *
3422 * This is called when we change block driver.
3423 */
3424static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3425{
3426 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3427 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3428
3429 Log7Func(("[%s]", pThis->szInst));
3430 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
3431
3432 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
3433 if (RT_SUCCESS(rc))
3434 {
3435 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
3436 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
3437 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
3438 }
3439 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
3440 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
3441 {
3442 /* This should never happen because this function is not called
3443 * if there is no driver to attach! */
3444 Log(("[%s] No attached driver!\n", pThis->szInst));
3445 }
3446
3447 RT_NOREF2(pThis, fFlags);
3448 return rc;
3449}
3450
3451/**
3452 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
3453 */
3454static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
3455{
3456 PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
3457 if (iLUN)
3458 return VERR_PDM_LUN_NOT_FOUND;
3459 *ppLed = &pThisR3->led;
3460 return VINF_SUCCESS;
3461}
3462
3463/**
3464 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3465 */
3466static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
3467{
3468 PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
3469 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
3470 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
3471 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
3472 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
3473 return NULL;
3474}
3475
3476/**
3477 * @interface_method_impl{PDMDEVREGR3,pfnReset}
3478 */
3479static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
3480{
3481 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3482 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3483
3484 virtioCoreR3ResetDevice(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
3485}
3486
3487/**
3488 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
3489 */
3490static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
3491{
3492 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
3493
3494 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3495 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3496
3497 Log(("[%s] Destroying instance\n", pThis->szInst));
3498 if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
3499 {
3500 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
3501 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
3502 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
3503 }
3504
3505 virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
3506 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
3507 return VINF_SUCCESS;
3508}
3509
3510/**
3511 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
3512 *
3513 * Notes about revising originally VirtIO 1.0+ only virtio-net device emulator to be "transitional",
3514 * a VirtIO term meaning this now interoperates with both "legacy" (e.g. pre-1.0) and "modern" (1.0+)
3515 * guest virtio-net drivers. The changes include migrating VMs saved using prior DevVirtioNet.cpp (0.95)
3516 * saveExec/loadExec semantics to use 1.0 save/load semantics.
3517 *
3518 * Regardless of the 1.0 spec's overall helpful guidance for implementing transitional devices,
3519 * A bit is left to the imagination, e.g. some things have to be determined deductively
3520 * (AKA "the hard way").
3521 *
3522 * Case in point: According to VirtIO 0.95 ("legacy") specification, section 2.2.1, "historically"
3523 * drivers may start driving prior to feature negotiation and prior to drivers setting DRIVER_OK
3524 * status, "provided driver doesn't use features that alter early use of this device". Interpreted
3525 * here to mean a virtio-net driver must respect default settings (such as implicit pkt header default
3526 * size, as determined per Note 1 below).
3527 *
3528 * ----------------------------------------------------------------------------------------------
3529 * Transitional device initialization Note 1: Identifying default value for network Rx pkt hdr size.
3530 * (VirtIO 1.0 specification section 5.1.6.1)
3531 *
3532 * Guest virtio legacy drivers may begin operations prematurely, regardless of early spec's
3533 * initialization sequence (see note 2 below). Legacy drivers implicitly default to using the
3534 * (historically) shortest-length network packet header *unless* VIRTIONET_F_MRG_RXBUF feature is
3535 * negotiated. If feature negotiation phase is [optionally] enacted by a legacy guest (i.e. we strictly
3536 * enforce full initialization protocol for modern guests), virtioNetConfigurePktHdr() is invoked again to
3537 * finalize device's network packet header size. Best-guess at default packet header size is deduced, e.g.
3538 * isn't documented, as follows: A legacy guest with VIRTIONET_F_MRG_RXBUF not-yet-negotiated is the only
3539 * case where network I/O could possibly occur with any reasonable assumption about packet type/size,
3540 * because logically other permutations couldn't possibly be inferred until feature negotiation
3541 * is complete. Specifically, those cases are:
3542 *
3543 * 1. A modern driver (detected only when VIRTIONET_F_VERSION_1 feature is ack'd by guest, and,
3544 * simultaneously, VIRTIONET_F_MRG_RXBUF feature is accepted or declined (determining network receive-packet
3545 * processing behavior).
3546 *
3547 * 2. A legacy driver that has agreed to use VIRTIONET_F_MRG_RXBUF feature, resulting in a two-byte larger pkt hdr,
3548 * (as well as deciding Rx packet processing behavior).
3549 *
3550 * ----------------------------------------------------------------------------------------------
3551 * Transitional device initialization Note 2: Creating unnegotiated control queue.
3552 * (VirtIO 1.0 spec, sections 5.1.5 and 5.1.6.5)
3553 *
3554 * Create all queues immediately, prior to feature negotiation, including control queue (irrespective
3555 * of the fact it's too early in initialization for control feature to be approved by guest). This
3556 * transitional device must deal with legacy guests which *can* (and on linux have been seen to) use
3557 * the control queue prior to feature negotiation.
3558 *
3559 * The initial assumption is *modern" guest virtio-net drivers out in the wild could never reasonably
3560 * attempt something as obviously risky as using ctrlq without first acking VIRTIO_NET_F_CTRL_VQ
3561 * feature to establish it. For now, we create the control queue proactively to accomodate a potentially
3562 * badly behaved but officially sanctioned legacy virtio-net driver, but *destroy* that same queue
3563 * if a driver announces as 'modern' during feature finalization yet leaves VIRTIO_NET_F_CTRL_VQ un-ack'd.
3564 */
3565static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3566{
3567 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3568 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3569 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3570 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3571
3572 /*
3573 * Quickly initialize state data to ensure destructor always works.
3574 */
3575 Log7Func(("PDM device instance: %d\n", iInstance));
3576 RTStrPrintf(pThis->szInst, sizeof(pThis->szInst), "virtio-net #%d", iInstance);
3577
3578 pThisCC->pDevIns = pDevIns;
3579 pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
3580 pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
3581 pThisCC->led.u32Magic = PDMLED_MAGIC;
3582
3583 /* Interfaces */
3584 pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
3585 pThisCC->INetworkDown.pfnReceive = virtioNetR3NetworkDown_Receive;
3586 pThisCC->INetworkDown.pfnReceiveGso = virtioNetR3NetworkDown_ReceiveGso;
3587 pThisCC->INetworkDown.pfnXmitPending = virtioNetR3NetworkDown_XmitPending;
3588 pThisCC->INetworkConfig.pfnGetMac = virtioNetR3NetworkConfig_GetMac;
3589 pThisCC->INetworkConfig.pfnGetLinkState = virtioNetR3NetworkConfig_GetLinkState;
3590 pThisCC->INetworkConfig.pfnSetLinkState = virtioNetR3NetworkConfig_SetLinkState;
3591
3592 pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
3593
3594 /*
3595 * Validate configuration.
3596 */
3597 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC"
3598 "|CableConnected"
3599 "|LineSpeed"
3600 "|LinkUpDelay"
3601 "|StatNo"
3602 "|Legacy"
3603 "|MmioBase"
3604 "|Irq", "");
3605
3606 /* Get config params */
3607 int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
3608 if (RT_FAILURE(rc))
3609 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
3610
3611 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
3612 if (RT_FAILURE(rc))
3613 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
3614
3615 uint32_t uStatNo = iInstance;
3616 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
3617 if (RT_FAILURE(rc))
3618 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
3619
3620 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
3621 if (RT_FAILURE(rc))
3622 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
3623
3624 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
3625
3626 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
3627 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
3628 pThis->szInst, pThis->cMsLinkUpDelay / 1000));
3629
3630 Log(("[%s] Link up delay is set to %u seconds\n", pThis->szInst, pThis->cMsLinkUpDelay / 1000));
3631
3632 /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
3633 memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
3634
3635 Log(("Using MAC address for %s: %2x:%2x:%2x:%2x:%2x:%2x\n", pThis->szInst,
3636 pThis->macConfigured.au8[0], pThis->macConfigured.au8[1], pThis->macConfigured.au8[2],
3637 pThis->macConfigured.au8[3], pThis->macConfigured.au8[4], pThis->macConfigured.au8[5]));
3638
3639 LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
3640
3641 /*
3642 * Configure Virtio core (generic Virtio queue and infrastructure management) parameters.
3643 */
3644# if FEATURE_OFFERED(STATUS)
3645 pThis->virtioNetConfig.uStatus = 0;
3646# endif
3647
3648 pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
3649 pThisCC->Virtio.pfnFeatureNegotiationComplete = pfnFeatureNegotiationComplete;
3650 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3651 pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChg;
3652 pThisCC->Virtio.pfnDevCapRead = virtioNetR3DevCapRead;
3653 pThisCC->Virtio.pfnDevCapWrite = virtioNetR3DevCapWrite;
3654
3655 VIRTIOPCIPARAMS VirtioPciParams;
3656 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
3657 VirtioPciParams.uClassBase = VBOX_PCI_CLASS_NETWORK;
3658 VirtioPciParams.uClassSub = VBOX_PCI_SUB_NETWORK_ETHERNET;
3659 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
3660 VirtioPciParams.uSubsystemId = DEVICE_PCI_NETWORK_SUBSYSTEM; /* VirtIO 1.0 allows PCI Device ID here */
3661 VirtioPciParams.uInterruptLine = 0x00;
3662 VirtioPciParams.uInterruptPin = 0x01;
3663 VirtioPciParams.uDeviceType = VIRTIO_DEVICE_TYPE_NETWORK;
3664
3665 /* Create semaphore used to synchronize/throttle the downstream LUN's Rx waiter thread. */
3666 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
3667 if (RT_FAILURE(rc))
3668 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
3669
3670 pThis->fOfferLegacy = VIRTIONET_TRANSITIONAL_ENABLE_FLAG;
3671 virtioNetConfigurePktHdr(pThis, pThis->fOfferLegacy); /* set defaults */
3672
3673 /* Initialize VirtIO core. (*pfnStatusChanged)() callback occurs when both host VirtIO core & guest driver are ready) */
3674 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
3675 VIRTIONET_HOST_FEATURES_OFFERED, pThis->fOfferLegacy,
3676 &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
3677 if (RT_FAILURE(rc))
3678 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
3679
3680 pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
3681 /** @todo validating features at this point is most probably pointless, as the negotiation hasn't started yet. */
3682 if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
3683 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
3684 pThis->cVirtqPairs = pThis->virtioNetConfig.uMaxVirtqPairs;
3685 pThis->cVirtqs += pThis->cVirtqPairs * 2 + 1;
3686 pThis->aVirtqs[CTRLQIDX].fCtlVirtq = true;
3687
3688 virtioNetR3SetVirtqNames(pThis, pThis->fOfferLegacy);
3689 for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
3690 {
3691 PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
3692 PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
3693 PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
3694 pVirtq->uIdx = pWorker->uIdx = pWorkerR3->uIdx = uVirtqNbr;
3695 }
3696 /*
3697 * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
3698 */
3699 rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
3700 if (RT_FAILURE(rc))
3701 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
3702
3703 /* Create Link Up Timer */
3704 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL,
3705 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
3706 "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
3707 AssertRCReturn(rc, rc);
3708
3709 /*
3710 * Attach network driver instance
3711 */
3712 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
3713 if (RT_SUCCESS(rc))
3714 {
3715 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
3716 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
3717 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
3718 }
3719 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
3720 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
3721 {
3722 /* No error! */
3723 Log(("[%s] No attached driver!\n", pThis->szInst));
3724 }
3725 else
3726 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
3727 /*
3728 * Status driver
3729 */
3730 PPDMIBASE pUpBase;
3731 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
3732 if (RT_SUCCESS(rc))
3733 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
3734 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
3735 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
3736
3737 /*
3738 * Register saved state.
3739 */
3740 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIONET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
3741 NULL, NULL, NULL, /** @todo r=aeichner Teleportation? */
3742 NULL, virtioNetR3ModernSaveExec, NULL,
3743 NULL, virtioNetR3ModernLoadExec, virtioNetR3ModernLoadDone);
3744 AssertRCReturn(rc, rc);
3745 /*
3746 * Statistics and debug stuff.
3747 * The /Public/ bits are official and used by session info in the GUI.
3748 */
3749 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3750 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
3751 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
3752 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
3753 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
3754 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
3755
3756 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
3757 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
3758 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
3759 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
3760 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
3761 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
3762# ifdef VBOX_WITH_STATISTICS
3763 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
3764 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
3765 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
3766 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
3767 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
3768 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
3769 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
3770 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
3771# endif
3772 /*
3773 * Register the debugger info callback (ignore errors).
3774 */
3775 char szTmp[128];
3776 RTStrPrintf(szTmp, sizeof(szTmp), "virtionet%d", iInstance);
3777 rc = PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "Display virtionet info (help, net, features, state, pointers, queues, all)", virtioNetR3Info);
3778 if (RT_FAILURE(rc))
3779 LogRel(("Failed to register DBGF info for device %s\n", szTmp));
3780 return rc;
3781}
3782
3783#else /* !IN_RING3 */
3784
3785/**
3786 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3787 */
3788static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
3789{
3790 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3791 PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
3792 PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
3793 pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
3794 return virtioCoreRZInit(pDevIns, &pThis->Virtio);
3795}
3796
3797#endif /* !IN_RING3 */
3798
3799/**
3800 * The device registration structure.
3801 */
3802const PDMDEVREG g_DeviceVirtioNet =
3803{
3804 /* .uVersion = */ PDM_DEVREG_VERSION,
3805 /* .uReserved0 = */ 0,
3806 /* .szName = */ "virtio-net",
3807 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
3808 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
3809 /* .cMaxInstances = */ ~0U,
3810 /* .uSharedVersion = */ 42,
3811 /* .cbInstanceShared = */ sizeof(VIRTIONET),
3812 /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
3813 /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
3814 /* .cMaxPciDevices = */ 1,
3815 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
3816 /* .pszDescription = */ "Virtio Host NET.\n",
3817#if defined(IN_RING3)
3818 /* .pszRCMod = */ "VBoxDDRC.rc",
3819 /* .pszR0Mod = */ "VBoxDDR0.r0",
3820 /* .pfnConstruct = */ virtioNetR3Construct,
3821 /* .pfnDestruct = */ virtioNetR3Destruct,
3822 /* .pfnRelocate = */ NULL,
3823 /* .pfnMemSetup = */ NULL,
3824 /* .pfnPowerOn = */ NULL,
3825 /* .pfnReset = */ virtioNetR3Reset,
3826 /* .pfnSuspend = */ virtioNetWakeupRxBufWaiter,
3827 /* .pfnResume = */ NULL,
3828 /* .pfnAttach = */ virtioNetR3Attach,
3829 /* .pfnDetach = */ virtioNetR3Detach,
3830 /* .pfnQueryInterface = */ NULL,
3831 /* .pfnInitComplete = */ NULL,
3832 /* .pfnPowerOff = */ virtioNetWakeupRxBufWaiter,
3833 /* .pfnSoftReset = */ NULL,
3834 /* .pfnReserved0 = */ NULL,
3835 /* .pfnReserved1 = */ NULL,
3836 /* .pfnReserved2 = */ NULL,
3837 /* .pfnReserved3 = */ NULL,
3838 /* .pfnReserved4 = */ NULL,
3839 /* .pfnReserved5 = */ NULL,
3840 /* .pfnReserved6 = */ NULL,
3841 /* .pfnReserved7 = */ NULL,
3842#elif defined(IN_RING0)
3843 /* .pfnEarlyConstruct = */ NULL,
3844 /* .pfnConstruct = */ virtioNetRZConstruct,
3845 /* .pfnDestruct = */ NULL,
3846 /* .pfnFinalDestruct = */ NULL,
3847 /* .pfnRequest = */ NULL,
3848 /* .pfnReserved0 = */ NULL,
3849 /* .pfnReserved1 = */ NULL,
3850 /* .pfnReserved2 = */ NULL,
3851 /* .pfnReserved3 = */ NULL,
3852 /* .pfnReserved4 = */ NULL,
3853 /* .pfnReserved5 = */ NULL,
3854 /* .pfnReserved6 = */ NULL,
3855 /* .pfnReserved7 = */ NULL,
3856#elif defined(IN_RC)
3857 /* .pfnConstruct = */ virtioNetRZConstruct,
3858 /* .pfnReserved0 = */ NULL,
3859 /* .pfnReserved1 = */ NULL,
3860 /* .pfnReserved2 = */ NULL,
3861 /* .pfnReserved3 = */ NULL,
3862 /* .pfnReserved4 = */ NULL,
3863 /* .pfnReserved5 = */ NULL,
3864 /* .pfnReserved6 = */ NULL,
3865 /* .pfnReserved7 = */ NULL,
3866#else
3867# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3868#endif
3869 /* .uVersionEnd = */ PDM_DEVREG_VERSION
3870};
3871
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